cancel
Showing results for 
Search instead for 
Did you mean: 
Reply
HemanthN
Helper II
Helper II

How to configure SSO in PVA?

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-a...

 

Can anyone please help me out here?

 

Regards,

Hemanth

34 REPLIES 34
renatoromao
Super User
Super User

Hi @HemanthN ,

 

Try to change the phrase "your prefix...".

 var userID = clientApplication.account?.accountIdentifier != null ? ("mypva" + clientApplication.account.accountIdentifier).substr(0,64) : (Math.random().toString() + Date.now().toString().substr(0,64)

 

Did you change the variables e codes that contain ClientId and BotId?

Like 

  var BOT_ID = "<BOT ID>";

Did I answer your question? Mark my post as a solution!
Thanks!

Renato Romão,

Connect with me here 😉

Power Virtual Agents course (+2.300 students | PROMOTIONS) : English | Português

Hi @renatoromao ,

 

I have added the bot id,client id of the canvas app and the tenant id/Directory ID were it is required as per the code.

 

And also we have created 2 app registrations on the Azure AD one for Authentication and the other for SSO.

 

On adding the below code as you have suggested. I still see the same syntax error.

p2.png

Regards,

Hemanth

BoLi
PVA
PVA

Hi Hemanth, 

 

I think it is because there is a missing close bracket in the code sample in SSO document. Thank you for raising that issue out. You can change the line of code to below to fix the issue.

var userId = clientApplication.account?.accountIdentifier != null ?
                                  ("You-customized-prefix" + clientApplication.account.accountIdentifier).substr(0, 64)
                                  : (Math.random().toString() + Date.now().toString()).substr(0,64);    

 

see the hightlighted close bracket after Date.Now().toString().

 

Best Regards

Bo

Hi @BoLi ,

 

Thank you for your response. I have tried added the closed bracket but i still see the same error. Kindly have a look at the attached snapshot for your reference.

 

p3.png

Regards,

Hemanth

Hi Hemanth, 

 

sorry that there are some issue in the document and our doc writer is updating that. Meanwhile you can use the sample sso code on github to unblock you.

 

https://github.com/microsoft/PowerVirtualAgentsSamples/blob/master/BuildYourOwnCanvasSamples/3.singl...

 

Thanks

Bo

@HemanthN we also updated our documentation example. Let us know if these solved the issue for you.

 

 

https://docs.microsoft.com/en-us/power-virtual-agents/configure-sso

 

Thanks

Hi @CleberM / @BoLi ,

 

I still see the same syntax error even after trying the updated code from the SSO doc. I have even tried the sample code from github. Both are having the same syntax error.

 

p5.png

 

Regards,

Hemanth

Hi @CleberM / @BoLi / @renatoromao  ,

 

I am still not able to configure SSO. I am try to achieve this on a Sharepoint Webpage. I have posted the issue through multiple thread but no luck.

I am still being asked to pass the token id.

Can you please help me out with this? Any help is much appreciated.

https://powerusers.microsoft.com/t5/General/PVA-SSO-Configuration/td-p/650994/jump-to/first-unread-m...

 

e1.pnge2.png

 

Below is the code that i am using. I have added a button on the Sharepoint site so the chat bot come up in clicking on it. The same Home page URL were the button is added is the redirect URL that is added on Azure AD App registration.

 

<!DOCTYPE html>
<html>
<head>
    <title>Contoso Sample Web Chat</title>

    <meta name="viewport" content="width=device-width, initial-scale=1">

    <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>
<style>
        body {
            font-family: Arial, Helvetica, sans-serif;
        }

        /* The Modal (background) */
        .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 */
        .modal-content {
            background-color: #fefefe;
            margin: auto;
            padding: 10px;
            border: 1px solid #888;
            width: 500px;
            height: 575px;
        }

        /* The Close Button */
        .close {
            color: black;
            float: right;
            font-size: 28px;
            font-weight: bold;
        }

            .close:hover,
            .close:focus {
                color: #000;
                text-decoration: none;
                cursor: pointer;
            }

        html, body {
            height: 100%;
        }

        body {
            margin: 0;
        }

        div[role="form"] {
            background-color: #3392FF;
        }

        #webchat {
            position: center;
            height: 530px;
            width: 100%;
            top: 60px;
            overflow: hidden;
        }
		.heading{
		padding-bottom: 5px;
		}
		.span{
        font-weight: bold;
        }
		
    </style>
</head>
<body>

<!--Button and ChatBot View-->

    <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 class="chat">
                <div class="heading">
                    <!-- Change the h1 text to change the bot name -->
				
    <img src="heading image added here" width="42" height="30" alt="logo">

                    <span class="span"><strong>Heading</strong></span>
                </div>
                <div id="webchat" role="main"></div><br>
            </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-->


    <!--SSO Code is added here-->
<script>

	
    function getOAuthCardResourceUri(activity) 
	{

        //alert("activity var inside getOAuthCardResourceUri: " + JSON.stringify(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

            //alert("Inside if condition of activity: " + JSON.stringify(activity.attachments[0].content.tokenExchangeResource.uri));

            return activity.attachments[0].content.tokenExchangeResource.uri;
        }
    }

    function exchangeTokenAsync(resourceUri) 
	{
	
        let user = clientApplication.getAccount();
		
		//alert("user value inside exchangeTokenAsync: " + JSON.stringify(user));
        debugger;
		if (user) 
		{
            let requestObj = 
			{
                scopes: [resourceUri]
            };
			//alert("requestObj inside exchangeTokenAsync: " + JSON.stringify(requestObj));
            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 = {}) 
		{
			//alert("url inside fetchJSON: " + JSON.stringify(url));
			const res = await fetch(url, {
				...options,
				headers: 
				{
					...options.headers,
					accept: 'application/json'
				}
		});
		//alert("res inside fetchJSON: " + JSON.stringify(res));
		if (!res.ok) 
		{
			throw new Error(`Failed to fetch JSON due to ${res.status}`);
		}

		return await res.json();
		} 
    </script>
<script>
	
	var clientApplication;
        (function () 
		{
            var msalConfig = {
                auth: 
				{
                    clientId: '<Client Id of canvas>',
                    authority: 'https://login.microsoftonline.com/<Directory ID>'
                },
                cache: 
				{
                    cacheLocation: 'localStorage',
                    storeAuthStateInCookie: false
                }
            };
			
            if (!clientApplication) 
			{
                
                //alert("msalConfig: " + JSON.stringify(msalConfig));
                
				clientApplication = new Msal.UserAgentApplication(msalConfig);
				
				//alert("clientApplication inside if: " + JSON.stringify(clientApplication));
            }
        }());
	
	
        (async function main() {
 // Add your BOT ID below
            var BOT_ID = "ABC";
            var theURL = "https://powerva.microsoft.com/api/botmanagement/v1/directline/directlinetoken?botId=" + BOT_ID;

            const styleOptions = {

                botAvatarImage: "BOT image",
                userAvatarImage: "user image",
                hideUploadButton: true
            };
			
             const { token } = await fetchJSON(theURL);
            const directLine = window.WebChat.createDirectLine({ token });
            
             var userID = clientApplication.account?.accountIdentifier != null ?
                ("mypva" + clientApplication.account.accountIdentifier).substr(0, 64)
                : (Math.random().toString() + Date.now().toString()).substr(0,64);

            
            //alert("userID inside async function is " + userID);
            

            //alert("Token:" + JSON.stringify(token));

            const store = window.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))) {
						
						//alert("Inside if DIRECT_LINE/INCOMING_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 => {
                                        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);
            });

            window.WebChat.renderWebChat({
                directLine: directLine,
                store,
                userID: userID,
                styleOptions
            },
                document.getElementById('webchat')
            );
        })().catch(err => console.error("An error occurred: " + err));
    </script>
	
    <!--SSO Code ends here-->

</body>
</html>

 

Regards,

Hemanth

Thank you Hemanth, it is required to pass a token to PVA for exchange in order for SSO to work. Are you able to do SSO use the sample file on github?

Suggest process to debug the issue

1. try to talk to your bot in PVA demo website and check if it can login successfully without SSO.

2. If #1 works, config the token exchange url field based on the SSO document.

3. Download the sample SSO test file on github and check if SSO works. If it does not work, you need to debug the code and see which steps failed the calls. Usually how SSO works is like this. When some user loggined to customer canvas, AAD will issue a login token and the client will call AAD to get an On-behalf-of token based on the login token. This On-behalf-of token will then be passed to PVA service and they will exchange this for a user access token. If you failed any steps above, the call will fail.

 

So you need to debug more into the code and tell what steps failed the call and what the error is so that we can help you

Helpful resources

Announcements
MBAS Attendee Badge

Claim Your Badge & Digital Swag!

Check out how to claim yours today!

March Update

Welcome to the User Group Private Preview

Check out new user group experience and if you are a leader please create your group

secondImage

Are Your Ready?

Test your skills now with the Cloud Skill Challenge.

Top Solution Authors
Top Kudoed Authors
Users online (88,227)