I would like to create an ASP.NET Core 2.0 application that uses Azure Active Directory as the identity provider (for authentication) but ASP.NET Core Identity for authorization (e.g. using controller attributes like '[Authorize(Roles = "Admin")]'). In the solution, I expect the local Identity database table AspNetUserLogins to hold references to the Azure Active Directory identities
I think the solution would involve claim transformation to decorate the authenticated user with roles fetched from ASP.NET Core Identity.
My problems:
Steps to reproduce my baseline...
In portal.azure.com...
Create ASP.NET Core 2.0 project using Visual Studio 2017 15.4.2...
I'm less certain from here...
(I borrowed code from the template-generated solution with 'authentication options' set to 'Individual User Accounts' > 'Store user accounts in-app'.)
Add the following classes
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<AspNetCoreIdentity.Data.ApplicationDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
public class ApplicationUser : IdentityUser
{
}
Add the following at the beginning of Startup.ConfigureServices()
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Add connection string to appsettings.json (assumes default SQL Server instance on localhost and identity database named 'AspNetCoreIdentity')
"ConnectionStrings": {
"DefaultConnection": "Data Source=.\\;Initial Catalog=AspNetCoreIdentity;Integrated Security=True;MultipleActiveResultSets=True"
}
Now when I run the app again, I end up in a redirect loop which I think runs between my app and Azure Active Directory sign on. Tracing shows...
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService: Information: Authorization failed for user: (null).
Microsoft.AspNetCore.Mvc.RazorPages.Internal.PageActionInvoker: Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
I then tried adding methods to AccountController (Login, ExternalLogin) in the hope that I could hit a breakpoint, but now I'm really stuck.
other references...
Select ASP.NET Core Web Application>Choose Web Application (Model-View-Controller) template> Click on the "Change Authentication" button>Select "Work or School Accounts". Choose Cloud - Single Organization. Fill up the field of Domain which is the Azure Active Directory tenant name (say, softdreams.onmicrosoft.com).
In the New ASP.NET Project dialog, select MVC, and then click Change Authentication. On the Change Authentication dialog, select Organizational Accounts. These options can be used to automatically register your application with Azure AD as well as automatically configure your application to integrate with Azure AD.
What is Active Directory Authentication and Authorization? Active Directory is a directory service implemented by Microsoft for Windows domain networks. An Active Directory domain controller authenticates and authorizes users in a Windows-domain network by enforcing security policies for all computers.
I think I have this working, but I'm pretty new to this framework, so critiques to this method are welcome.
In startup, I had to add two things to the example from Microsoft.
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddCookie(options =>
{
options.AccessDeniedPath = "/AccessDenied";
});
// Remaining code removed
I then extended the AzureAdAuthenticationBuilderExtensions > ConfigureAzureOptions class and am doing some additional work (i.e. loading the user roles from whatever the role store is) when the token validation event occurs
AzureAdAuthenticationBuilderExtensions.cs
public void Configure(string name, OpenIdConnectOptions options)
{
options.ClientId = _azureOptions.ClientId;
options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
options.UseTokenLifetime = true;
options.CallbackPath = _azureOptions.CallbackPath;
options.RequireHttpsMetadata = false;
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = (context) =>
{
// Load roles from role store here
var roles = new List<string>() { "Admin" };
var claims = new List<Claim>();
foreach (var role in roles) claims.Add(new Claim(ClaimTypes.Role, role));
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
context.Principal.AddIdentity(claimsIdentity);
return Task.CompletedTask;
}
};
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With