I'm trying to setup Bearer authentication for an SPA using ASP.NET Core 1.0. I've almost got it working for JwtToken with OpenIdConnect Server but have an issue that my custom claims are not returned with the token.
My Startup.cs logic for authentication is the following:
private void ConfigureAuthentication(IApplicationBuilder app)
{
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.Authority = "http://localhost:53844";
options.Audience = "http://localhost:53844";
options.RequireHttpsMetadata = false;
});
app.UseOpenIdConnectServer(options =>
{
options.TokenEndpointPath = "/api/v1/token";
options.AllowInsecureHttp = true;
options.AuthorizationEndpointPath = PathString.Empty;
options.Provider = new OpenIdConnectServerProvider
{
OnValidateClientAuthentication = context =>
{
context.Skipped();
return Task.FromResult<Object>(null);
},
OnGrantResourceOwnerCredentials = async context =>
{
var usersService = app.ApplicationServices.GetService<IUsersService>();
User user = usersService.getUser(context.Username, context.Password);
var identity = new ClaimsIdentity(new List<Claim>(), OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()));
identity.AddClaim(new Claim(ClaimTypes.Name, user.Id.ToString()));
identity.AddClaim(new Claim("myclaim", "4815162342"));
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
context.Options.AuthenticationScheme);
ticket.SetResources(new[] { "http://localhost:53844" });
ticket.SetAudiences(new [] {"http://localhost:53844"});
ticket.SetScopes(new [] {"email", "offline_access" });
context.Validated(ticket);
}
};
});
}
Both access_token and refresh_token are generating succesfully and when passing access_token in Authorization header system treats request as authorized.
The only issue is that all claims except NameIdentifier are not passed.
I use the following code to receive my claims for authenticated request:
public class WebUserContext : IUserContext
{
private readonly IHttpContextAccessor contextAccessor;
public WebUserContext(IHttpContextAccessor contextAccessor)
{
this.contextAccessor = contextAccessor;
}
public long UserId
{
get
{
ClaimsIdentity identity = Principal?.Identity as ClaimsIdentity;
if (identity == null)
{
return -1;
}
Claim claim = identity.Claims.FirstOrDefault(c => c.Type == ClaimTypes.Name); // There is no such claim in claims collection
return long.Parse(claim.Value);
}
}
private ClaimsPrincipal Principal => contextAccessor.HttpContext.User as ClaimsPrincipal;
}
What can be the reason my claims are not passed or extracted from the token?
Security.
Unlike
OAuthAuthorizationServerMiddleware
, ASOS doesn't assume access tokens are always consumed by your own resource servers (though I agree it's a common scenario) and refuses to serialize claims that don't explicitly specify a "destination" to avoid leaking confidential data to unauthorized parties.With JWT being the default format in ASOS beta4 (but not in the next beta), you must also keep in mind that even client applications (or users) can read your access tokens.
For this reason, you must explicitly attach a "destination" to your claims:
Specify
id_token
to serialize the claim in the identity token,token
to serialize it in the access token or both to serialize it in both tokens (there's no equivalent for authorization codes or refresh tokens as they are always encrypted and only readable by the authorization server itself)