skip to Main Content

I have set up a solution with the following three assets:

  • an ASP.NET MVC Core Application
  • an ASP.NET Core Web API
  • an IdentityServer4 host

Currently, authentication works: if I try to access a protected resource in the MVC app, it redirects to IdentityServer4, then I can log on with my Facebook account, the user is provisioned and a subject ID is generated, and I’m redirected back to the MVC app. This works correctly.

I can also call a protected web API function from within the MVC site. However, in the claims that I receive in the web API, I don’t receive subject ID.

If I investigate further, then I can see that in my MVC application, I get back an access token from the RequestClientCredentialsTokenAsync call, but it only contains:

{
  "nbf": 1548693531,
  "exp": 1548697131,
  "iss": "http://localhost:5000",
  "aud": [
    "http://localhost:5000/resources",
    "mgpApi"
  ],
  "client_id": "mgpPortal",
  "scope": [
    "mgpApi"
  ]
}

What I would like is that I also receive subject ID in this access token, so that I also have it in the web API function that I call.

My IdentityServer4 host is configured with:

public void ConfigureServices(IServiceCollection services)
{
    var builder = services.AddIdentityServer()
            .AddInMemoryIdentityResources(Resources.GetIdentityResources())
            .AddInMemoryApiResources(Resources.GetApiResources())                
            .AddProfileService<ProfileService>()
            .AddInMemoryClients(Clients.Get());
}

…and I’m registering a client with:

new Client
{
    EnableLocalLogin = false,

    ClientId = "mgpPortal",
    ClientName = "MGP Portal Site",
    AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials,

    // where to redirect to after login
    RedirectUris = { "http://localhost:5002/signin-oidc" },

    // where to redirect to after logout
    PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },

    // secret for authentication
    ClientSecrets =
    {
        new Secret("secret".Sha256())
    },

    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        "mgpApi"
    },

    AllowOfflineAccess = true,
    AlwaysIncludeUserClaimsInIdToken = true,
    AlwaysSendClientClaims = true,
}

…and I also tried, as a test, to add claims with a ProfileService like:

public class ProfileService : IProfileService
{
    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        context.IssuedClaims.AddRange(context.Subject.Claims);
        context.IssuedClaims.Add(new Claim("sub", "test"));

        return Task.FromResult(0);
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        return Task.FromResult(0);
    }
}

But it doesn’t make any difference.

My MVC client is configured with:

public void ConfigureServices(IServiceCollection services)
{
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

    services.AddAuthentication(options =>
    {
        options.DefaultScheme = "Cookies";
        options.DefaultChallengeScheme = "oidc";                 
    })
    .AddCookie("Cookies")
    .AddOpenIdConnect("oidc", options =>
    {                
        options.Authority = "http://localhost:5000";
        options.RequireHttpsMetadata = false;                
        options.ClientId = "mgpPortal";
        options.SaveTokens = true;                
    });           
}

So what should I add in order to receive subject ID in the access token?

If more information is required, please let me know. Any help is appreciated!

2

Answers


  1. You are using the wrong flow, use hybrid flow. In client credentials there is no concept of user subject hence the issue you are experiencing.

    Login or Signup to reply.
  2. I was in a similar situation. After lot of proofs I have found that is very easy to use the same token of the user of a MVC application to access a different Web Api application. It is explained here:

    public async Task<IActionResult> CallApi()
    {
        var accessToken = await HttpContext.GetTokenAsync("access_token");
    
        var client = new HttpClient();
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
        ...
    }
    

    Here another version using IdentityModel:

    public async Task<IActionResult> CallApi()
    {
      var accessToken = await HttpContext.GetTokenAsync("access_token");
      var apiClient = new HttpClient();
      client.SetBearerToken(accessToken); 
      ....
      await client.PutAsync(url, json);   
    

    Hope it could help…

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search