Настройте конечную точку сервера авторизации
Вопрос
Как мы используем токен на предъявителя в ASP.NET 5, используя поток имени пользователя и пароля? В нашем сценарии мы хотим позволить пользователю зарегистрироваться и войти в систему с помощью вызовов AJAX без необходимости использования внешнего входа в систему.
Для этого нам нужна конечная точка сервера авторизации. В предыдущих версиях ASP.NET мы делали следующее, а затем входили в ourdomain.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 вышеприведенное не работает. Мы пытались выяснить новый подход. Например, 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
, нет ошибки, просто снова наша страница "Привет, мир!"
2 ответа
Хорошо, давайте вспомним другое промежуточное ПО 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 Core.app.UseOAuthBearerTokens
: это расширение на самом деле не соответствовало промежуточному программному обеспечению и было просто оберткой вокругapp.UseOAuthAuthorizationServer
а такжеapp.UseOAuthBearerAuthentication
, Он был частью пакета удостоверений ASP.NET и был просто удобным способом настройки как сервера авторизации OAuth2, так и промежуточного программного обеспечения носителя OAuth2, используемого для проверки токенов доступа за один вызов. Он не будет перенесен в ASP.NET Core.
ASP.NET Core предложит совершенно новое промежуточное ПО (и я с гордостью могу сказать, что разработал его):
app.UseOAuthAuthentication
/OAuthAuthenticationMiddleware
: это новое промежуточное ПО - это универсальный интерактивный клиент OAuth2, который ведет себя точно так жеapp.UseFacebookAuthentication
или жеapp.UseGoogleAuthentication
но это поддерживает практически любого стандартного поставщика OAuth2, включая вашего. Google, Facebook и Microsoft провайдеры были обновлены, чтобы наследовать от этого нового базового промежуточного программного обеспечения.
Итак, промежуточное ПО, которое вы на самом деле ищете, это промежуточное ПО сервера авторизации OAuth2, иначе 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.
Не стесняйтесь дать образцу Почтальона попытку понять, как он работает. Я бы рекомендовал прочитать соответствующий пост в блоге, в котором объясняется, как реализовать поток паролей владельца ресурса.
Не стесняйтесь пинговать меня, если вам все еще нужна помощь. Удачи!
С помощью @Pinpoint мы соединили зачатки ответа. Это показывает, как компоненты соединяются вместе, не будучи полным решением.
Fiddler Demo
С нашей элементарной настройкой проекта мы смогли сделать следующий запрос и ответ в 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'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);
}
}
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
}