Skip to content

Commit fcc6b52

Browse files
committed
Updates articles about Bot Powered ACEs security
1 parent 2fc6131 commit fcc6b52

File tree

2 files changed

+107
-3
lines changed

2 files changed

+107
-3
lines changed

docs/spfx/viva/bot-powered/AuthN-and-AuthZ-in-Bot-Powered-ACEs-Entra.md

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ Go back to the Visual Studio project that you created before. Open the **appsett
100100
}
101101
```
102102

103+
To have the latest types needed to support the security infrastructure of the Bot Powered ACE, upgrade the NuGet package with name "Microsoft.Bot.Builder.Integration.AspNet.Core" to version 4.22.9 or higher.
104+
103105
Rename the **EmptyBot.cs** file into **WelcomeUserBot.cs**, change the base class from `ActivityHandler` to `SharePointActivityHandler`, and import the namespace `Microsoft.Bot.Builder.SharePoint`.
104106
Follow the instructions provided in the ["Implement the actual Bot Powered ACE"](./Building-Your-First-Bot-Powered-ACE.md#implement-the-actual-bot-powered-ace) section of the reference article ["Building your first Bot Powered Adaptive Card Extension"](./Building-Your-First-Bot-Powered-ACE.md) to implement the basic code of the Bot Powered ACE. Specifically, implement four Card Views:
105107

@@ -231,6 +233,94 @@ The Signed out Card View is a basic card view with a simple text message in the
231233
232234
In the sample solution, there's also an Error Card View, which for the sake of simplicity isn't illustrated in this article but is available in the [reference solution](https://github.com/pnp/viva-dev-bot-powered-aces/tree/main/samples/dotnet/WelcomeUserBotPoweredAce-SSO).
233235

236+
#### Implementing Bot Powered ACEs specific methods
237+
238+
You also need to implement couple of methods to handle the card view rendering and any action, like a button selection, in the UI of the card views. Following code excerpt shows how to implement both the `OnSharePointTaskGetCardViewAsync` and `OnSharePointTaskHandleActionAsync` methods.
239+
240+
```CSharp
241+
protected async override Task<CardViewResponse> OnSharePointTaskGetCardViewAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
242+
{
243+
// Check to see if the user has already signed in
244+
var (displayName, upn) = await GetAuthenticatedUser(magicCode: null, turnContext, cancellationToken);
245+
if (displayName != null && upn != null)
246+
{
247+
var homeCardView = cardViews[HomeCardView_ID];
248+
if (homeCardView != null)
249+
{
250+
((homeCardView.CardViewParameters.Header.ToList())[0] as CardTextComponent).Text = $"Welcome {displayName}!";
251+
((homeCardView.CardViewParameters.Body.ToList())[0] as CardTextComponent).Text = $"Your UPN is: {upn}";
252+
return homeCardView;
253+
}
254+
}
255+
else
256+
{
257+
var signInCardView = cardViews[SignInCardView_ID];
258+
if (signInCardView != null)
259+
{
260+
var signInResource = await GetSignInResource(turnContext, cancellationToken);
261+
var signInLink = signInResource != null ? new Uri(signInResource.SignInLink) : new Uri(string.Empty);
262+
263+
signInCardView.AceData.Properties = Newtonsoft.Json.Linq.JObject.FromObject(new Dictionary<string, object>() {
264+
{ "uri", signInLink },
265+
{ "connectionName", this._connectionName }
266+
});
267+
return signInCardView;
268+
}
269+
}
270+
271+
return cardViews[ErrorCardView_ID];
272+
}
273+
274+
protected async override Task<BaseHandleActionResponse> OnSharePointTaskHandleActionAsync(ITurnContext<IInvokeActivity> turnContext, AceRequest aceRequest, CancellationToken cancellationToken)
275+
{
276+
if (turnContext != null)
277+
{
278+
if (cancellationToken.IsCancellationRequested)
279+
{
280+
cancellationToken.ThrowIfCancellationRequested();
281+
}
282+
}
283+
JObject actionParameters = aceRequest.Data as JObject;
284+
285+
if (actionParameters != null)
286+
{
287+
var actionId = actionParameters["id"].ToString();
288+
if (actionId == "SignOut")
289+
{
290+
await SignOutUser(turnContext, cancellationToken);
291+
292+
return new CardViewHandleActionResponse
293+
{
294+
RenderArguments = cardViews[SignedOutCardView_ID]
295+
};
296+
}
297+
else if (actionId == "OkSignedOut")
298+
{
299+
return new CardViewHandleActionResponse
300+
{
301+
RenderArguments = cardViews[SignInCardView_ID]
302+
};
303+
}
304+
else if (actionId == "OkError")
305+
{
306+
return new CardViewHandleActionResponse
307+
{
308+
RenderArguments = cardViews[HomeCardView_ID]
309+
};
310+
}
311+
}
312+
313+
return new CardViewHandleActionResponse
314+
{
315+
RenderArguments = cardViews[ErrorCardView_ID]
316+
};
317+
}
318+
```
319+
320+
The `OnSharePointTaskGetCardViewAsync` method handles the rendering of the Welcome card view, rendering the display name and the user principal name of the currently authenticated user, if any. On the contrary, if there isn't a user's security context, it renders the Sign-in card view, configuring the name of the OAuth connection to use and retrieving the Sign-in URL from the infrastructural services provided by the Bot Framework via the `GetSignInResource` method.
321+
322+
The `OnSharePointTaskHandleActionAsync` method handles the selection of all the buttons provided in the UI of the Bot Powered ACE.
323+
234324
#### Handling single sign-on dedicated logic
235325

236326
To authenticate users with single sign-on, you need to override the `OnSignInInvokeAsync` method to handle the single sign-on request.
@@ -313,7 +403,7 @@ If you're using dependency injection, you can configure the cache manager as a s
313403

314404
Once you support the single sign-on logic, you can rely on a set of utility methods to retrieve the current user's information. These methods retrieve the current authenticated user, their token, and provide the sign out logic, if there's need.
315405

316-
Here follows the internal logic that you should rely on to manage the access token and the current user's identity retrieval through the `GetAuthenticatedUser` and `GetUserToken` methods.
406+
Here follows the internal logic that you should rely on to manage the access token and the current user's identity retrieval through the `GetAuthenticatedUser`, `GetUserToken`, and `GetSignInResource` methods.
317407

318408
```CSharp
319409
private async Task<(string displayName, string upn)> GetAuthenticatedUser(string magicCode, ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
@@ -352,6 +442,16 @@ private async Task<TokenResponse> GetUserToken(string magicCode, ITurnContext<II
352442
magicCode,
353443
cancellationToken).ConfigureAwait(false);
354444
}
445+
446+
private async Task<SignInResource> GetSignInResource(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
447+
{
448+
// Get the UserTokenClient service instance
449+
var userTokenClient = turnContext.TurnState.Get<UserTokenClient>();
450+
451+
// Retrieve the Sign In Resource from the UserTokenClient service instance
452+
var signInResource = await userTokenClient.GetSignInResourceAsync(_connectionName, (Microsoft.Bot.Schema.Activity)turnContext.Activity, null, cancellationToken).ConfigureAwait(false);
453+
return signInResource;
454+
}
355455
```
356456

357457
The `GetAuthenticatedUser` method accepts the magic code value and the Bot `TurnContext` instance. Internally it uses the `GetUserToken` method to retrieve the actual access token value and then uses the `JwtSecurityToken` class of `System.IdentityModel.Tokens.Jwt` to decode the token and get access to the user's display name and user principal name.
@@ -370,6 +470,8 @@ The `GetUserToken` method retrieves an instance of the `UserTokenClient` service
370470
The result of the `GetUserTokenAsync` method is an instance of the `TokenResponse` type that includes a `Token` property with the actual value of the access token.
371471
Once you have the access token and you extracted the display name and the user principal name, you can render them in the welcome card view.
372472

473+
The `GetSignInResource` method relies on an instance of the `UserTokenClient` service and retrieves the URL to use for signing in the user invoking the `GetSignInResourceAsync` method.
474+
373475
In the code excerpt, you can also see how the sign out is handled, invoking the custom `SignOutUser` method, which is illustrated in the following code excerpt.
374476

375477
```CSharp

docs/spfx/viva/bot-powered/AuthN-and-AuthZ-in-Bot-Powered-ACEs-Magic-Code.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ Following picture you can see how the Adaptive Card Extension looks like in the
2525

2626
In both scenarios (desktop and mobile), there are:
2727

28-
- a "Sign in" button to initiate the sign-in flow
29-
- a "Complete sign in" button to provide the magic code obtained by the Bot Framework and complete the authentication flow
28+
- "Sign in" button to initiate the sign-in flow
29+
- "Complete sign in" button to provide the magic code obtained by the Bot Framework and complete the authentication flow
3030

3131
From a developer point of view, you build the ACE once and you benefit of it in both desktop and mobile experiences.
3232
The whole source code of the .NET sample is available in the following GitHub repository: [Welcome User Bot Powered ACE](https://github.com/pnp/viva-dev-bot-powered-aces/tree/main/samples/dotnet/WelcomeUserBotPoweredAce).
@@ -85,6 +85,8 @@ Go back to the Visual Studio project that you created before. Open the **appsett
8585
}
8686
```
8787

88+
To have the latest types needed to support the security infrastructure of the Bot Powered ACE, upgrade the NuGet package with name "Microsoft.Bot.Builder.Integration.AspNet.Core" to version 4.22.9 or higher.
89+
8890
Rename the **EmptyBot.cs** file into **WelcomeUserBot.cs**, change the base class from `ActivityHandler` to `SharePointActivityHandler`, and import the namespace `Microsoft.Bot.Builder.SharePoint`.
8991
Follow the instructions provided in the ["Implement the actual Bot Powered ACE"](./Building-Your-First-Bot-Powered-ACE.md#implement-the-actual-bot-powered-ace) section of the reference article ["Building your first Bot Powered Adaptive Card Extension"](./Building-Your-First-Bot-Powered-ACE.md) to implement the basic code of the Bot Powered ACE. Specifically, implement four Card Views:
9092

0 commit comments

Comments
 (0)