提问者:小点点

配置授权服务器终结点


我们如何使用用户名和密码流使用ASP.NET5的不记名令牌?对于我们的场景,我们希望让用户使用AJAX调用注册和登录,而无需使用外部登录。

为此,我们需要一个授权服务器endpoint。在以前版本的ASP.NET中,我们会执行以下操作,然后登录< code > our domain . com/Token URL。

// Configure the application for OAuth based flow
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
};

然而,在当前版本的ASP.NET中,上述内容不起作用。我们一直在试图找出新的方法。例如,GitHub上的aspnet/identity示例配置了脸书、Google和Twitter身份验证,但似乎没有配置非外部OAuth授权服务器endpoint,除非< code > AddDefaultTokenProviders()这样做,在这种情况下,我们想知道提供者的URL是什么。

通过阅读这里的源代码,我们了解到可以通过调用< code>IAppBuilder将“承载认证中间件”添加到HTTP管道中。UseOAuthBearerAuthentication 在我们的< code>Startup类中。这是一个好的开始,尽管我们仍然不确定如何设置它的令牌endpoint。这不起作用:

public void Configure(IApplicationBuilder app)
{  
    app.UseOAuthBearerAuthentication(options =>
    {
        options.MetadataAddress = "meta";
    });

    // if this isn't here, we just get a 404
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello World.");
    });
}

在去ourdomain.com/meta时,我们只收到我们的hello world页面。

进一步的研究表明,我们还可以使用IAppBuilder.UseOAuthAuthentication扩展方法,并且它采用OAuthAuthentication选项参数。该参数具有TokenEndpoint属性。所以,尽管我们不确定自己在做什么,但我们尝试了一下,当然没有成功。

public void Configure(IApplicationBuilder app)
{
    app.UseOAuthAuthentication("What is this?", options =>
    {
        options.TokenEndpoint = "/token";
        options.AuthorizationEndpoint = "/oauth";
        options.ClientId = "What is this?";
        options.ClientSecret = "What is this?";
        options.SignInScheme = "What is this?";
        options.AutomaticAuthentication = true;
    });

    // if this isn't here, we just get a 404
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello World.");
    });
}

换句话说,在进入ourdomain.com/token时,没有错误,只是再次出现了我们的hello world页面。


共2个答案

匿名用户

编辑(2021年1月28日):AspNet.Security。作为3.0更新的一部分,OpenIdConnect. Server已合并到OpenIdduce中。要开始使用OpenIdduce,请访问documentation.openiddict.com。

好吧,让我们回顾一下OWIN/Katana 3提供的不同OAuth2中间件(以及它们各自的IAppBuilder扩展),以及将移植到ASP.NET Core的中间件:

    < li>应用程序。UseOAuthBearerAuthentication /< code > OAuthBearerAuthenticationMiddleware :它的名字不是很明显,但是它负责验证由OAuth2服务器中间件发布的访问令牌(现在仍然是,因为它已经被移植到ASP.NET核心)。它基本上是cookies中间件的令牌对等物,用于保护您的API。在ASP.NET核心中,它增加了可选的OpenID Connect功能(它现在能够从发布令牌的OpenID Connect服务器中自动检索签名证书)。

注意:从ASP.NETCore beta8开始,它现在被命名为app. UseJwtBearerAuthentication/JwtBearerAuthenticationMiddleware

>

  • app. UseOAuthorizationServer/OAuthAuthorizationServerMiddleware:顾名思义,OAuthorizationServerMiddleware是一个OAuth2授权服务器中间件,用于创建和颁发访问令牌。此中间件不会移植到ASP.NETCore:ASP.NETCore中的OAuth Authorination Service。

    .app。UseOAuthBearerTokens:这个扩展并不真正对应于中间件,只是应用程序的包装器。使用OAuthAuthorizationServer and app.使用OAuthBearerAuthentication。它是 ASP.NET 标识包的一部分,只是在单个调用中配置 OAuth2 授权服务器和用于验证访问令牌的 OAuth2 持有者中间件的便捷方法。它不会移植到 ASP.NET 核心。

    ASP.NET Core将提供一个全新的中间件(我很自豪地说我设计了它):

    • app. UseOAuthentication/OAuthenticationMiddleware:这个新的中间件是一个通用的OAuth2交互式客户端,其行为与app.UseFacebookAuthenticationapp.UseGoogleAuthentication完全相同,但它几乎支持任何标准的OAuth2提供程序,包括您的。Google、Facebook和Microsoft提供程序都已更新为继承自这个新的基本中间件。

    因此,您实际上正在寻找的中间件是OAuth2授权服务器中间件,又名OAuthAuthorizationServerMiddleware

    虽然它被社区的大部分人认为是一个必要的组件,但它不会被移植到ASP.NET核心。

    幸运的是,已经有了一个直接的替代品:AspNet。安全。OpenID connect . server (https://github.com/aspnet-contrib/AspNet。Security.OpenIdConnect.Server)

    这个中间件是OAuth2授权服务器中间件的一个高级分支,随Katana 3一起提供,但目标是OpenID Connect(它本身基于OAuth2)。它使用相同的低级方法提供细粒度控制(通过各种通知),并允许您使用自己的框架(南希,ASP.NET核心MVC)来服务您的授权页面,就像您可以使用OAuth2服务器中间件一样。配置它很容易:

    ASP.NET 酷睿1.x:

    // Add a new middleware validating access tokens issued by the server.
    app.UseOAuthValidation();
    
    // Add a new middleware issuing tokens.
    app.UseOpenIdConnectServer(options =>
    {
        options.TokenEndpointPath = "/connect/token";
    
        // Create your own `OpenIdConnectServerProvider` and override
        // ValidateTokenRequest/HandleTokenRequest to support the resource
        // owner password flow exactly like you did with the OAuth2 middleware.
        options.Provider = new AuthorizationProvider();
    });
    

    ASP.NET核心2.x:

    // Add a new middleware validating access tokens issued by the server.
    services.AddAuthentication()
        .AddOAuthValidation()
    
        // Add a new middleware issuing tokens.
        .AddOpenIdConnectServer(options =>
        {
            options.TokenEndpointPath = "/connect/token";
    
            // Create your own `OpenIdConnectServerProvider` and override
            // ValidateTokenRequest/HandleTokenRequest to support the resource
            // owner password flow exactly like you did with the OAuth2 middleware.
            options.Provider = new AuthorizationProvider();
        });
    

    有一个OWIN/Katana3版本,以及一个同时支持.NET Desktop和.NET Core的 ASP.NET Core版本。

    不要犹豫,尝试给邮递员样本,了解它是如何工作的。我建议阅读相关的博客文章,其中解释了如何实现资源所有者密码流。

    如果你还需要帮助,请随时打电话给我。祝你好运

  • 匿名用户

    在@Pinpoint的帮助下,我们已经拼凑出了答案的雏形。它展示了组件如何在没有完整解决方案的情况下连接在一起。

    通过我们基本的项目设置,我们能够在 Fiddler 中提出以下请求和响应。

    POST http://localhost:50000/connect/token HTTP/1.1
    User-Agent: Fiddler
    Host: localhost:50000
    Content-Length: 61
    Content-Type: application/x-www-form-urlencoded
    
    grant_type=password&username=my_username&password=my_password
    
    HTTP/1.1 200 OK
    Cache-Control: no-cache
    Pragma: no-cache
    Content-Length: 1687
    Content-Type: application/json;charset=UTF-8
    Expires: -1
    X-Powered-By: ASP.NET
    Date: Tue, 16 Jun 2015 01:24:42 GMT
    
    {
      "access_token" : "eyJ0eXAiOi ... 5UVACg",
      "expires_in" : 3600,
      "token_type" : "bearer"
    }
    

    响应提供了一个持有者令牌,我们可以使用该令牌来访问应用程序的安全部分。

    这是我们在Visual Studio中项目的结构。我们必须设置它的Properties

    ResourceOwnerPasswordFlow
        Providers
            AuthorizationProvider.cs
        project.json
        Startup.cs
    

    为了可读性,我将< code>Startup类分成了两部分。

    对于最基本的,我们只需要AddAuthentication()。

    public partial class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication();
        }
    }
    
    public partial class Startup
    {
        public void Configure(IApplicationBuilder app)
        {
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
    
            // Add a new middleware validating access tokens issued by the server.
            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                Audience = "resource_server_1",
                Authority = "http://localhost:50000/",
                RequireHttpsMetadata = false
            });
    
            // Add a new middleware issuing tokens.
            app.UseOpenIdConnectServer(options =>
            {
                // Disable the HTTPS requirement.
                options.AllowInsecureHttp = true;
    
                // Enable the token endpoint.
                options.TokenEndpointPath = "/connect/token";
    
                options.Provider = new AuthorizationProvider();
    
                // Force the OpenID Connect server middleware to use JWT
                // instead of the default opaque/encrypted format.
                options.AccessTokenHandler = new JwtSecurityTokenHandler
                {
                    InboundClaimTypeMap = new Dictionary<string, string>(),
                    OutboundClaimTypeMap = new Dictionary<string, string>()
                };
    
                // Register an ephemeral signing key, used to protect the JWT tokens.
                // On production, you'd likely prefer using a signing certificate.
                options.SigningCredentials.AddEphemeralKey();
            });
    
            app.UseMvc();
    
            app.Run(async context =>
            {
                await context.Response.WriteAsync("Hello World!");
            });
        }
    }
    
    public sealed class AuthorizationProvider : OpenIdConnectServerProvider
    {
        public override Task ValidateTokenRequest(ValidateTokenRequestContext context)
        {
            // Reject the token requests that don't use
            // grant_type=password or grant_type=refresh_token.
            if (!context.Request.IsPasswordGrantType() &&
                !context.Request.IsRefreshTokenGrantType())
            {
                context.Reject(
                    error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
                    description: "Only grant_type=password and refresh_token " +
                                 "requests are accepted by this server.");
    
                return Task.FromResult(0);
            }
    
            // Since there's only one application and since it's a public client
            // (i.e a client that cannot keep its credentials private), call Skip()
            // to inform the server that the request should be accepted without 
            // enforcing client authentication.
            context.Skip();
    
            return Task.FromResult(0);
        }
    
        public override Task HandleTokenRequest(HandleTokenRequestContext context)
        {
            // Only handle grant_type=password token requests and let the
            // OpenID Connect server middleware handle the other grant types.
            if (context.Request.IsPasswordGrantType())
            {
                // Validate the credentials here (e.g using ASP.NET Core Identity).
                // You can call Reject() with an error code/description to reject
                // the request and return a message to the caller.
    
                var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
                identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "[unique identifier]");
    
                // By default, claims are not serialized in the access and identity tokens.
                // Use the overload taking a "destinations" parameter to make sure 
                // your claims are correctly serialized in the appropriate tokens.
                identity.AddClaim("urn:customclaim", "value",
                    OpenIdConnectConstants.Destinations.AccessToken,
                    OpenIdConnectConstants.Destinations.IdentityToken);
    
                var ticket = new AuthenticationTicket(
                    new ClaimsPrincipal(identity),
                    new AuthenticationProperties(),
                    context.Options.AuthenticationScheme);
    
                // Call SetResources with the list of resource servers
                // the access token should be issued for.
                ticket.SetResources("resource_server_1");
    
                // Call SetScopes with the list of scopes you want to grant
                // (specify offline_access to issue a refresh token).
                ticket.SetScopes("profile", "offline_access");
    
                context.Validate(ticket);
            }
    
            return Task.FromResult(0);
        }
    }
    
    {
      "dependencies": {
        "AspNet.Security.OpenIdConnect.Server": "1.0.0",
        "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",
        "Microsoft.AspNetCore.Mvc": "1.0.0",
      }
    
      // other code omitted
    }