如何 Consume/Validate AspNet.Security.OpenIdConnect.Server 发行的代币 (RC1)?

How to Consume/Validate Token Issued by AspNet.Security.OpenIdConnect.Server (RC1)?

我已经按照我从帖子中了解到的关于如何实施 AspNet.Security.OpenIdConnect.Server.

的所有内容进行了操作

Pinpoint,你听到了吗? ;)

我已经设法将代币发行和代币消费分开。我不会显示 "auth server side",因为我认为该部分已全部设置好,但我将展示如何在我的自定义 AuthorizationProvider 中构建身份验证票证:

public sealed class AuthorizationProvider : OpenIdConnectServerProvider
{
    // The other overrides are not show. I've relaxed them to always validate.

    public override async Task GrantResourceOwnerCredentials(GrantResourceOwnerCredentialsContext context)
    {
          // I'm using Microsoft.AspNet.Identity to validate user/password. 
          // So, let's say that I already have MyUser user from
          //UserManager<MyUser> UM:

            var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
            //identity.AddClaims(await UM.GetClaimsAsync(user));
            identity.AddClaim(ClaimTypes.Name, user.UserName);

            (await UM.GetRolesAsync(user)).ToList().ForEach(role => {
                identity.AddClaim(ClaimTypes.Role, role);
            });

            var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity),
                                                  new AuthenticationProperties(),
                                                  context.Options.AuthenticationScheme);
            // Some new stuff, per my latest research
            ticket.SetResources(new[] { "my_resource_server" });
            ticket.SetAudiences(new[] { "my_resource_server" });
            ticket.SetScopes(new[] { "defaultscope" });

            context.Validated(ticket);
        }
    }

并在授权服务器上启动:

using System;
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Extensions.DependencyInjection;

using MyAuthServer.Providers;

namespace My.AuthServer
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication();
            services.AddCaching();
            services.AddMvc();

            string connectionString = "there is actually one";

            services.AddEntityFramework()
                    .AddSqlServer()
                    .AddDbContext<MyDbContext>(options => {
                        options.UseSqlServer(connectionString).UseRowNumberForPaging();
                    });

            services.AddIdentity<User, Role>()
                    .AddEntityFrameworkStores<MyDbContext>().AddDefaultTokenProviders();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseIISPlatformHandler();

            app.UseOpenIdConnectServer(options => {
                options.ApplicationCanDisplayErrors = true;
                options.AllowInsecureHttp = true;                
                options.Provider = new AuthorizationProvider();
                options.TokenEndpointPath = "/token";
                options.AccessTokenLifetime = new TimeSpan(1, 0, 0, 0);
                options.Issuer = new Uri("http://localhost:60556/");
            });

            app.UseMvc();
            app.UseWelcomePage();
        }

        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
}

果然,当我收到此 HTTP 请求时,我确实获得了一个访问令牌,但我不确定该访问令牌是否具有资源服务器期望的所有数据。

POST /token HTTP/1.1
Host: localhost:60556
Content-Type: application/x-www-form-urlencoded

username=admin&password=pw&grant_type=password

现在,在资源服务器端,我正在使用 JWT Bearer Authentication。在启动时,我有:

using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Hosting;
using Microsoft.Data.Entity;
using Microsoft.Extensions.DependencyInjection;

namespace MyResourceServer
{
    public class Startup
    {            
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();

            string connectionString = "there is actually one";

            services.AddEntityFramework()
                    .AddSqlServer()
                    .AddDbContext<MyDbContext>(options => {
                        options.UseSqlServer(connectionString).UseRowNumberForPaging();                        
                    });

            services.AddIdentity<User, Role>()
                    .AddEntityFrameworkStores<MyDbContext>().AddDefaultTokenProviders();
        }

        public void Configure(IApplicationBuilder app)
        {
            app.UseIISPlatformHandler();
            app.UseMvc();
            app.UseWelcomePage();

            app.UseJwtBearerAuthentication(options => {
                options.Audience = "my_resource_server";  
                options.Authority = "http://localhost:60556/"; 
                options.AutomaticAuthenticate = true;
                options.RequireHttpsMetadata = false;                
            });
        }

        public static void Main(string[] args) => WebApplication.Run<Startup>(args);
    }
}

当我向资源服务器发出此 HTTP 请求时,我收到 401 Unauthorized:

GET /api/user/myroles HTTP/1.1
Host: localhost:64539
Authorization: Bearer eyJhbGciOiJS...
Content-Type: application/json;charset=utf-8

有路由到 /api/user/myroles 的控制器用没有参数的普通 [Authorize] 修饰。

我觉得我在身份验证服务器和资源服务器中都缺少一些东西,但不知道它们是什么。

"how to validate token issued by AspNet.Security.OpenIdConnect.Server" 提出的其他问题没有答案。我将不胜感激。

此外,我注意到示例提供程序中注释掉了 OAuth Introspection,并且在某处读到 Jwt 不会很快得到支持。我找不到给我 OAuth Instrospection 的依赖项。


UPDATE 我已经包含了我的两个 startup.cs,来自每个身份验证和资源服务器。会不会有什么错误会导致资源服务器总是 return 每个请求都是 401?

我在整个努力过程中没有真正触及的一件事是签名。它似乎在 auth 服务器上为 JWT 生成签名,但资源服务器(我猜)不知道签名密钥。回到 OWIN 项目,我必须创建一个机器密钥并放在两台服务器上。

编辑: 您的中间件实例的顺序不正确:

app.UseIISPlatformHandler();

app.UseJwtBearerAuthentication(options => {
    options.Audience = "my_resource_server";  
    options.Authority = "http://localhost:60556/"; 
    options.AutomaticAuthenticate = true;
    options.RequireHttpsMetadata = false;
});

app.UseMvc();
app.UseWelcomePage();

Sure enough, when I have this HTTP request, I do get an access token, but I'm not sure if that access token has all the data that the resource server expects.

您的授权服务器和资源服务器配置看起来不错,但是您在添加声明时没有设置“目的地”(不要忘记,为了避免泄露机密数据,AspNet.Security.OpenIdConnect.Server 拒绝序列化未明确指定目的地的声明):

var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme);
identity.AddClaim(ClaimTypes.Name, user.UserName, destination: "id_token token");

(await UM.GetRolesAsync(user)).ToList().ForEach(role => {
    identity.AddClaim(ClaimTypes.Role, role, destination: "id_token token");
});

Also, I've noticed that there is OAuth Introspection commented out in the sample provider, and have read somewhere that Jwt is not going to be supported soon. I can't find the dependency that gives me the OAuth Instrospection.

从下一个测试版(ASOS beta5,写这个答案时 NuGet.org 还没有)开始,我们将停止使用 JWT 作为访问令牌的默认格式,但当然,JWT 仍然是支持OTB。

令牌现在默认是不透明的,您必须使用新的 validation middleware (inspired from Katana's OAuthBearerAuthenticationMiddleware) or the new standard introspection middleware, that implements the OAuth2 introspection RFC:

app.UseOAuthValidation();

// Alternatively, you can also use the introspection middleware.
// Using it is recommended if your resource server is in a
// different application/separated from the authorization server.
// 
// app.UseOAuthIntrospection(options => {
//     options.AutomaticAuthenticate = true;
//     options.AutomaticChallenge = true;
//     options.Authority = "http://localhost:54540/";
//     options.Audience = "resource_server";
//     options.ClientId = "resource_server";
//     options.ClientSecret = "875sqd4s5d748z78z7ds1ff8zz8814ff88ed8ea4z4zzd";
// });

您可以在此处找到有关这两个中间件的更多信息:https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/issues/185