Very simple question, i have upgraded to latest version and now i cannot login with giving a subject? What should that be. Also if i enter anything, i then get
The authentication ticket was rejected because it doesn't contain the mandatory subject claim.
The very simple answer can be found here: http://kevinchalet.com/2017/01/30/implementing-simple-token-authentication-in-aspnet-core-with-openiddict/#comment-3182486876 :smile:
@PinpointTownes sorry little confused. It says to change the claims, but i aint using claims. My code looks nearly the same as the Mvc.Server AuthorizationController, and i cant see anything updated here?
Of course you are. It's just hidden because Identity does it for you.
Here's what you need to add: https://github.com/openiddict/openiddict-samples/blob/master/samples/PasswordFlow/AuthorizationServer/Startup.cs#L32-L40
@PinpointTownes although that works to log my in, can you explain why this code has stopped working??
_httpContextAccessor.HttpContext.User.Identity.Name
This now returns null, instead of the username?
But _httpContextAccessor.HttpContext.User.Identity.IsAuthenticated still returns true when the user is logged in??
Identity.Name uses the OpenIdConnectConstants.Claims.Name claim. Make sure it is there.
Yep, it is in the code you sent me
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
No, that's not what I meant. I meant in the User.Claims list.
Sorry if stupid today, long day, but not sure what you mean. Looked at claims in the identity as below??
As you can see, no username??

Tried this too, and returned string.Empty
if (!_httpContextAccessor.HttpContext.User.HasClaim(e => e.Type == OpenIdConnectConstants.Claims.Name))
return string.Empty;
That's exactly what I meant. As you can see, the name claim is not here. Are you sure it has the right destination?
I updated what u said... do I need to update something else? Got nothing to do with scopes created in authorizationcontroller does it?
I'm not fakir... share your code if you need help :sweat_smile:
Will post example when at laptop. I am using JWT do don't know if that effects anything?
@PinpointTownes after looking over my code and trying to create you an example, it appears i also needed to change the AuthorizationHandler as had this code before
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
foreach (var claim in principal.Claims)
{
// Always include the user identifier in the
// access token and the identity token.
if (claim.Type == ClaimTypes.NameIdentifier)
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Include the name claim, but only if the "profile" scope was requested.
else if (claim.Type == OpenIdConnectConstants.Claims.Name && scopes.Contains(OpenIdConnectConstants.Scopes.Profile))
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Include the role claims, but only if the "roles" scope was requested.
else if (claim.Type == ClaimTypes.Role && scopes.Contains(OpenIddictConstants.Scopes.Roles))
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// The other claims won't be added to the access
// and identity tokens and will be kept private.
}
Had to change these to use the new Claims, so became something like this
```
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
foreach (var claim in principal.Claims)
{
// Always include the user identifier in the
// access token and the identity token.
if (claim.Type == OpenIdConnectConstants.Claims.Name)
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// Include the role claims, but only if the "roles" scope was requested.
else if (claim.Type == OpenIdConnectConstants.Claims.Role && scopes.Contains(OpenIddictConstants.Scopes.Roles))
{
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken,
OpenIdConnectConstants.Destinations.IdentityToken);
}
// The other claims won't be added to the access
// and identity tokens and will be kept private.
}
Is this right? Seems to work now
Yes, that's fine.
Thank you
I'll leave this topic open to make sure it's visible enough 馃槃
Thanks for posting this - we ran into this same issue yesterday but figured it out after looking at the updated sample. We are successfully able to log in again.
There is one outstanding issue (maybe I should open a new issue for this).
We have a UserInfo endpoint in the AuthorizationController that is identical to the samples. This all used to work but since updating to beta2-0564 we are getting a null user when we call GetUserAsync:
var user = await _userManager.GetUserAsync(User);
We are using bearer authentication. I notice that User.Identity.Name is null though. The only difference I can see to the samples is that we are using EF Core and the demo uses an inMemory database.
@daveyjay have you updated your Startup code to include this snippet?
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
Yes, I have added those lines in.
Interestingly, I tried reverting back to version 1.0.0-beta2-0554 , without the new lines mentioned above everything works. User.Name is populated and _userManager.GetUserAsync(User) works as expected.
When I add in those new lines it breaks the behaviour and User.Name is null and _userManager.GetUserAsync returns null.
Could this be related to Issue #282 which is now closed?
Could this be related to Issue #282 which is now closed?
Very unlikely. Could you please share the version of the validation/introspection middleware you're using and the claims contained in User.Claims?
Here is the validation/introspection in my project.json
"AspNet.Security.OAuth.Validation": "1.0.0-*",
"AspNet.Security.OAuth.Introspection": "1.0.0-beta1-0200"
And here our the claims that appear when I am inside the Authorize method of the AuthorizationController:
[Authorize, HttpGet("~/connect/authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request) {
{sub: 7908119e-cc79-44f7-b469-ff2690e8a5f7} System.Security.Claims.Claim
{name: djohnson} System.Security.Claims.Claim
{AspNet.Identity.SecurityStamp: 43dd749b-af84-4037-8511-4d5cf147ef4c} System.Security.Claims.Claim
{http://schemas.microsoft.com/ws/2008/06/identity/claims/role: Clerk} System.Security.Claims.Claim
{email: [email protected]} System.Security.Claims.Claim
After authorizing and logging in I hit the UserinfoController Userinfo method:
[Authorize(ActiveAuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)]
[HttpGet("connect/userinfo"), Produces("application/json")]
public async Task<IActionResult> Userinfo()
{
var user = await _userManager.GetUserAsync(User); // this line returns null - User.Identity.Name is null
The claims I see in the User ClaimsPrincipal now are:
Notice that the sub claim is gone.
{iss: http://localhost:60909/} System.Security.Claims.Claim
{http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: 7908119e-cc79-44f7-b469-ff2690e8a5f7} System.Security.Claims.Claim
{scope: openid} System.Security.Claims.Claim
{scope: email} System.Security.Claims.Claim
{scope: profile} System.Security.Claims.Claim
{scope: roles} System.Security.Claims.Claim
{name: djohnson} System.Security.Claims.Claim
{AspNet.Identity.SecurityStamp: 43dd749b-af84-4037-8511-4d5cf147ef4c} System.Security.Claims.Claim
{http://schemas.microsoft.com/ws/2008/06/identity/claims/role: Clerk} System.Security.Claims.Claim
{email: [email protected]} System.Security.Claims.Claim
{usage: access_token} System.Security.Claims.Claim
{azp: OurApp} System.Security.Claims.Claim
Wait, I see your access token has a usage and an azp claim, are you using the JWT middleware?
Yes, I have enabled Json Web tokens in the Configure method like so:
services.AddOpenIddict()
.UseJsonWebTokens()
I notice that if I comment out these 3 lines:
services.Configure
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
//options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
then User.Identity.Name is populated and I do not have the usage and azp claims.
However, if I do include those new lines then I have both the usage and azp claims.
Again, this is with version 1.0.0-beta2-0554
Is there some other configuration that I need to do in order to get this to work?
The only difference I can see to the samples is that we are using EF Core and the demo uses an inMemory database.
Well, looks like you missed the most important one... your project uses the JWT middleware :trollface:
Is there some other configuration that I need to do in order to get this to work?
Yes. You'll now need to disable the automatic mapping made by the JWT bearer middleware that converts JWT claims to their ClaimTypes equivalent:
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = "http://localhost:58795/",
Audience = "resource_server",
RequireHttpsMetadata = false,
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = OpenIdConnectConstants.Claims.Subject,
RoleClaimType = OpenIdConnectConstants.Claims.Role
}
});
For more information, I suggest reading https://leastprivilege.com/2016/08/21/why-does-my-authorize-attribute-not-work/.
FYI, I spent some time updating all the samples to show how you're supposed to register the JWT middleware so it works correctly with the recent changes. Feel free to take a look.
Yes I did miss the most important one!
Thanks for explaining that - everything works correctly now. I appreciate your clear explanation and the samples being updated now as well - that should help others that may run into the same issue. That link is just what I needed to better understand how this all works under the covers.
Thanks for explaining that - everything works correctly now
Nice! :clap:
Off-topic: FYI, I just answered your SO question: http://stackoverflow.com/questions/42496306/openidconnect-access-token-size-and-accessing-claims-server-side.
Thanks! Will check that out.
Why my requests return 401 Unauthorized? It was working before the update.
The token endpoint produces the access_token normally, but sending the request with Bearer TOKEN returns Unauthorized.
AuthorizationController in a txt:
authorization.txt
That's my configuration:
services.AddDbContext<ApplicationEntityDbContext>(options =>
{
options.UseSqlServer(connectionString, x => x.MigrationsAssembly("Dreampper"));
options.UseOpenIddict<long>();
});
services.AddIdentity<ApplicationUserEntity, IdentityRole<long>>(x =>
{
x.User.RequireUniqueEmail = true;
x.Password.RequireDigit = false;
x.Password.RequiredLength = 6;
x.Password.RequireLowercase = false;
x.Password.RequireNonAlphanumeric = false;
x.Password.RequireUppercase = false;
})
.AddEntityFrameworkStores<ApplicationEntityDbContext, long>()
.AddDefaultTokenProviders();
services.AddScoped<ApplicationUserEntity>();
// Configure Identity to use the same JWT claims as OpenIddict instead
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
// which saves you from doing the mapping in your authorization controller.
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
// OpenIddict
// Register the OpenIddict services, including the default Entity Framework stores.
services.AddOpenIddict<long>()
.AddEntityFrameworkCoreStores<ApplicationEntityDbContext>()
// Enable the token endpoint (required to use the password flow).
.EnableTokenEndpoint("/authorization/token")
.EnableAuthorizationEndpoint("/authorization/authorize")
.EnableUserinfoEndpoint("/api/account/userinfo")
// Allow client applications to use the grant_type=password flow.
//.AllowClientCredentialsFlow()
.SetAccessTokenLifetime(TimeSpan.FromHours(168))
.SetRefreshTokenLifetime(TimeSpan.FromHours(268))
.AllowPasswordFlow()
// During development, you can disable the HTTPS requirement.
.DisableHttpsRequirement()
// Register a new ephemeral key, that is discarded when the application
// shuts down. Tokens signed using this key are automatically invalidated.
// This method should only be used during development.
.AddEphemeralSigningKey()
.AllowRefreshTokenFlow()
.AllowAuthorizationCodeFlow()
// Register the ASP.NET Core MVC binder used by OpenIddict.
// Note: if you don't call this method, you won't be able to
// bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
.AddMvcBinders();
app.Use(next => context =>
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken, new CookieOptions() { HttpOnly = false });
return next(context);
});
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIdentity();
app.UseOAuthValidation();
app.UseOpenIddict();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
// Add a new middleware validating access tokens.
app.UseOAuthValidation(options =>
{
options.Events = new OAuthValidationEvents
{
// Note: for SignalR connections, the default Authorization header does not work,
// because the WebSockets JS API doesn't allow setting custom parameters.
// To work around this limitation, the access token is retrieved from the query string.
OnRetrieveToken = context =>
{
context.Token = context.Request.Query["access_token"];
if (context.Token != null)
{
var a = context.Token;
}
return Task.FromResult(0);
}
};
});
app.UseFileServer(new FileServerOptions
{
EnableDefaultFiles = true,
EnableDirectoryBrowsing = false
});
app.UseCors(builder =>
{
builder.AllowAnyOrigin();
});
app.UseSignalR<NotificationsConnection>("/notifications");
app.UseSignalR<MessengerConnection>("/messenger");
app.UseSignalR<PaperplaneConnection>("/paperplanes");
app.UseMvc();
I ran into this issues today and adding
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
To my Startup.cs fixed the issue. However, the samples are missing this code. It might be worthwhile to update the code to reflect that.
However, the samples are missing this code.
Which sample(s) do you refer to?
All the samples that use Identity include this snippet:
Ah, I was actually looking at the PasswordFlow sample. I think I must've skimmed over it. The first comment had me a little confused when I clicked on it because it highlighted not that code. You're right, my bad!
Most helpful comment
FYI, I spent some time updating all the samples to show how you're supposed to register the JWT middleware so it works correctly with the recent changes. Feel free to take a look.