在 ASP.NET 5 中对 OpenIdConnect.Server 使用声明

Using Claims with OpenIdConnect.Server in ASP.NET 5

在过去的 7 天里,我尝试使用 OpenIdConnect.Server 和资源所有者流程来设置 ASP.NET 5 WebApi。

我或多或少成功地生成了令牌并访问了 [Authorize] 受保护的操作。

但是,当我尝试访问 this.User.Identity.Claims 时,它是空的。我现在正在使用 ASP.NET 5,beta6(无法升级到最新的 beta7 并等待它的正式发布)

在 Startup.cs 我得到了以下信息:

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();
    }
}

我使用 app.UseOAuthBearerAuthentication 因为我无法让 app.UseOpenIdConnectAuthentication 工作,我只能在控制台中得到这个:

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.

超时后出现异常

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) ...

有了这个配置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
    };

});

所以真正的问题是:

  1. 如何让资源所有者流处理声明
  2. ValidateLifetime = trueValidateAudience = true 会抛出异常并导致 Http 代码 500 响应,而不会打印错误。
  3. 如何将身份验证失败转换为有意义的 400/403 代码并向用户显示 json 或 xml 响应(取决于客户端首选项)? (JavaScript 在这种情况下是客户端)?

app.UseOpenIdConnectAuthentication()(依赖于 OpenIdConnectAuthenticationMiddleware)仅用于支持交互式流程 (code/implicit/hybrid),不能与资源所有者密码凭据授权类型一起使用。由于您只想验证访问令牌,请改用 app.UseOAuthBearerAuthentication()

有关 ASP.NET 5 中不同 OpenID Connect/OAuth2 中间件的更多信息,请参阅此 SO 答案:

How to get resource owner flow to work with claims

您使用的整个 OpenIdConnectServerMiddleware 都是基于声明。

如果您在序列化特定声明时遇到问题,请记住除了 ClaimTypes.NameIdentifier 之外的所有声明默认情况下都不会在身份和访问令牌中序列化,因为客户端应用程序和用户代理都可以读取它们。为避免泄露机密数据,您需要明确指定 destination 以指示您希望将声明序列化的位置:

// This claim will be only serialized in the access token.
identity.AddClaim(ClaimTypes.Name, username, OpenIdConnectConstants.Destinations.AccessToken);

// This claim will be serialized in both the identity and the access tokens.
identity.AddClaim(ClaimTypes.Surname, "Doe",
    OpenIdConnectConstants.Destinations.AccessToken,
    OpenIdConnectConstants.Destinations.IdentityToken););

ValidateLifetime = true or ValidateAudience = 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)?

这就是 OIDC 客户端中间件(由 MSFT 管理)当前的默认工作方式,但最终会得到修复。您可以查看此 GitHub 工单解决方法:https://github.com/aspnet/Security/issues/411