Skip to content
This repository was archived by the owner on May 17, 2024. It is now read-only.

Commit 4d104ba

Browse files
committed
review changes
2 parents bdab777 + a835a09 commit 4d104ba

File tree

6 files changed

+71
-34
lines changed

6 files changed

+71
-34
lines changed

5-AccessControl/1-call-api-roles/AppCreationScripts/Configure.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,11 @@ Function ConfigureApplications
193193
New-MgApplicationOwnerByRef -ApplicationId $clientAadApplication.Id -BodyParameter = @{"@odata.id" = "htps://graph.microsoft.com/v1.0/directoryObjects/$user.ObjectId"}
194194
Write-Host "'$($user.UserPrincipalName)' added as an application owner to app '$($clientServicePrincipal.DisplayName)'"
195195
}
196+
197+
# Add optional claims
198+
$newClaim = CreateOptionalClaim -name "acct"
199+
$optionalClaims.AccessToken += ($newClaim)
200+
Update-MgApplication -ApplicationId $serviceAadApplication.Id -OptionalClaims $optionalClaims
196201

197202
# Add application Roles
198203
$appRoles = New-Object System.Collections.Generic.List[Microsoft.Graph.PowerShell.Models.MicrosoftGraphAppRole]

5-AccessControl/1-call-api-roles/AppCreationScripts/sample.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"Endpoint": "AAD v2.0",
99
"Languages": [
1010
"typescript",
11-
"csharp"
11+
"csharp",
12+
"javascript"
1213
],
1314
"Description": "Angular single-page application calling a protected web API using App Roles to implement Role-Based Access Control",
1415
"products": [

5-AccessControl/1-call-api-roles/README.md

Lines changed: 59 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ services: ms-identity
55
languages:
66
- typescript
77
- csharp
8+
- javascript
89
products:
910
- azure-active-directory
1011
- msal-js
@@ -31,10 +32,14 @@ description: Angular single-page application calling a protected web API using A
3132

3233
This sample demonstrates a cross-platform application suite involving an Angular single-page application (*TodoListSPA*) calling an ASP.NET Core web API (*TodoListAPI*) secured with the Microsoft identity platform. In doing so, it implements **Role-based Access Control** (RBAC) by using Azure AD **App Roles**.
3334

34-
Access control in Azure AD can be done with **Security Groups** as well, as we will cover in the [next tutorial](../2-call-api-groups/README.md). **Security Groups** and **App Roles** in Azure AD are by no means mutually exclusive - they can be used in tandem to provide even finer grained access control.
35+
Role based access control in Azure AD can be done with **Delegated** and **App** permissions and **Security Groups** as well. we will cover RBAC using Security Groups in the [next tutorial](../2-call-api-groups/README.md). **Delegated** and **App** permissions, **Security Groups** and **App Roles** in Azure AD are by no means mutually exclusive - they can be used in tandem to provide even finer grained access control.
3536

3637
In the sample, a **dashboard** component allows signed-in users to see the tasks assigned to users and is only accessible by users under an **app role** named **TaskAdmin**.
3738

39+
> :information_source: See the community call: [Implement authorization in your applications with the Microsoft identity platform](https://www.youtube.com/watch?v=LRoc-na27l0)
40+
41+
> :information_source: See the community call: [Deep dive on using MSAL.js to integrate Angular single-page applications with Azure Active Directory](https://www.youtube.com/watch?v=EJey9KP1dZA)
42+
3843
## Scenario
3944

4045
- The **TodoListSPA** uses [MSAL Angular](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular) to authenticate a user with the Microsoft identity platform.
@@ -165,10 +170,10 @@ To manually register the apps, as a first step you'll need to:
165170
1. Select **Add a scope** button open the **Add a scope** screen and Enter the values as indicated below:
166171
1. For **Scope name**, use `access_as_user`.
167172
1. Select **Admins and users** options for **Who can consent?**.
168-
1. For **Admin consent display name** type in the details, `e.g. Allow the users of the app msal-angular-app to read ToDo list items`.
169-
1. For **Admin consent description** type in the details `e.g. Allows the app msal-angular-app to read the signed-in users ToDo list items.`
170-
1. For **User consent display name** type in the details `e.g. Read Todolist items as yourself`.
171-
1. For **User consent description** type in the details `e.g. Allow the app msal-angular-app to read Todolist items on your behalf.`
173+
1. For **Admin consent display name** type in *Access 'msal-angular-app' as the signed-in user.*.
174+
1. For **Admin consent description** type in *Allow the app to access the 'msal-angular-app' as a signed-in user.*.
175+
1. For **User consent display name** type in *Access 'msal-angular-app' on your behalf.*.
176+
1. For **User consent description** type in *Allow the app to access the 'msal-angular-app' on your behalf.*.
172177
1. Keep **State** as **Enabled**.
173178
1. Select the **Add scope** button on the bottom to save this scope.
174179
1. Select the **Manifest** blade on the left.
@@ -207,15 +212,24 @@ To add users to this app role, follow the guidelines here: [Assign users and gro
207212
208213
For more information, see: [How to: Add app roles in your application and receive them in the token](https://docs.microsoft.com/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps)
209214
210-
##### Configure the app (msal-angular-app) to use your app registration
215+
##### (Optional) Configure Optional Claims
216+
217+
1. Still on the same app registration, select the **Token configuration** blade to the left.
218+
1. Select **Add optional claim**:
219+
1. Select **optional claim type**, then choose **Access**.
220+
1. Select the optional claim **acct**.
221+
> Provides user's account status in tenant. If the user is a member of the tenant, the value is 0. If they're a guest, the value is 1.
222+
1. Select **Add** to save your changes.
223+
224+
##### Configure the client app (msal-angular-app) to use your app registration
211225
212226
Open the project in your IDE (like Visual Studio or Visual Studio Code) to configure the code.
213227
214228
> In the steps below, "ClientID" is the same as "Application ID" or "AppId".
215229
216230
1. Open the `API\TodoListAPI\appsettings.json` file.
217-
1. Find the key `Enter the domain of your Azure AD tenant, e.g. contoso.onmicrosoft.com` and replace the existing value with your Azure AD tenant name.
218-
1. Find the key `Enter the ID of your Azure AD tenant copied from the Azure portal` and replace the existing value with your Azure AD tenant ID.
231+
1. Find the key `Enter the domain of your Azure AD tenant, e.g. contoso.onmicrosoft.com` and replace the existing value with your Azure AD tenant domain, ex. `contoso.onmicrosoft.com`.
232+
1. Find the key `Enter the ID of your Azure AD tenant copied from the Azure portal` and replace the existing value with your Azure AD tenant/directory ID.
219233
1. Find the key `Enter the application ID (clientId) of the 'TodoListAPI' application copied from the Azure portal` and replace the existing value with the application ID (clientId) of `msal-angular-app` app copied from the Azure portal.
220234
221235
1. Open the `SPA\src\app\auth-config.ts` file.
@@ -282,41 +296,61 @@ To provide feedback on or suggest features for Azure Active Directory, visit [Us
282296

283297
### Angular RoleGuard and protected routes for role-based access control
284298

285-
The client application Angular SPA has a **RoleGuard** (`role.guard.ts`) component that checks whether a user has the right privileges to access a protected route. It does this by checking `roles` claim the ID token of the signed-in user:
299+
The client application Angular SPA has a [role.guard.ts](./SPA/src/app/role.guard.ts) component that checks whether a user has the right privileges to access a protected route. It does this by checking `roles` claim the ID token of the signed-in user:
286300

287301
```typescript
288-
export class RoleGuard implements CanActivate {
302+
@Injectable()
303+
export class RoleGuard extends BaseGuard {
304+
305+
constructor(
306+
@Inject(MSAL_GUARD_CONFIG) protected override msalGuardConfig: MsalGuardConfiguration,
307+
protected override msalBroadcastService: MsalBroadcastService,
308+
protected override authService: MsalService,
309+
protected override location: Location,
310+
protected override router: Router
311+
) {
312+
super(msalGuardConfig, msalBroadcastService, authService, location, router);
313+
}
314+
315+
override activateHelper(state?: RouterStateSnapshot, route?: ActivatedRouteSnapshot): Observable<boolean | UrlTree> {
316+
let result = super.activateHelper(state, route);
289317

290-
constructor(private authService: MsalService) { }
318+
const expectedRoles: string[] = route ? route.data['expectedRoles'] : [];
291319

292-
canActivate(route: ActivatedRouteSnapshot): boolean {
293-
const expectedRoles: string[] = route.data['expectedRoles'];
294-
let account: AccountInfo = this.authService.instance.getActiveAccount()!;
320+
return result.pipe(
321+
concatMap(() => {
322+
let activeAccount = this.authService.instance.getActiveAccount();
295323

296-
if (!account.idTokenClaims?.roles) {
324+
if (!activeAccount && this.authService.instance.getAllAccounts().length > 0) {
325+
activeAccount = this.authService.instance.getAllAccounts()[0];
326+
}
327+
328+
if (!activeAccount?.idTokenClaims?.roles) {
297329
window.alert('Token does not have roles claim. Please ensure that your account is assigned to an app role and then sign-out and sign-in again.');
298-
return false;
330+
return of(false);
299331
}
300332

301-
if (!expectedRoles.includes(account.idTokenClaims.roles[0])) {
333+
const hasRequiredRole = expectedRoles.some((role: string) => activeAccount?.idTokenClaims?.roles?.includes(role));
334+
335+
if (!hasRequiredRole) {
302336
window.alert('You do not have access as the expected role is not found. Please ensure that your account is assigned to an app role and then sign-out and sign-in again.');
303-
return false;
304337
}
305338

306-
return true;
307-
}
339+
return of(hasRequiredRole);
340+
})
341+
);
342+
}
308343
}
309344
```
310345

311-
We then enable **RoleGuard** in `app-routing.module.ts` as follows:
346+
We then enable **RoleGuard** in [app-routing.module.ts](./SPA/src/app/app-routing.module.ts) as follows:
312347

313348
```typescript
314349
const routes: Routes = [
315350
{
316351
path: 'todo-edit/:id',
317352
component: TodoEditComponent,
318353
canActivate: [
319-
MsalGuard,
320354
RoleGuard
321355
],
322356
data: {
@@ -327,7 +361,6 @@ const routes: Routes = [
327361
path: 'todo-view',
328362
component: TodoViewComponent,
329363
canActivate: [
330-
MsalGuard,
331364
RoleGuard
332365
],
333366
data: {
@@ -338,7 +371,6 @@ const routes: Routes = [
338371
path: 'dashboard',
339372
component: DashboardComponent,
340373
canActivate: [
341-
MsalGuard,
342374
RoleGuard,
343375
],
344376
data: {
@@ -395,7 +427,7 @@ Finally, in `TodoListController.cs`, we decorate our routes with the appropriate
395427
// GET: api/todolist/getAll
396428
[HttpGet]
397429
[Route("getAll")]
398-
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes:Read")]
430+
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
399431
[Authorize(Policy = AuthorizationPolicies.AssignmentToTaskAdminRoleRequired)]
400432
public async Task<ActionResult<IEnumerable<TodoItem>>> GetAll()
401433
{
@@ -404,7 +436,7 @@ Finally, in `TodoListController.cs`, we decorate our routes with the appropriate
404436

405437
// GET: api/TodoItems
406438
[HttpGet]
407-
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes:Read")]
439+
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
408440
[Authorize(Policy = AuthorizationPolicies.AssignmentToTaskUserRoleRequired)]
409441
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
410442
{
@@ -435,4 +467,4 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope
435467
* [Building Zero Trust ready apps](https://aka.ms/ztdevsession)
436468
* [National Clouds](https://docs.microsoft.com/azure/active-directory/develop/authentication-national-cloud#app-registration-endpoints)
437469
* [Azure AD code samples](https://docs.microsoft.com/azure/active-directory/develop/sample-v2-code)
438-
* [Microsoft.Identity.Web](https://aka.ms/microsoft-identity-web)
470+
* [Microsoft.Identity.Web](https://aka.ms/microsoft-identity-web)

5-AccessControl/1-call-api-roles/SPA/src/app/base.guard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import { MsalBroadcastService, MsalGuardConfiguration, MSAL_GUARD_CONFIG, MsalSe
1313
import { InteractionType, BrowserConfigurationAuthError, BrowserUtils, UrlString, PopupRequest, RedirectRequest, AuthenticationResult } from "@azure/msal-browser";
1414

1515
@Injectable()
16-
export class MsalGuard implements CanActivate, CanActivateChild, CanLoad {
16+
export class BaseGuard implements CanActivate, CanActivateChild, CanLoad {
1717
private loginFailedRoute?: UrlTree;
1818

1919
constructor(
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
.card-section {
2-
margin: 10%;
2+
margin: 5%;
33
padding: 5%;
44
}

5-AccessControl/1-call-api-roles/SPA/src/app/role.guard.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { Observable, of } from "rxjs";
55
import { concatMap } from "rxjs/operators";
66

77
import { MsalBroadcastService, MsalGuardConfiguration, MsalService, MSAL_GUARD_CONFIG } from "@azure/msal-angular";
8-
import { MsalGuard } from "./base.guard";
8+
import { BaseGuard } from "./base.guard";
99

1010
@Injectable()
11-
export class RoleGuard extends MsalGuard {
11+
export class RoleGuard extends BaseGuard {
1212

1313
constructor(
1414
@Inject(MSAL_GUARD_CONFIG) protected override msalGuardConfig: MsalGuardConfiguration,
@@ -46,7 +46,6 @@ export class RoleGuard extends MsalGuard {
4646

4747
return of(hasRequiredRole);
4848
})
49-
)
50-
49+
);
5150
}
5251
}

0 commit comments

Comments
 (0)