Skip to content

Commit 7a15b4e

Browse files
author
Kalyan Krishna
committed
2 parents 2f679ef + 4d104ba commit 7a15b4e

File tree

9 files changed

+421
-140
lines changed

9 files changed

+421
-140
lines changed

5-AccessControl/1-call-api-roles/API/TodoListAPI/appsettings.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
{
22
"AzureAd": {
33
"Instance": "https://login.microsoftonline.com/",
4-
"Domain": "Enter the domain of your Azure AD tenant, e.g. 'contoso.onmicrosoft.com'",
5-
"TenantId": "Enter the tenant ID",
6-
"ClientId": "Enter the Client ID (aka 'Application ID')",
4+
"Domain": "Enter the domain of your Azure AD tenant, e.g. contoso.onmicrosoft.com",
5+
"TenantId": "Enter the ID of your Azure AD tenant copied from the Azure portal",
6+
"ClientId": "Enter the application ID (clientId) of the 'TodoListAPI' application copied from the Azure portal",
77
"Scopes": ["access_as_user"],
88
"Roles": {
99
"TaskAdmin": "TaskAdmin",

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]
Lines changed: 108 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,116 @@
11
{
2-
"Sample": {
3-
"Title": "Angular single-page application calling a protected web API and using App Roles to implement Role-Based Access Control",
4-
"Level": 300,
5-
"Client": "Angular SPA",
6-
"Service": ".NET Core web API",
7-
"RepositoryUrl": "ms-identity-javascript-angular-tutorial",
8-
"Endpoint": "AAD v2.0",
9-
"Languages": ["typescript", "csharp", "javascript"],
10-
"Description": "Angular single-page application calling a protected web API using App Roles to implement Role-Based Access Control",
11-
"products": ["azure-active-directory", "msal-js", "msal-angular", "microsoft-identity-web"]
12-
},
13-
"AADApps": [
14-
{
15-
"Id": "client",
16-
"Name": "msal-angular-app",
17-
"Kind": "SinglePageApplication",
18-
"Audience": "AzureADMyOrg",
19-
"HomePage": "http://localhost:4200/",
20-
"ReplyUrls": "http://localhost:4200/, http://localhost:4200/auth",
21-
"Scopes": ["access_as_user"],
22-
"Sample": {
23-
"SampleSubPath": "5-AccessControl\\1-call-api-roles\\SPA",
24-
"ProjectDirectory": "\\1-call-api-roles\\SPA"
25-
},
26-
"RequiredResourcesAccess": [
27-
{
28-
"Resource": "client",
29-
"DelegatedPermissions": ["access_as_user"]
30-
}
31-
],
32-
"AppRoles": [
33-
{
34-
"AllowedMemberTypes": [
35-
"User"
36-
],
37-
"Name": "TaskAdmin",
38-
"Description": "Admins can read any user's todo list"
39-
},
40-
{
41-
"AllowedMemberTypes": [
42-
"User"
43-
],
44-
"Name": "TaskUser",
45-
"Description": "Users can read and modify their todo lists"
46-
}
47-
],
48-
"ManualSteps": [
49-
{
50-
"Comment": "To receive the 'roles' claim with the name of the app roles this user is assigned to, make sure that the user accounts you plan to sign-in to this app is assigned to the app roles of this SPA app. The guide, https://aka.ms/userassignmentrequired provides step by step instructions."
51-
},
52-
{
53-
"Comment": "Or you can run the .\\CreateUsersAndAssignRoles.ps1 command to automatically create a number of users, and assign these users to the app roles of this app."
54-
}
55-
]
56-
}
57-
],
58-
"CodeConfiguration": [
59-
{
60-
"App": "client",
61-
"SettingKind": "Replace",
62-
"SettingFile": "\\..\\API\\TodoListAPI\\appsettings.json",
63-
"Mappings": [
64-
{
65-
"key": "Enter the domain of your Azure AD tenant, e.g. contoso.onmicrosoft.com",
66-
"value": "$tenantName"
67-
},
68-
{
69-
"key": "Enter the ID of your Azure AD tenant copied from the Azure portal",
70-
"value": "$tenantId"
71-
},
72-
{
73-
"key": "Enter the application ID (clientId) of the 'TodoListAPI' application copied from the Azure portal",
74-
"value": "client.AppId"
75-
}
76-
]
2+
"Sample": {
3+
"Title": "Angular single-page application calling a protected web API and using App Roles to implement Role-Based Access Control",
4+
"Level": 300,
5+
"Client": "Angular SPA",
6+
"Service": ".NET Core web API",
7+
"RepositoryUrl": "ms-identity-javascript-angular-tutorial",
8+
"Endpoint": "AAD v2.0",
9+
"Languages": [
10+
"typescript",
11+
"csharp",
12+
"javascript"
13+
],
14+
"Description": "Angular single-page application calling a protected web API using App Roles to implement Role-Based Access Control",
15+
"products": [
16+
"azure-active-directory",
17+
"msal-js",
18+
"msal-angular",
19+
"microsoft-identity-web"
20+
]
7721
},
78-
{
79-
"App": "client",
80-
"SettingKind": "Replace",
81-
"SettingFile": "\\..\\SPA\\src\\app\\auth-config.ts",
82-
"Mappings": [
22+
"AADApps": [
8323
{
84-
"key": "Enter_the_Application_Id_Here",
85-
"value": "client.AppId"
86-
},
24+
"Id": "client",
25+
"Name": "msal-angular-app",
26+
"Kind": "SinglePageApplication",
27+
"Audience": "AzureADMyOrg",
28+
"HomePage": "http://localhost:4200/",
29+
"ReplyUrls": "http://localhost:4200/, http://localhost:4200/auth",
30+
"Scopes": [
31+
"access_as_user"
32+
],
33+
"Sample": {
34+
"SampleSubPath": "5-AccessControl\\1-call-api-roles\\SPA",
35+
"ProjectDirectory": "\\1-call-api-roles\\SPA"
36+
},
37+
"RequiredResourcesAccess": [
38+
{
39+
"Resource": "client",
40+
"DelegatedPermissions": [
41+
"access_as_user"
42+
]
43+
}
44+
],
45+
"AppRoles": [
46+
{
47+
"AllowedMemberTypes": [
48+
"User"
49+
],
50+
"Name": "TaskAdmin",
51+
"Description": "Admins can read any user's todo list"
52+
},
53+
{
54+
"AllowedMemberTypes": [
55+
"User"
56+
],
57+
"Name": "TaskUser",
58+
"Description": "Users can read and modify their todo lists"
59+
}
60+
],
61+
"OptionalClaims": {
62+
"IdTokenClaims": [
63+
"acct"
64+
]
65+
},
66+
"ManualSteps": [
67+
{
68+
"Comment": "To receive the 'roles' claim with the name of the app roles this user is assigned to, make sure that the user accounts you plan to sign-in to this app is assigned to the app roles of this SPA app. The guide, https://aka.ms/userassignmentrequired provides step by step instructions."
69+
},
70+
{
71+
"Comment": "Or you can run the .\\CreateUsersAndAssignRoles.ps1 command to automatically create a number of users, and assign these users to the app roles of this app."
72+
}
73+
]
74+
}
75+
],
76+
"CodeConfiguration": [
8777
{
88-
"key": "Enter_the_Tenant_Info_Here",
89-
"value": "$tenantId"
78+
"App": "client",
79+
"SettingKind": "Replace",
80+
"SettingFile": "\\..\\API\\TodoListAPI\\appsettings.json",
81+
"Mappings": [
82+
{
83+
"key": "Enter the domain of your Azure AD tenant, e.g. contoso.onmicrosoft.com",
84+
"value": "$tenantName"
85+
},
86+
{
87+
"key": "Enter the ID of your Azure AD tenant copied from the Azure portal",
88+
"value": "$tenantId"
89+
},
90+
{
91+
"key": "Enter the application ID (clientId) of the 'TodoListAPI' application copied from the Azure portal",
92+
"value": "client.AppId"
93+
}
94+
]
9095
},
9196
{
92-
"key": "Enter_the_Web_Api_Application_Id_Here",
93-
"value": "client.AppId"
97+
"App": "client",
98+
"SettingKind": "Replace",
99+
"SettingFile": "\\..\\SPA\\src\\app\\auth-config.ts",
100+
"Mappings": [
101+
{
102+
"key": "Enter_the_Application_Id_Here",
103+
"value": "client.AppId"
104+
},
105+
{
106+
"key": "Enter_the_Tenant_Info_Here",
107+
"value": "$tenantId"
108+
},
109+
{
110+
"key": "Enter_the_Web_Api_Application_Id_Here",
111+
"value": "client.AppId"
112+
}
113+
]
94114
}
95-
]
96-
}
97-
]
115+
]
98116
}

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

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ In the sample, a **dashboard** component allows signed-in users to see the tasks
3838

3939
> :information_source: See the community call: [Implement authorization in your applications with the Microsoft identity platform](https://www.youtube.com/watch?v=LRoc-na27l0)
4040
41-
4241
> :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)
4342
4443
## Scenario
@@ -213,6 +212,15 @@ To add users to this app role, follow the guidelines here: [Assign users and gro
213212
214213
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)
215214
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+
216224
##### Configure the client app (msal-angular-app) to use your app registration
217225
218226
Open the project in your IDE (like Visual Studio or Visual Studio Code) to configure the code.
@@ -288,41 +296,61 @@ To provide feedback on or suggest features for Azure Active Directory, visit [Us
288296

289297
### Angular RoleGuard and protected routes for role-based access control
290298

291-
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:
292300

293301
```typescript
294-
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);
295317

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

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

302-
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) {
303329
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.');
304-
return false;
330+
return of(false);
305331
}
306332

307-
if (!expectedRoles.includes(account.idTokenClaims.roles[0])) {
333+
const hasRequiredRole = expectedRoles.some((role: string) => activeAccount?.idTokenClaims?.roles?.includes(role));
334+
335+
if (!hasRequiredRole) {
308336
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.');
309-
return false;
310337
}
311338

312-
return true;
313-
}
339+
return of(hasRequiredRole);
340+
})
341+
);
342+
}
314343
}
315344
```
316345

317-
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:
318347

319348
```typescript
320349
const routes: Routes = [
321350
{
322351
path: 'todo-edit/:id',
323352
component: TodoEditComponent,
324353
canActivate: [
325-
MsalGuard,
326354
RoleGuard
327355
],
328356
data: {
@@ -333,7 +361,6 @@ const routes: Routes = [
333361
path: 'todo-view',
334362
component: TodoViewComponent,
335363
canActivate: [
336-
MsalGuard,
337364
RoleGuard
338365
],
339366
data: {
@@ -344,7 +371,6 @@ const routes: Routes = [
344371
path: 'dashboard',
345372
component: DashboardComponent,
346373
canActivate: [
347-
MsalGuard,
348374
RoleGuard,
349375
],
350376
data: {
@@ -401,7 +427,7 @@ Finally, in `TodoListController.cs`, we decorate our routes with the appropriate
401427
// GET: api/todolist/getAll
402428
[HttpGet]
403429
[Route("getAll")]
404-
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes:Read")]
430+
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
405431
[Authorize(Policy = AuthorizationPolicies.AssignmentToTaskAdminRoleRequired)]
406432
public async Task<ActionResult<IEnumerable<TodoItem>>> GetAll()
407433
{
@@ -410,7 +436,7 @@ Finally, in `TodoListController.cs`, we decorate our routes with the appropriate
410436

411437
// GET: api/TodoItems
412438
[HttpGet]
413-
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes:Read")]
439+
[RequiredScope(RequiredScopesConfigurationKey = "AzureAd:Scopes")]
414440
[Authorize(Policy = AuthorizationPolicies.AssignmentToTaskUserRoleRequired)]
415441
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
416442
{

5-AccessControl/1-call-api-roles/SPA/src/app/app-routing.module.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ const routes: Routes = [
2020
path: 'todo-edit/:id',
2121
component: TodoEditComponent,
2222
canActivate: [
23-
MsalGuard,
2423
RoleGuard
2524
],
2625
data: {
@@ -31,7 +30,6 @@ const routes: Routes = [
3130
path: 'todo-view',
3231
component: TodoViewComponent,
3332
canActivate: [
34-
MsalGuard,
3533
RoleGuard
3634
],
3735
data: {
@@ -42,7 +40,6 @@ const routes: Routes = [
4240
path: 'dashboard',
4341
component: DashboardComponent,
4442
canActivate: [
45-
MsalGuard,
4643
RoleGuard,
4744
],
4845
data: {

0 commit comments

Comments
 (0)