Search code examples
c#dependency-injectionazure-active-directoryclaimsasp.net-core-2.0

How to add own claims after logging in to Azure AD with OIDC in ASP.NET Core 2 with dependency injection?


In ASP.NET Core 2 logging in to Azure AD is fairly easy, in ConfigureServices(IServiceCollection services) just add the following

// Azure AD login
services.AddAuthentication(a =>
{
    a.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    a.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    a.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(o => o.LoginPath = new PathString("/Account/SignIn"))
.AddOpenIdConnect(o =>
{
    o.ClientId = Configuration["Authentication:AzureAd:ClientId"];
    o.ClientSecret = Configuration["Authentication:AzureAd:ClientSecret"];
    o.Authority = Configuration["Authentication:AzureAd:AADInstance"] + 
                  Configuration["Authentication:AzureAd:TenantId"];
    o.CallbackPath = Configuration["Authentication:AzureAd:CallbackPath"];
    o.ResponseType = OpenIdConnectResponseType.CodeIdToken;
    o.Events = new OpenIdConnectEvents
    {
        OnRemoteFailure = RemoteFailure,
        OnTokenValidated = TokenValidated
    };
});

and everything works fine. Then I can add Claims in TokenValidated and that works fine aswell:

private Task TokenValidated(TokenValidatedContext context)
{
    var claims = new List<Claim>();
    var claim = new Claim(ClaimTypes.Role, "Test", ClaimValueTypes.String, "Issuername")
    context.Principal.AddIdentity(new ClaimsIdentity(claims));
    return Task.FromResult(0);
}

However, it's never quite that easy. The Claims I want are dependent on a external calls to a service, and the address is stored in the configuration.

In ConfigureServices I also have various classes added for dependency injection with works fine for the controllers.

services.AddTransient<IRoleClaims, RoleClaims>();

This RoleClaims is a class I want to call from the TokenValidated method, but as far as I can see I cannot use DI here. Nor can I access the ServiceCollection to get it via ActivatorUtilities.CreateInstance.

The constructor to RoleClaims looks like this:

public RoleClaims(IOptions<EmployeeConfiguration> configuration)

So, the big question: How is this supposed to work? Can I somehow use dependency injection in the TokenValidated method? Am I trying to add my own claims in the wrong place?


Solution

  • In ASP.NET Core 2.0, you can get a service from the contain using:

    private async Task TokenValidated(TokenValidatedContext context)
    {
      var widget = ctx.HttpContext.RequestServices.GetRequiredService<Widget>();
      ...
    }