Skip to main content

Notifications

Community site session details

Community site session details

Session Id :
Copilot Studio - General
Unanswered

How to configure SSO in PVA?

(0) ShareShare
ReportReport
Posted on by

Hi,

 

I am trying to connect SSO in PVA on the Sharepoint website. I see the below error on the chrome console.

p1.png

I see a syntax error from the below line of code. I am following the SSO configuration by the doc provided below: "https://docs.microsoft.com/en-us/power-virtual-agents/configure-sso"

 

The below code is from the doc.

 

 var userID = clientApplication.account?.accountIdentifier != null ? ("Your-customized-prefix-max-20-characters" + clientApplication.account.accountIdentifier).substr(0,64) : (Math.random().toString() + Date.now().toString().substr(0,64)

 

I had posted by query before as well but no luck.. https://powerusers.microsoft.com/t5/General/How-to-configure-Single-Sign-On-in-PVA-with-Sharepoint-and-Teams/m-p/633564#M1006

 

Can anyone please help me out here?

 

Regards,

Hemanth

Categories:
  • h4tgdev Profile Picture
    8 on at
    Re: How to configure SSO in PVA?

    Hi,

     

    I was wondering if you have figured out how to fix the 403 issue.  I am having the same problem.

     

    thanks

  • nportillo Profile Picture
    Microsoft Employee on at
    Re: How to configure SSO in PVA?

    Wrong post apologies

  • Community Power Platform Member Profile Picture
    on at
    Re: How to configure SSO in PVA?

    Thank you @BoLi...appreciate it.

     

    There are also several errors within the scripts provided within that article. I used this thread and one other to solve several of those.

     

    I was wondering if you can help with the 403 error that I reported above somehow.

    I think it just needs someone who has done this before to possibly advise as I'm confident that I have followed the article closely.

     

    I'd be more than happy to provide my findings and contribute to improving the article by providing the correct working script etc.

    Is there any chance we can connect virtually over a call? 

     

    Cheers.

  • BoLi Profile Picture
    on at
    Re: How to configure SSO in PVA?

    Thanks, the document is indeed very confused and not clear. I will ask someone to update the doc. Thank you for pointing it out

  • Community Power Platform Member Profile Picture
    on at
    Re: How to configure SSO in PVA?

    Hi,

     

    This thread has helped me progress through various errors so thanks everyone for that.

    I believe I'm real close but am getting a 403 error when the direct line API is called.

     I have followed the PVA SSO guide and gone through all the steps - Configure single sign-on - Power Virtual Agents | Microsoft Docs.

    Also, I'm somewhat confused by contradicting instructions.

    Please note that the two steps (in italics from the SSO article) below seem to contradict each other as to which app the expose an API scope need to be added to. Step 1 says add it to the initial app reg. However, in the following section step 4, it refers to the canvas app

     

    Which one should the scope be configured in? - Initial app reg or the canvas app?

     

    Define a custom scope for your bot

    1. Open the app registration that you created when you configured authentication.

    Step 4 refers to the Canvas app reg - Enter the full scope URI from the Expose an API blade for the canvas app registration in the Token exchange URL field. The URI will be in the format of api://1234-4567/scope.name.

     

    ZakiVNGS_0-1615120969702.png

     

    Can someone please help? @BoLi @PaulCullivan @Anonymous 

    Thanks in advance.

  • Community Power Platform Member Profile Picture
    on at
    Re: How to configure SSO in PVA?

    Hi @PaulCullivan 

    Thank you for responding back. I have done the above code changes, I am passing the user.userName as a loginHint. Please find the below code.

    function exchangeTokenAsync(resourceUri) 
     {
     let user = clientApplication.getAccount();
    
     console.log("user.userName is: " + user.userName); // User Email ID
     document.getElementById("userName").innerHTML = "Welcome " + user.name; // User Name
    
     let requestObj = {
     scopes: [resourceUri, "openid", "profile"],
     loginHint: user.userName,
     };
    
     return clientApplication.acquireTokenSilent(requestObj).then(function (tokenResponse) {
     return tokenResponse.accessToken;
     }).catch(function (error) 
     {
     console.log("Error from exchangeTokenAsync function" + error);
     });
     }

     

    Below is the MSAL code.

     

    var clientApplication;
    
     (function () {
     var msalConfig = {
     auth:
     {
     clientId:'<Canvas(SSO) App Client ID> ',
     authority:'https://login.microsoftonline.com//<Directory ID>',
     redirectUri:'<My SharePoint website url were the bot is deployed>'
     },
     cache: 
     {
     cacheLocation: "localStorage",
     storeAuthStateInCookie: false,
     },
     };
     if (!clientApplication) 
     {
     clientApplication = new Msal.UserAgentApplication(msalConfig);
     }
     })();

     

    Everytime my code enters into the if(id === "retry") block of code.

     

    Errors from the logs:

    id: retry - bot was not able to handle the invoke, so display the oauthCard

    Error from exchangeTokenAsync functionClientAuthError: Token calls are blocked in hidden iframes

    Failed to load resource: the server responded with a status of 502 ()

     

    But I see my userName being fetched by the MSAL.

    code.jpg

     

    Below is the exchangeTokenAsync function

     

    code2.jpg

    I am not sure why this always fails and displays the login card.

  • PaulCullivan Profile Picture
    Microsoft Employee on at
    Re: How to configure SSO in PVA?

    Hey HermanthN, per our conversation here's what we did with the relevant changes.  Apologies for the delay.

     

    @using Microsoft.AspNetCore.Http
    @using Microsoft.Extensions.Configuration
    @inject IHttpContextAccessor HttpContextAssessor
    @inject IConfiguration Configuration
    
    @{
     string userId = HttpContextAssessor.HttpContext.User.Claims.FirstOrDefault(user => user.Type == "preferred_username").Value;
     string redirectUri = Configuration.GetValue<string>("ConnectionStrings:redirectUri");
     string clientId = Configuration.GetValue<string>("AzureAd:ClientId");
     string botId = Configuration.GetValue<string>("ConnectionStrings:BotId");
     string authority = $"{Configuration.GetValue<string>("AzureAd:Instance")}{Configuration.GetValue<string>("AzureAd:TenantId")}";
    }

     

    This gets you the user name through httpcontextassessor that I mentioned earlier.  From here you can pass this as a login hint:

     

     function exchangeTokenAsync(resourceUri) {
     let requestObj = {
     scopes: [resourceUri, 'openid', 'profile'],
     loginHint: '@userId'
     };
     return clientApplication.acquireTokenSilent(requestObj)
     .then(function (tokenResponse) {
     return tokenResponse.accessToken;
     })
     .catch(function (error) {
     console.log(error);
     });
     }

     

    Note the redirect URI, that's also relevant and passed in here:

     

    var clientApplication;
     (function () {
     var msalConfig = {
     auth: {
     // Client/tenant ID from CosineADOSupport app registration
     clientId: '@clientId',
     authority: '@authority',
     redirectUri: '@redirectUri'
     },
     cache: {
     cacheLocation: 'localStorage',
     storeAuthStateInCookie: false
     }
     };
     if (!clientApplication) {
     clientApplication = new Msal.UserAgentApplication(msalConfig);
     }
     }());

     

    That's pretty much the difference between our code.  Let me know if you have any other questions.

  • BoLi Profile Picture
    on at
    Re: How to configure SSO in PVA?

    you can refer the sample code on our github to get an insight of how to pass token to PVA. you can find the github file link in SSO configuration page

  • Community Power Platform Member Profile Picture
    on at
    Re: How to configure SSO in PVA?

    Hi @BoLi 

     

    Thank you for your response. Can you please explain me how to pass the login token to PVA in order for SSO to work by providing code samples? I really am not able to figure this out.

     

    I have followed the doc to provide the API Permissions for both of the canvas and authentication app.

     

    Below is the snapshot of the App registration for the Canvas App: I have added 2 extra roles here.
    azure1.png

    Below is the snapshot of the App registration for the Authentication App:

    azure2.png

    The Application id looks weird because I have edited it due to security reasons. 

    Below is the current code that I am using.

     

    <!DOCTYPE html>
    <html>
    <head>
     <title>Virtual Agent</title>
     <script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
     <script type="text/javascript" src="https://alcdn.msauth.net/lib/1.2.0/js/msal.js"></script>
     <script src="https://unpkg.com/@azure/storage-blob@10.3.0/browser/azure-storage.blob.min.js" integrity="sha384-fsfhtLyVQo3L3Bh73qgQoRR328xEeXnRGdoi53kjo1uectCfAHFfavrBBN2Nkbdf" crossorigin="anonymous"></script>
     <script type="text/javascript">
     if (typeof Msal === "undefined")
     document.write(unescape("%3Cscript src='https://alcdn.msftauth.net/lib/1.2.0/js/msal.js' type='text/javascript' %3E%3C/script%3E"));
     </script>
    
     <script>
     var clientApplication;
     (function () {
     var msalConfig = {
     auth: {
     clientId: '<CLIENT ID I HAVE REMOVED>',
     authority: 'https://login.microsoftonline.com/<DIRECTORY ID I HAVE REMOVED>'
     },
     cache: {
     cacheLocation: 'localStorage',
     storeAuthStateInCookie: false
     }
     };
     if (!clientApplication) {
     clientApplication = new Msal.UserAgentApplication(msalConfig);
     }
     } ());
     </script>
    
     <style>
     html,
     body {
     height: 100%;
     }
    
     body {
     margin: 0;
     }
    
     .modal {
     display: none; /* Hidden by default */
     position: fixed; /* Stay in place */
     z-index: 1; /* Sit on top */
     padding-top: 100px; /* Location of the box */
     left: 0;
     top: 0;
     width: 100%; /* Full width */
     height: 100%; /* Full height */
     overflow: auto; /* Enable scroll if needed */
     background-color: rgb(0, 0, 0); /* Fallback color */
     background-color: rgba(0, 0, 0, 0.4); /* Black w/ opacity */
     }
    
     .modal-content {
     background-color: #fefefe;
     margin: auto;
     padding: 10px;
     border: 1px solid #888;
     width: 500px;
     height: 575px;
     }
     .close {
     color: black;
     float: right;
     font-size: 28px;
     font-weight: bold;
     }
    
     .close:hover,
     .close:focus {
     color: #000;
     text-decoration: none;
     cursor: pointer;
     }
    
     .main {
     margin: 18px;
     border-radius: 4px;
     }
    
     div[role="form"] {
     background-color: #3392ff;
     }
    
     #webchat {
     position: center;
     height: 530px;
     width: 100%;
     top: 60px;
     overflow: hidden;
     }
     #heading {
     padding-bottom: 5px;
     }
    
     h1 {
     font-size: 14px;
     font-family: Segoe UI;
     font-style: normal;
     font-weight: 600;
     font-size: 14px;
     line-height: 20px;
     color: #f3f2f1;
     letter-spacing: 0.005em;
     display: table-cell;
     vertical-align: middle;
     padding: 13px 0px 0px 20px;
     }
    
     #login {
     position: fixed;
     margin-left: 150px;
     }
    
     .span {
     font-weight: bold;
     }
     #myBtn {
     position: fixed;
     float: right;
     outline: none;
     width: 60px;
     height: 80px;
     margin: auto auto auto 10px;
     }
     button:hover {
     background-color: transparent;
     }
     </style>
    
    </head>
     <body>
     <button id="myBtn" type="button">Power Virtual Agent</button>
    
     <div id="myModal" class="modal">
     <!-- Modal content -->
     <div class="modal-content" style="background-color: #ffd933">
     <span class="close">&times;</span>
     <div id="chatwindow">
     <div id="heading">
     <img src="https://www.flaticon.com/svg/vstatic/svg/4061/4061262.svg?token=exp=1611082398~hmac=d7fe65d90930596808248cc855fd1fda" width="42" height="30" alt="KMT-logo"/>
     <span class="span"><strong>Virtual Agent</strong></span>
     </div>
     <div id="webchat"></div>
     </div>
     </div>
     </div>
    
     <!--Button code begins here-->
    
     <script>
     // Get the modal
     var modal = document.getElementById("myModal");
    
     // Get the button that opens the modal
     var btn = document.getElementById("myBtn");
    
     // Get the <span> element that closes the modal
     var span = document.getElementsByClassName("close")[0];
    
     // When the user clicks the button, open the modal
     btn.onclick = function () {
     modal.style.display = "block";
     };
    
     // When the user clicks on <span> (x), close the modal
     span.onclick = function () {
     modal.style.display = "none";
     };
    
     // When the user clicks anywhere outside of the modal, close it
     window.onclick = function (event) {
     if (event.target == modal) {
     modal.style.display = "none";
     }
     };
     </script>
    
     <!--Button code ends here-->
     <script>
    function getOAuthCardResourceUri(activity) {
     if (activity &&
     activity.attachments &&
     activity.attachments[0] &&
     activity.attachments[0].contentType === 'application/vnd.microsoft.card.oauth' &&
     activity.attachments[0].content.tokenExchangeResource) {
     // asking for token exchange with AAD
     return activity.attachments[0].content.tokenExchangeResource.uri;
     }
    }
    
    function exchangeTokenAsync(resourceUri) {
     let user = clientApplication.getAccount();
     if (user) {
     let requestObj = {
     scopes: [resourceUri]
     };
     return clientApplication.acquireTokenSilent(requestObj)
     .then(function (tokenResponse) {
     return tokenResponse.accessToken;
     })
     .catch(function (error) {
     console.log(error);
     });
     }
     else {
     return Promise.resolve(null);
     }
    }
    
    async function fetchJSON(url, options = {}) {
     const res = await fetch(url, {
     ...options,
     headers: {
     ...options.headers,
     accept: "application/json",
     },
     });
    
     if (!res.ok) {
     console.log(`KMT - Failed to fetch JSON due to ${res.status}`);
     throw new Error(`Failed to fetch JSON due to ${res.status}`);
     }
    
     return await res.json();
     }
    </script>
    
    <script>
     (async function main() {
    
     // Add your BOT ID below
     var BOT_ID = "<BOT ID I HAVE REMOVED>";
     var theURL = "https://powerva.microsoft.com/api/botmanagement/v1/directline/directlinetoken?botId=" + BOT_ID;
    
     const {
     token
     } = await fetchJSON(theURL);
     const directLine = window.WebChat.createDirectLine({
     token
     });
     var userID = clientApplication.account?.accountIdentifier != null ?
     ("Your-customized-prefix-max-20-characters" + clientApplication.account.accountIdentifier).substr(0, 64) :
     (Math.random().toString() + Date.now().toString()).substr(0, 64); // Make sure this will not exceed 64 characters
     const store = WebChat.createStore({}, ({
     dispatch
     }) => next => action => {
     const {
     type
     } = action;
     if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
     dispatch({
     type: 'WEB_CHAT/SEND_EVENT',
     payload: {
     name: 'startConversation',
     type: 'event',
     value: {
     text: "hello"
     }
     }
     });
     return next(action);
     }
     if (action.type === 'DIRECT_LINE/INCOMING_ACTIVITY') {
     const activity = action.payload.activity;
     let resourceUri;
     if (activity.from && activity.from.role === 'bot' &&
     (resourceUri = getOAuthCardResourceUri(activity))) {
     exchangeTokenAsync(resourceUri).then(function(token) {
     if (token) {
     directLine.postActivity({
     type: 'invoke',
     name: 'signin/tokenExchange',
     value: {
     id: activity.attachments[0].content.tokenExchangeResource.id,
     connectionName: activity.attachments[0].content.connectionName,
     token,
     },
     "from": {
     id: userID,
     name: clientApplication.account.name,
     role: "user"
     }
     }).subscribe(
     id => {
     console.log("KMT - id: " + id);
     if (id === 'retry') {
     // bot was not able to handle the invoke, so display the oauthCard
     return next(action);
     }
     // else: tokenexchange successful and we do not display the oauthCard
     },
     error => {
     // an error occurred to display the oauthCard
     return next(action);
     }
     );
     return;
     } else
     return next(action);
     });
     } else
     return next(action);
     } else
     return next(action);
     });
    
     const styleOptions = {
    
     // Add styleOptions to customize Web Chat canvas
     botAvatarInitials: "BT",
     userAvatarInitials: "UR",
     //accent: '#00809d',
     botAvatarBackgroundColor: "#FFFFFF",
     userAvatarBackgroundColor: "#FFFFFF",
     botAvatarImage:
     "https://www.flaticon.com/svg/vstatic/svg/1587/1587565.svg?token=exp=1611082485~hmac=740caa18cae9c7b8ba42daccc841eef0",
     userAvatarImage:
     "https://www.flaticon.com/svg/vstatic/svg/64/64572.svg?token=exp=1611082510~hmac=c15408c5ec67720b3be4b75976161466",
     hideUploadButton: true
     };
    
     window.WebChat.renderWebChat({
     directLine: directLine,
     store,
     userID: userID,
     styleOptions
     },
     document.getElementById('webchat')
     );
     })().catch(err => console.error("An error occurred: " + err));
    </script>
    
     </body>
    </html>

     

    Thanks in advance!

  • BoLi Profile Picture
    on at
    Re: How to configure SSO in PVA?

    @Anonymous , you will still have to pass the login token to PVA in order for SSO to work. that is a must.  For the error you attached, it says the user has not consent to use the application which means you don't grant tenant level consent for your app(Canvas PVA bot SSO). if the app is for PVA bot, then you must grant tenant level consent first(this is already mentioned in .  Besides, the application id looks weird to me, I expect it to be a AAD application id which is a GUID but this is different. Make sure that you are using AAD V2 application.

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

🌸 Community Spring Festival 2025 Challenge Winners! 🌸

Congratulations to all our community participants!

Warren Belz – Community Spotlight

We are honored to recognize Warren Belz as our May 2025 Community…

Congratulations to the April Top 10 Community Stars!

Thanks for all your good work in the Community!

Leaderboard > Copilot Studio - General

#1
Pablo Roldan Profile Picture

Pablo Roldan 25

#2
Romain The Low-Code Bearded Bear Profile Picture

Romain The Low-Code... 23

#3
stampcoin Profile Picture

stampcoin 10

Overall leaderboard