Skip to content

Commit cc36d77

Browse files
author
Tiago Brenck
authored
Merge pull request #300 from Azure-Samples/tibre/mergeCleanup
Fixing merge
2 parents 00e7c8c + 5c86721 commit cc36d77

File tree

10 files changed

+80
-72
lines changed

10 files changed

+80
-72
lines changed

4-WebApp-your-API/4-2-B2C/Client/Views/Shared/_LoginPartial.cshtml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
{
44
<ul class="nav navbar-nav navbar-right">
55
<li class="navbar-text">Hello @User.GetDisplayName()!</li>
6+
<li class="navbar-btn">
7+
<form method="get" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="EditProfile">
8+
<button type="submit" class="btn btn-primary" style="margin-right:5px">Edit Profile</button>
9+
</form>
10+
</li>
611
<li><a asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignOut">Sign out</a></li>
712
</ul>
813
}

4-WebApp-your-API/4-2-B2C/Client/appsettings.json

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,6 @@
1010
"ClientSecret": "X330F3#92!z614M4"
1111
//"CallbackPath": "/signin/B2C_1_sign_up_in" // defaults to /signin-oidc
1212
},
13-
"AzureAd": {
14-
"Instance": "https://login.microsoftonline.com/",
15-
"Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
16-
"TenantId": "[Enter 'common', or 'organizations' or the Tenant Id (Obtained from the Azure portal. Select 'Endpoints' from the 'App registrations' blade and use the GUID in any of the URLs), e.g. da41245a5-11b3-996c-00a8-4d99re19f292]",
17-
"ClientId": "[Enter the Client Id (Application ID obtained from the Azure portal), e.g. ba74781c2-53c2-442a-97c2-3d60re42f403]",
18-
"CallbackPath": "/signin-oidc",
19-
"SignedOutCallbackPath ": "/signout-callback-oidc",
20-
21-
// To call an API
22-
"ClientSecret": "[Copy the client secret added to the app from the Azure portal]"
23-
},
2413
"TodoList": {
2514
/*
2615
TodoListScope is the scope of the Web API you want to call. This can be: "api://8f085429-c424-45c4-beb3-75f6f0a7924f/user_impersonation",
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
using Microsoft.AspNetCore.Authorization;
2+
using Microsoft.AspNetCore.Authorization.Infrastructure;
3+
using Microsoft.Identity.Web;
4+
using System;
5+
using System.Linq;
6+
using System.Security.Claims;
7+
using System.Threading.Tasks;
8+
9+
namespace TodoListService.AuthorizationPolicies
10+
{
11+
/// <summary>
12+
/// Requirement used in authorization policies, to check if the scope claim has at least one of the requirement values.
13+
/// Since the class also extends AuthorizationHandler, its dependency injection is done out of the box.
14+
/// </summary>
15+
public class ScopesRequirement : AuthorizationHandler<ScopesRequirement>, IAuthorizationRequirement
16+
{
17+
string[] _acceptedScopes;
18+
19+
public ScopesRequirement(params string[] acceptedScopes)
20+
{
21+
_acceptedScopes = acceptedScopes;
22+
}
23+
24+
/// <summary>
25+
/// AuthorizationHandler that will check if the scope claim has at least one of the requirement values
26+
/// </summary>
27+
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
28+
ScopesRequirement requirement)
29+
{
30+
// If there are no scopes, do not process
31+
if (!context.User.Claims.Any(x => x.Type == ClaimConstants.Scope)
32+
&& !context.User.Claims.Any(y => y.Type == ClaimConstants.Scp))
33+
{
34+
return Task.CompletedTask;
35+
}
36+
37+
Claim scopeClaim = context?.User?.FindFirst(ClaimConstants.Scp);
38+
39+
if (scopeClaim == null)
40+
scopeClaim = context?.User?.FindFirst(ClaimConstants.Scope);
41+
42+
if (scopeClaim != null && scopeClaim.Value.Split(' ').Intersect(requirement._acceptedScopes).Any())
43+
{
44+
context.Succeed(requirement);
45+
}
46+
47+
return Task.CompletedTask;
48+
}
49+
}
50+
}

4-WebApp-your-API/4-2-B2C/TodoListService/Controllers/TodoListController.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public TodoListController(IHttpContextAccessor contextAccessor)
5454

5555
// GET: api/values
5656
[HttpGet]
57+
[Authorize(Policy = "ReadScope")]
5758
public IEnumerable<Todo> Get()
5859
{
5960
string owner = User.Identity.Name;
@@ -62,6 +63,7 @@ public IEnumerable<Todo> Get()
6263

6364
// GET: api/values
6465
[HttpGet("{id}", Name = "Get")]
66+
[Authorize(Policy = "ReadScope")]
6567
public Todo Get(int id)
6668
{
6769
return TodoStore.Values.FirstOrDefault(t => t.Id == id);

4-WebApp-your-API/4-2-B2C/TodoListService/Startup.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
using Microsoft.Extensions.DependencyInjection;
99
using Microsoft.Identity.Web;
1010
using Microsoft.AspNetCore.Authentication.JwtBearer;
11+
using Microsoft.AspNetCore.Authorization;
12+
using TodoListService.AuthorizationPolicies;
13+
using Microsoft.AspNetCore.Authorization.Infrastructure;
1114

1215
namespace TodoListService
1316
{
@@ -31,9 +34,20 @@ public void ConfigureServices(IServiceCollection services)
3134

3235
// Adds Microsoft Identity platform (AAD v2.0) support to protect this Api
3336
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
34-
.AddProtectedWebApi("AzureAdB2C", Configuration, options => Configuration.Bind("AzureAdB2C", options));
37+
.AddProtectedWebApi("AzureAdB2C", Configuration, options =>
38+
{
39+
Configuration.Bind("AzureAdB2C", options);
40+
41+
options.TokenValidationParameters.NameClaimType = "name";
42+
});
3543

3644
services.AddControllers();
45+
services.AddAuthorization(options =>
46+
{
47+
// Create policy to check for the scope 'read'
48+
options.AddPolicy("ReadScope",
49+
policy => policy.Requirements.Add(new ScopesRequirement("read")));
50+
});
3751
}
3852

3953
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

4-WebApp-your-API/4-2-B2C/TodoListService/appsettings.json

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,6 @@
99
"EditProfilePolicyId": "b2c_1_edit_profile" // Optional profile editing policy
1010
//"CallbackPath": "/signin/B2C_1_sign_up_in" // defaults to /signin-oidc
1111
},
12-
"AzureAd": {
13-
"Instance": "https://login.microsoftonline.com/",
14-
"Domain": "[Enter the domain of your tenant, e.g. contoso.onmicrosoft.com]",
15-
"TenantId": "[Enter 'common', or 'organizations' or the Tenant Id (Obtained from the Azure portal. Select 'Endpoints' from the 'App registrations' blade and use the GUID in any of the URLs), e.g. da41245a5-11b3-996c-00a8-4d99re19f292]",
16-
"ClientId": "[Enter the Client Id (Application ID obtained from the Azure portal), e.g. ba74781c2-53c2-442a-97c2-3d60re42f403]"
17-
18-
},
1912
"Kestrel": {
2013
"Endpoints": {
2114
"Http": {

Microsoft.Identity.Web/AuthorizeForScopesAttribute.cs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public class AuthorizeForScopesAttribute : ExceptionFilterAttribute
4545
/// <param name="context">Context provided by ASP.NET Core</param>
4646
public override void OnException(ExceptionContext context)
4747
{
48+
// Do not re-use the attribute param Scopes. For more info: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/issues/273
4849
string[] incrementalConsentScopes = new string[] { };
4950
MsalUiRequiredException msalUiRequiredException = context.Exception as MsalUiRequiredException;
5051

@@ -57,9 +58,6 @@ public override void OnException(ExceptionContext context)
5758
{
5859
if (CanBeSolvedByReSignInOfUser(msalUiRequiredException))
5960
{
60-
// Do not re-use the attribute param Scopes. For more info: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/issues/273
61-
string[] scopes = null;
62-
6361
// the users cannot provide both scopes and ScopeKeySection at the same time
6462
if (!string.IsNullOrWhiteSpace(ScopeKeySection) && Scopes != null && Scopes.Length > 0)
6563
{
@@ -77,17 +75,17 @@ public override void OnException(ExceptionContext context)
7775
throw new InvalidOperationException($"The {nameof(ScopeKeySection)} is provided but the IConfiguration instance is not present in the services collection");
7876
}
7977

80-
scopes = new string[] { configuration.GetValue<string>(ScopeKeySection) };
78+
incrementalConsentScopes = new string[] { configuration.GetValue<string>(ScopeKeySection) };
8179

82-
if (Scopes != null && Scopes.Length > 0 && scopes != null && scopes.Length > 0)
80+
if (Scopes != null && Scopes.Length > 0 && incrementalConsentScopes != null && incrementalConsentScopes.Length > 0)
8381
{
8482
throw new InvalidOperationException("no scopes provided in scopes...");
8583
}
8684
}
8785
else
88-
scopes = Scopes;
86+
incrementalConsentScopes = Scopes;
8987

90-
var properties = BuildAuthenticationPropertiesForIncrementalConsent(scopes, msalUiRequiredException, context.HttpContext);
88+
var properties = BuildAuthenticationPropertiesForIncrementalConsent(incrementalConsentScopes, msalUiRequiredException, context.HttpContext);
9189
context.Result = new ChallengeResult(properties);
9290
}
9391
}

Microsoft.Identity.Web/ClaimConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ public static class ClaimConstants
2121
// Newer scope claim
2222
public const string Scp = "scp";
2323
public const string Roles = "roles";
24+
public const string Role = "http://schemas.microsoft.com/ws/2008/06/identity/claims/role";
2425
public const string Sub = "sub";
2526

2627
// Policy claims

Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ public static AuthenticationBuilder AddProtectedWebApi(
198198
// This check is required to ensure that the Web API only accepts tokens from tenants where it has been consented and provisioned.
199199
if (!context.Principal.Claims.Any(x => x.Type == ClaimConstants.Scope)
200200
&& !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Scp)
201-
&& !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Roles))
201+
&& !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Roles)
202+
&& !context.Principal.Claims.Any(y => y.Type == ClaimConstants.Role))
202203
{
203204
throw new UnauthorizedAccessException("Neither scope or roles claim was found in the bearer token.");
204205
}

Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -308,50 +308,5 @@ public static AuthenticationBuilder AddSignIn(
308308

309309
return builder;
310310
}
311-
312-
313-
// Method taken from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
314-
public static bool DisallowsSameSiteNone(string userAgent)
315-
{
316-
if (string.IsNullOrEmpty(userAgent))
317-
{
318-
return false;
319-
}
320-
321-
// Cover all iOS based browsers here. This includes:
322-
// - Safari on iOS 12 for iPhone, iPod Touch, iPad
323-
// - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
324-
// - Chrome on iOS 12 for iPhone, iPod Touch, iPad
325-
// All of which are broken by SameSite=None, because they use the iOS networking
326-
// stack.
327-
if (userAgent.Contains("CPU iPhone OS 12") ||
328-
userAgent.Contains("iPad; CPU OS 12"))
329-
{
330-
return true;
331-
}
332-
333-
// Cover Mac OS X based browsers that use the Mac OS networking stack.
334-
// This includes:
335-
// - Safari on Mac OS X.
336-
// This does not include:
337-
// - Chrome on Mac OS X
338-
// Because they do not use the Mac OS networking stack.
339-
if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") &&
340-
userAgent.Contains("Version/") && userAgent.Contains("Safari"))
341-
{
342-
return true;
343-
}
344-
345-
// Cover Chrome 50-69, because some versions are broken by SameSite=None,
346-
// and none in this range require it.
347-
// Note: this covers some pre-Chromium Edge versions,
348-
// but pre-Chromium Edge does not require SameSite=None.
349-
if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
350-
{
351-
return true;
352-
}
353-
354-
return false;
355-
}
356311
}
357312
}

0 commit comments

Comments
 (0)