Skip to content

Commit 2881976

Browse files
author
Tiago Brenck
authored
Merge pull request #253 from Azure-Samples/tibre/policyBasedAuthz
Policy based authZ for roles
2 parents e439c0b + f95596d commit 2881976

File tree

7 files changed

+95
-36
lines changed

7 files changed

+95
-36
lines changed

5-WebApp-AuthZ/5-1-Roles/Controllers/AccountController.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@ public IActionResult AccessDenied()
3333
return View();
3434
}
3535

36+
/// <summary>
37+
/// Fetches all the groups a user is assigned to. This method requires the signed-in user to be assigned to the 'DirectoryViewers' approle.
38+
/// </summary>
39+
/// <returns></returns>
3640
[AuthorizeForScopes(Scopes = new[] { GraphScopes.DirectoryReadAll })]
37-
[Authorize(Roles = AppRoles.DirectoryViewers)]
41+
[Authorize(Policy = AuthorizationPolicies.AssignmentToDirectoryViewerRoleRequired)]
3842
public async Task<IActionResult> Groups()
3943
{
4044
string[] scopes = new[] { GraphScopes.DirectoryReadAll };

5-WebApp-AuthZ/5-1-Roles/Controllers/HomeController.cs

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ public class HomeController : Controller
1919
private readonly ITokenAcquisition tokenAcquisition;
2020
private readonly WebOptions webOptions;
2121

22-
public HomeController(ITokenAcquisition tokenAcquisition,
23-
IOptions<WebOptions> webOptionValue)
22+
public HomeController(ITokenAcquisition tokenAcquisition, IOptions<WebOptions> webOptionValue)
2423
{
2524
this.tokenAcquisition = tokenAcquisition;
2625
this.webOptions = webOptionValue.Value;
@@ -56,6 +55,23 @@ public async Task<IActionResult> Profile()
5655
return View();
5756
}
5857

58+
/// <summary>
59+
/// Fetches and displays all the users in this directory. This method requires the signed-in user to be assigned to the 'UserReaders' approle.
60+
/// </summary>
61+
/// <returns></returns>
62+
[AuthorizeForScopes(Scopes = new[] { GraphScopes.UserReadBasicAll })]
63+
[Authorize(Policy = AuthorizationPolicies.AssignmentToUserReaderRoleRequired)]
64+
public async Task<IActionResult> Users()
65+
{
66+
// Initialize the GraphServiceClient.
67+
Graph::GraphServiceClient graphClient = GetGraphServiceClient(new[] { GraphScopes.UserReadBasicAll });
68+
69+
var users = await graphClient.Users.Request().GetAsync();
70+
ViewData["Users"] = users.CurrentPage;
71+
72+
return View();
73+
}
74+
5975
private Graph::GraphServiceClient GetGraphServiceClient(string[] scopes)
6076
{
6177
return GraphServiceClientFactory.GetAuthenticatedGraphClient(async () =>
@@ -72,17 +88,5 @@ public IActionResult Error()
7288
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
7389
}
7490

75-
[AuthorizeForScopes(Scopes = new[] { GraphScopes.UserReadBasicAll })]
76-
[Authorize(Roles = AppRoles.UserReaders)]
77-
public async Task<IActionResult> Users()
78-
{
79-
// Initialize the GraphServiceClient.
80-
Graph::GraphServiceClient graphClient = GetGraphServiceClient(new[] { GraphScopes.UserReadBasicAll });
81-
82-
var users = await graphClient.Users.Request().GetAsync();
83-
ViewData["Users"] = users.CurrentPage;
84-
85-
return View();
86-
}
8791
}
8892
}

5-WebApp-AuthZ/5-1-Roles/Infrastructure/AppRoles.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,27 @@
66
namespace WebApp_OpenIDConnect_DotNet.Infrastructure
77
{
88
/// <summary>
9-
/// Contains a list of all the Azure Ad app roles this app works with
9+
/// Contains a list of all the Azure AD app roles this app depends on and works with.
1010
/// </summary>
11-
public static class AppRoles
11+
public static class AppRole
1212
{
13+
/// <summary>
14+
/// User readers can read basic profiles of all users in the directory.
15+
/// </summary>
1316
public const string UserReaders = "UserReaders";
17+
18+
/// <summary>
19+
/// Directory viewers can view objects in the whole directory.
20+
/// </summary>
1421
public const string DirectoryViewers = "DirectoryViewers";
1522
}
23+
24+
/// <summary>
25+
/// Wrapper class the contain all the authorization policies available in this application.
26+
/// </summary>
27+
public static class AuthorizationPolicies
28+
{
29+
public const string AssignmentToUserReaderRoleRequired = "AssignmentToUserReaderRoleRequired";
30+
public const string AssignmentToDirectoryViewerRoleRequired = "AssignmentToDirectoryViewerRoleRequired";
31+
}
1632
}

5-WebApp-AuthZ/5-1-Roles/README-incremental-instructions.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,13 @@ public static IServiceCollection AddMicrosoftIdentityPlatformAuthentication(this
176176
// Use the groups claim for populating roles
177177
options.TokenValidationParameters.RoleClaimType = "roles";
178178
});
179+
180+
// Adding authorization policies that enforce authorization using Azure AD roles.
181+
services.AddAuthorization(options =>
182+
{
183+
options.AddPolicy(AuthorizationPolicies.AssignmentToUserReaderRoleRequired, policy => policy.RequireRole(AppRole.UserReaders));
184+
options.AddPolicy(AuthorizationPolicies.AssignmentToDirectoryViewerRoleRequired, policy => policy.RequireRole(AppRole.DirectoryViewers));
185+
});
179186
// [removed for] brevity
180187
}
181188

@@ -198,7 +205,7 @@ The following files have the code that would be of interest to you.
198205

199206
1. Startup.cs
200207

201-
1. In the `ConfigureServices` method of `Startup.cs', add the following line:
208+
1. In the `ConfigureServices` method of `Startup.cs', add the following lines:
202209

203210
```CSharp
204211
// This is required to be instantiated before the OpenIdConnectOptions starts getting configured.
@@ -211,16 +218,23 @@ The following files have the code that would be of interest to you.
211218
1. In the `HomeController.cs`, the following method is added with the `Authorize` attribute with the name of the app role **UserReaders**, that permits listing of users in the tenant.
212219

213220
```CSharp
214-
[Authorize(Roles = AppRoles.UserReaders )]
221+
[Authorize(Policy = AuthorizationPolicies.AssignmentToDirectoryViewerRoleRequired)]
215222
public async Task<IActionResult> Users()
216223
{
217224
```
218225

219226
1. In the `ConfigureServices` method of `Startup.cs', the following line instructs the asp.net security middleware to use the **roles** claim to fetch roles for authorization:
220227

221228
```CSharp
222-
// The claim in the Jwt token where App roles are available.
223-
options.TokenValidationParameters.RoleClaimType = "roles";
229+
// The claim in the Jwt token where App roles are available.
230+
options.TokenValidationParameters.RoleClaimType = "roles";
231+
232+
// Adding authorization policies that enforce authorization using Azure AD roles.
233+
services.AddAuthorization(options =>
234+
{
235+
options.AddPolicy(AuthorizationPolicies.AssignmentToUserReaderRoleRequired, policy => policy.RequireRole(AppRole.UserReaders));
236+
options.AddPolicy(AuthorizationPolicies.AssignmentToDirectoryViewerRoleRequired, policy => policy.RequireRole(AppRole.DirectoryViewers));
237+
});
224238
```
225239

226240
1. A new class called `AccountController.cs` is introduced. This contains the code to intercept the default AccessDenied error's route and present the user with an option to sign-out and sign-back in with a different account that has access to the required role.
@@ -234,7 +248,7 @@ The following files have the code that would be of interest to you.
234248
1. The following method is also added with the `Authorize` attribute with the name of the app role **DirectoryViewers**, that permits listing of roles and groups the signed-in user is assigned to.
235249

236250
```CSharp
237-
[Authorize(Roles = AppRoles.DirectoryViewers)]
251+
[Authorize(Policy = AuthorizationPolicies.AssignmentToUserReaderRoleRequired)]
238252
public async Task<IActionResult> Groups()
239253
{
240254
```

5-WebApp-AuthZ/5-1-Roles/README.md

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ There is one project in this sample. To register it, you can:
100100
1. In PowerShell run:
101101

102102
```PowerShell
103-
cd .\AppCreationScripts\
103+
cd .\AppCreationScripts\
104104
.\Configure.ps1
105105
```
106106

@@ -251,11 +251,18 @@ public static IServiceCollection AddMicrosoftIdentityPlatformAuthentication(this
251251
// Use the groups claim for populating roles
252252
options.TokenValidationParameters.RoleClaimType = "roles";
253253
});
254+
255+
// Adding authorization policies that enforce authorization using Azure AD roles.
256+
services.AddAuthorization(options =>
257+
{
258+
options.AddPolicy(AuthorizationPolicies.AssignmentToUserReaderRoleRequired, policy => policy.RequireRole(AppRole.UserReaders));
259+
options.AddPolicy(AuthorizationPolicies.AssignmentToDirectoryViewerRoleRequired, policy => policy.RequireRole(AppRole.DirectoryViewers));
260+
});
254261
// [removed for] brevity
255262
}
256263

257264
// In code..(Controllers & elsewhere)
258-
[Authorize(Roles = DirectoryViewers")] // In controllers
265+
[Authorize(Policy = AuthorizationPolicies.AssignmentToDirectoryViewerRoleRequired)]
259266
// or
260267
User.IsInRole("UserReaders"); // In methods
261268
```
@@ -303,21 +310,28 @@ This project was created using the following command.
303310
// 'http://schemas.microsoft.com/ws/2008/06/identity/claims/role' instead of 'roles'
304311
// This flag ensures that the ClaimsIdentity claims collection will be built from the claims in the token
305312
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
306-
```
307-
308-
1. In the `HomeController.cs`, the following method is added with the `Authorize` attribute with the name of the app role **UserReaders**, that permits listing of users in the tenant.
309313
310-
```CSharp
311-
[Authorize(Roles = AppRoles.UserReaders )]
312-
public async Task<IActionResult> Users()
313-
{
314+
// Adding authorization policies that enforce authorization using Azure AD roles.
315+
services.AddAuthorization(options =>
316+
{
317+
options.AddPolicy(AuthorizationPolicies.AssignmentToUserReaderRoleRequired, policy => policy.RequireRole(AppRole.UserReaders));
318+
options.AddPolicy(AuthorizationPolicies.AssignmentToDirectoryViewerRoleRequired, policy => policy.RequireRole(AppRole.DirectoryViewers));
319+
});
314320
```
315321

316322
1. In the `ConfigureServices` method of `Startup.cs', the following line instructs the asp.net security middleware to use the **roles** claim to fetch roles for authorization:
317323
318324
```CSharp
319-
// The claim in the Jwt token where App roles are available.
320-
options.TokenValidationParameters.RoleClaimType = "roles";
325+
// The claim in the Jwt token where App roles are available.
326+
options.TokenValidationParameters.RoleClaimType = "roles";
327+
```
328+
329+
1. In the `HomeController.cs`, the following method is added with the `Authorize` attribute with the name of the policy that enforces that the signed-in user is present in the app role **UserReaders**, that permits listing of users in the tenant.
330+
331+
```CSharp
332+
[Authorize(Policy = AuthorizationPolicies.AssignmentToUserReaderRoleRequired)]
333+
public async Task<IActionResult> Users()
334+
{
321335
```
322336
323337
1. A new class called `AccountController.cs` is introduced. This contains the code to intercept the default AccessDenied error's route and present the user with an option to sign-out and sign-back in with a different account that has access to the required role.
@@ -328,10 +342,10 @@ This project was created using the following command.
328342
{
329343
```
330344
331-
1. The following method is also added with the `Authorize` attribute with the name of the app role **DirectoryViewers**, that permits listing of roles and groups the signed-in user is assigned to.
345+
1. The following method is also added with the `Authorize` attribute with the name of the policy that enforces that the signed-in user is present in the app role **DirectoryViewers**, that permits listing of roles and groups the signed-in user is assigned to.
332346
333347
```CSharp
334-
[Authorize(Roles = AppRoles.DirectoryViewers)]
348+
[Authorize(Policy = AuthorizationPolicies.AssignmentToDirectoryViewerRoleRequired)]
335349
public async Task<IActionResult> Groups()
336350
{
337351
```

5-WebApp-AuthZ/5-1-Roles/Startup.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ public void ConfigureServices(IServiceCollection services)
6161
options.TokenValidationParameters.RoleClaimType = "roles";
6262
});
6363

64+
// Adding authorization policies that enforce authorization using Azure AD roles.
65+
services.AddAuthorization(options =>
66+
{
67+
options.AddPolicy(AuthorizationPolicies.AssignmentToUserReaderRoleRequired, policy => policy.RequireRole(AppRole.UserReaders));
68+
options.AddPolicy(AuthorizationPolicies.AssignmentToDirectoryViewerRoleRequired, policy => policy.RequireRole(AppRole.DirectoryViewers));
69+
});
70+
6471
services.AddControllersWithViews(options =>
6572
{
6673
var policy = new AuthorizationPolicyBuilder()

5-WebApp-AuthZ/5-1-Roles/WebApp-OpenIDConnect-DotNet.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
<ItemGroup>
2121
<PackageReference Include="Microsoft.AspNetCore.Authentication.AzureAD.UI" Version="3.0.0" />
22-
<PackageReference Include="Microsoft.Graph" Version="1.14.0" />
22+
<PackageReference Include="Microsoft.Graph" Version="1.21.0" />
2323
</ItemGroup>
2424

2525
<ItemGroup>

0 commit comments

Comments
 (0)