Подтвердить что ты не робот

Настройка конечной точки сервера авторизации

Вопрос

Как мы используем токен-носитель с ASP.NET 5 с использованием потока имени пользователя и пароля? Для нашего сценария мы хотим, чтобы пользователь регистрировался и входил в систему с помощью AJAX-вызовов без необходимости использования внешнего входа.

Для этого нам нужно иметь конечную точку сервера авторизации. В предыдущих версиях ASP.NET мы выполнили следующие действия, а затем запустили бы с адресом ourdomain.com/Token.

// 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, однако, это не работает. Мы пытались выяснить новый подход. пример aspnet/identity на GitHub, например, настраивает аутентификацию Facebook, Google и Twitter, но не отображает конечную точку сервера внешней авторизации OAuth, если только это не означает, что AddDefaultTokenProviders(), и в этом случае нам интересно, каким будет URL-адрес провайдера.

Исследование

Мы узнали из чтения источника здесь, что мы можем добавить "промежуточное ПО аутентификации на предъявителя" в конвейер HTTP, вызвав IAppBuilder.UseOAuthBearerAuthentication в нашем классе Startup, Это хорошее начало, хотя мы все еще не уверены в том, как установить конечную точку маркера. Это не помогло:

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 мы просто получаем нашу всемирную страницу приветствия.

Дальнейшие исследования показали, что мы также можем использовать метод расширения IAppBuilder.UseOAuthAuthentication и что он принимает параметр OAuthAuthenticationOptions. Этот параметр имеет свойство 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 нет ошибки, мы снова вернем нашу всемирную страницу приветствия.

4b9b3361

Ответ 1

Хорошо, давайте вспомним другое промежуточное ПО OAuth2 (и их соответствующие расширения IAppBuilder), которые были предложены OWIN/Katana 3, а те, которые будут перенесены на ASP.NET Core

  • app.UseOAuthBearerAuthentication/OAuthBearerAuthenticationMiddleware: его имя не было явно очевидным, но оно было (и по-прежнему, поскольку оно было перенесено в ASP.NET Core), ответственное за проверку токенов доступа, выпущенных промежуточным программным обеспечением сервера OAuth2. Это в основном токен-копия промежуточного программного обеспечения cookie и используется для защиты ваших API-интерфейсов. В ASP.NET Core он был дополнен дополнительными функциями OpenID Connect (теперь он может автоматически получать сертификат подписи с сервера OpenID Connect, который выдал токены).

Примечание. Начиная с ASP.NET Core beta8, теперь он называется app.UseJwtBearerAuthentication/JwtBearerAuthenticationMiddleware.

  • app.UseOAuthAuthorizationServer/OAuthAuthorizationServerMiddleware: как следует из названия, OAuthAuthorizationServerMiddleware было промежуточным программным обеспечением сервера авторизации OAuth2 и использовалось для создания и выпуска токенов доступа. Это промежуточное программное обеспечение не будет перенесено в ASP.NET Core: Служба авторизации OAuth в ядре ASP.NET.

  • app.UseOAuthBearerTokens: это расширение действительно не соответствовало промежуточному программному обеспечению и было просто оберткой вокруг app.UseOAuthAuthorizationServer и app.UseOAuthBearerAuthentication. Он был частью пакета ASP.NET Identity и был просто удобным способом настройки сервера авторизации OAuth2 и промежуточного программного обеспечения OAuth2, используемого для проверки токенов доступа в одном вызове. Он не будет перенесен в ядро ​​ASP.NET.

ASP.NET Core предложит совершенно новое промежуточное ПО (и я с гордостью могу сказать, что я его разработал):

  • app.UseOAuthAuthentication/OAuthAuthenticationMiddleware: это новое промежуточное программное обеспечение представляет собой общий интерактивный клиент OAuth2, который ведет себя точно так же, как app.UseFacebookAuthentication или app.UseGoogleAuthentication, но поддерживает практически любой стандартный поставщик OAuth2, включая ваш. Поставщики Google, Facebook и Microsoft все были обновлены, чтобы наследовать это новое базовое промежуточное программное обеспечение.

Итак, промежуточное программное обеспечение, которое вы действительно ищете, - это промежуточное программное обеспечение сервера авторизации OAuth2, aka OAuthAuthorizationServerMiddleware.

Хотя он считается важным компонентом значительной части сообщества, он не будет перенесен в ASP.NET Core.

К счастью, уже есть прямая замена: AspNet.Security.OpenIdConnect.Server (https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server)

Это промежуточное программное обеспечение представляет собой передовую вилку промежуточного программного обеспечения сервера авторизации OAuth2, которое поставляется с Katana 3, но оно предназначено для OpenID Connect (которое само основано на OAuth2). Он использует тот же подход на низком уровне, который предлагает мелкомасштабный контроль (через различные уведомления) и позволяет использовать свою собственную инфраструктуру (Nancy, ASP.NET Core MVC) для обслуживания ваших страниц авторизации, как вы могли, с помощью промежуточного ПО сервера OAuth2, Конфигурирование легко:

ASP.NET Core 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 Core 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/Katana 3 и версия ASP.NET Core, которая поддерживает как .NET Desktop, так и .NET Core.

Не стесняйтесь давать образец Postman, чтобы понять, как это работает. Я бы рекомендовал прочитать связанное сообщение в блоге, в котором объясняется, как вы можете реализовать поток паролей владельца ресурса.

Не стесняйтесь пинговать меня, если вам все еще нужна помощь. Удачи!

Ответ 2

С помощью справки @Pinpoint мы связали рудименты ответа. Он показывает, как компоненты соединяются вместе, не будучи полным решением.

Демоверсия Fiddler

С нашей рудиментарной настройкой проекта мы смогли сделать следующий запрос и ответ в 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 > Debug > Port в 50000, чтобы он работал в качестве сервера идентификации, который мы настроили. Вот соответствующие файлы:

ResourceOwnerPasswordFlow
    Providers
        AuthorizationProvider.cs
    project.json
    Startup.cs

Startup.cs

Для удобства чтения я разделил класс Startup на две части.

Startup.ConfigureServices

Для самых основ нам нужно только AddAuthentication().

public partial class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication();
    }
}

Startup.Configure

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!");
        });
    }
}

AuthorizationProvider.cs

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 only one application and since it 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);
    }
}

project.json

{
  "dependencies": {
    "AspNet.Security.OpenIdConnect.Server": "1.0.0",
    "Microsoft.AspNetCore.Authentication.JwtBearer": "1.0.0",
    "Microsoft.AspNetCore.Mvc": "1.0.0",
  }

  // other code omitted
}