ASP.NET Core 2.2 Azure AD Authorisation Middleware Not Handling Overages
Opened this issue · 3 comments
Cross-posted from this issue on ASP.NET Core GitHub library after being referred to this repo.
Describe the bug
I've recently set up authentication on an ASP.NET Core website I'm working on, and am now trying to authorize users based on their membership to a security group. I'm using this tutorial to assist me with the setup.
I've set up my AAD application with and received admin approval for the API permissions: User.Read and Directory.Read.All. I can tell that the basic AAD application is hooked up to our website correctly, since our application can successfully read a user's basic profile information after sign-on.
I have also enabled "groupMembershipClaims": "SecurityGroup"
in the application manifest to provide all group membership claims to our application.
However, when I attempt to restrict access to a controller with the following attribute:
[Authorize(Policy = "Admins")]
The result comes back unauthorized.
To investigate, I ran the following command to retrieve the list of all security group claims for a given user:
List<Claim> groups = User.Claims.Where(c => c.Type == "groups").ToList();
The result comes back with a Group Overage Claim indirection link ("_group_claims"
and "group_sources"
), leading me to believe that this might be caused by how the AAD authorization SDK handles group overages.
Is there anyting I've missed here?
To Reproduce
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "microsoft.onmicrosoft.com",
"TenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
"ClientId": "a3fc9157-32af-49a2-xxxx-xxxxxxxxxxxx",
"CallbackPath": "/signin-oidc"
},
"AzureSecurityGroup": {
"AdminObjectId": "48e5a47d-d6a2-44d8-xxxx-xxxxxxxxxxxx"
},
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AllowedHosts": "*"
}
startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddAuthorization(options =>
{
options.AddPolicy(
"Admins",
policyBuilder => policyBuilder.RequireClaim(
"groups",
this.Configuration.GetValue<string>("AzureSecurityGroup:AdminObjectId")));
});
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => this.Configuration.Bind("AzureAd", options))
.AddCookie();
services.Configure<CookieAuthenticationOptions>(
AzureADDefaults.CookieScheme,
options =>
{
options.AccessDeniedPath = $"/Error/403";
options.LoginPath = $"/Error/401";
});
services.AddMvc(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
Secured controller:
[Authorize(Policy = "Admins")]
public class AdminController : Controller
{
...
}
-->
Further technical details
- ASP.NET Core version: 2.2
- Include the output of
dotnet --info
.NET Core SDK (reflecting any global.json):
Version: 3.1.101
Commit: b377529961
Runtime Environment:
OS Name: Windows
OS Version: 10.0.18363
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\3.1.101\
Host (useful for support):
Version: 3.1.1
Commit: a1388f194c
.NET Core SDKs installed:
2.1.700 [C:\Program Files\dotnet\sdk]
2.2.300 [C:\Program Files\dotnet\sdk]
3.1.101 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.All 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.1.15 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 2.2.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
- The IDE (VS / VS Code/ VS4Mac) you're running on, and it's version: VS2019
@jublair : this is not an ADAL.NET issue.
Would you mind looking at the following sample and see if this solves your issue?
https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/5-WebApp-AuthZ/5-2-Groups
Thanks for the quick reply @jmprieur. Not sure which repo is the right one to contact here, since the ASP.NET Core devs referred me in this direction. Do you know the right repo to which I should file this issue, if it's not here nor there?
I have attempted the guidance listed here, but to no avail. The section just above it discussing overage claims appears to refer to processing overage claims when calling Graph API directly, but not in relation to the ASP.NET Core middleware libraries.
I've identified that the best workaround in this case (and one that also improves code reliability) is to make use of AAD Enterprise Application Roles, rather than checking group claims directly. That way I can continue to use the default AAD SDK, with just a few tweaks.
startup.cs:
services.AddAuthorization(options =>
{
options.AddPolicy("Admins", policy => policy.RequireClaim(ClaimTypes.Role, "Admin"));
});