In the past 7 days I've tried to setup an ASP.NET 5 WebApi using OpenIdConnect.Server
with the resource owner flow.
I was more or less successful in generating a token and accessing [Authorize]
protected actions.
However, when I try to access this.User.Identity.Claims
, it's empty. I am using ASP.NET 5, beta6 for now (having troubles upgrading to most recent beta7 and waiting for it's official release)
In the Startup.cs I got the following:
public void ConfigureServices(IServiceCollection services)
{
services.AddCaching();
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<AuthContext>(options =>
{
options.UseSqlServer(Configuration.Get("Data:DefaultConnection:ConnectionString"));
});
services.AddIdentity<AuthUser, AuthRole>(
options => options.User = new Microsoft.AspNet.Identity.UserOptions
{
RequireUniqueEmail = true,
UserNameValidationRegex = "^[a-zA-Z0-9@_\\.-]+$"
})
.AddEntityFrameworkStores<AuthContext, Guid>()
.AddDefaultTokenProviders();
services.ConfigureCors(configure =>
{
configure.AddPolicy("CorsPolicy", builder =>
{
builder.WithOrigins("http:/localhost/", "http://win2012.bludev.com/");
});
});
services.AddScoped<IAuthRepository, AuthRepository>();
}
public void Configure(IApplicationBuilder app)
{
var factory = app.ApplicationServices.GetRequiredService<ILoggerFactory>();
factory.AddConsole();
app.UseStaticFiles();
app.UseOAuthBearerAuthentication(options =>
{
options.Authority = "http://win2012.bludev.com/api/auth/";
options.Audience = "http://win2012.bludev.com/";
options.AutomaticAuthentication = true;
options.TokenValidationParameters = new TokenValidationParameters()
{
RequireExpirationTime = true,
RequireSignedTokens = true,
RoleClaimType = ClaimTypes.Role,
NameClaimType = ClaimTypes.NameIdentifier,
ValidateActor = true,
ValidateAudience = false,
ValidateIssuer = true,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
ValidateSignature = true,
ValidAudience = "http://win2012.bludev.com/",
ValidIssuer = "http://win2012.bludev.com/"
};
});
app.UseOpenIdConnectServer(options =>
{
options.Issuer = new Uri("http://win2012.bludev.com/api/auth/");
options.AllowInsecureHttp = true;
options.AuthorizationEndpointPath = PathString.Empty;
options.Provider = new AuthorizationProvider();
options.ApplicationCanDisplayErrors = true;
// Note: in a real world app, you'd probably prefer storing the X.509 certificate
// in the user or machine store. To keep this sample easy to use, the certificate
// is extracted from the Certificate.pfx file embedded in this assembly.
options.UseCertificate(
assembly: typeof(Startup).GetTypeInfo().Assembly,
resource: "AuthExample.Certificate.pfx",
password: "Owin.Security.OpenIdConnect.Server");
});
app.UseIdentity();
app.UseMvc();
}
}
I used app.UseOAuthBearerAuthentication
because I couldn't get app.UseOpenIdConnectAuthentication
working, all I would get is this in the console:
request: /admin/user/ warning : [Microsoft.AspNet.Authentication.OpenIdConnect.OpenIdConnectAuthentica tionMiddleware] OIDCH_0004: OpenIdConnectAuthenticationHandler: message.State is null or empty. request: /.well-known/openid-configuration warning : [Microsoft.AspNet.Authentication.OpenIdConnect.OpenIdConnectAuthentica tionMiddleware] OIDCH_0004: OpenIdConnectAuthenticationHandler: message.State is null or empty.
and an Exception after the time out
error : [Microsoft.AspNet.Server.WebListener.MessagePump] ProcessRequestAsync System.InvalidOperationException: IDX10803: Unable to create to obtain configura tion from: 'http://win2012.bludev.com/api/auth/.well-known/openid-configuration' . at Microsoft.IdentityModel.Logging.LogHelper.Throw(String message, Type excep tionType, EventLevel logLevel, Exception innerException) at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.d__24.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNot ification(Task task) ...
With this configuration UseOpenIdConnectAuthentication
app.UseOpenIdConnectAuthentication(options =>
{
options.AuthenticationScheme = OpenIdConnectAuthenticationDefaults.AuthenticationScheme;
options.Authority = "http://win2012.bludev.com/api/auth/";
options.Audience = "http://win2012.bludev.com/";
options.Resource = "http://win2012.bludev.com/";
options.AutomaticAuthentication = true;
options.TokenValidationParameters = new TokenValidationParameters()
{
RequireExpirationTime = true,
RequireSignedTokens = true,
RoleClaimType = ClaimTypes.Role,
NameClaimType = ClaimTypes.NameIdentifier,
ValidateActor = true,
ValidateAudience = false,
ValidateIssuer = true,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
ValidateSignature = true
};
});
So the real question is:
- How to get resource owner flow to work with claims
ValidateLifetime = true
orValidateAudience = true
would throw exception and result in a Http Code 500 response without a printed error.- How to turn authentication failures into a meaningful 400/403 code and a json or xml respones (depending on the client preference) to be displayed for the user? (JavaScript is the client in this case)?
app.UseOpenIdConnectAuthentication()
(which relies onOpenIdConnectAuthenticationMiddleware
) is only meant to support interactive flows (code/implicit/hybrid) and cannot be used with the resource owner password credentials grant type. Since you only want to validate access tokens, useapp.UseOAuthBearerAuthentication()
instead.See this SO answer for more information about the different OpenID Connect/OAuth2 middleware in ASP.NET 5: Configure the authorization server endpoint
The entire
OpenIdConnectServerMiddleware
you're using is based on claims.If you have trouble serializing specific claims, remember that all claims except
ClaimTypes.NameIdentifier
are not serialized by default in the identity and access tokens, since they are both readable by the client application and the user agent. To avoid leaking confidential data, you need to specify an explicitdestination
indicating where you want the claims to be serialized:That's how the OIDC client middleware (managed by MSFT) currently works by default, but it will be eventually fixed. You can see this GitHub ticket a workaround: https://github.com/aspnet/Security/issues/411