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
You are using the wrong flow, use
hybrid
flow. Inclient credentials
there is no concept of user subject hence the issue you are experiencing.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:
Here another version using IdentityModel:
Hope it could help…