Безопасности долг - как реализовать OAuth2, которые токенов
я использую шаблон Web Api 2, который поставляется с Visual Studio 2013, имеет некоторое промежуточное программное обеспечение OWIN для аутентификации пользователей и тому подобное.
на OAuthAuthorizationServerOptions Я заметил, что сервер OAuth2 настроен для раздачи токенов, срок действия которых истекает через 14 дней
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/api/token"),
Provider = new ApplicationOAuthProvider(PublicClientId,UserManagerFactory) ,
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
AllowInsecureHttp = true
};
это не подходит для моего последнего проекта. Я хотел бы раздать недолговечные bearer_tokens, которые можно обновить с помощью refresh_token
Я сделал много гуглить и не могу найти что-нибудь полезное.
так вот как далеко мне удалось зайти. Теперь я достиг точки "WTF do I now".
я написал a RefreshTokenProvider что реализует IAuthenticationTokenProvider на RefreshTokenProvider собственности на OAuthAuthorizationServerOptions класс:
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens = new ConcurrentDictionary<string, AuthenticationTicket>();
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString();
_refreshTokens.TryAdd(guid, context.Ticket);
// hash??
context.SetToken(guid);
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket;
if (_refreshTokens.TryRemove(context.Token, out ticket))
{
context.SetTicket(ticket);
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
// Now in my Startup.Auth.cs
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/api/token"),
Provider = new ApplicationOAuthProvider(PublicClientId,UserManagerFactory) ,
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(2),
AllowInsecureHttp = true,
RefreshTokenProvider = new RefreshTokenProvider() // This is my test
};
так что теперь, когда кто-то просит bearer_token теперь я посылаю refresh_token, что здорово.
Итак, как я могу использовать этот refresh_token, чтобы получить новый bearer_token, предположительно, мне нужно отправить запрос на мой маркер конечной точки с некоторыми определенными заголовками HTTP?
просто думаю вслух, как я печатаю... Должен ли я обрабатывать срок действия refresh_token в моем SimpleRefreshTokenProvider? Как бы клиент получил новый refresh_token?
Я действительно мог бы сделать с некоторыми материалами для чтения / документацией, потому что я не хочу ошибаться и хотел бы следовать какому-то стандарту.
4 ответов:
просто осуществили мою службу долг с носителем (так называемый маркер доступа в следующий) и обновить ключи. Мое понимание этого заключается в том, что вы можете использовать разные потоки. Таким образом, это зависит от потока, который вы хотите использовать, как вы устанавливаете время истечения access_token и refresh_token.
я опишу два течетA и B в follwing (я предлагаю, что вы хотите иметь это поток B):
A) действия время access_token и refresh_token такое же, как и по умолчанию 1200 секунд или 20 минут. Этот поток требует, чтобы ваш клиент сначала отправил client_id и client_secret с данными входа, чтобы получить access_token, refresh_token и expiration_time. С помощью refresh_token теперь можно получить новый access_token в течение 20 минут (или независимо от того, что вы установили AccessTokenExpireTimeSpan в OAuthAuthorizationServerOptions). По той причине, что время истечения access_token и refresh_token то же самое, ваш клиент несет ответственность, чтобы получить новый access_token до истечения срока действия! Например, ваш клиент может отправить вызов refresh POST на конечную точку маркера с телом (Примечание: Вы должны использовать https в производстве)
grant_type=refresh_token&client_id=xxxxxx&refresh_token=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxчтобы получить новый токен, например, через 19 минут, чтобы предотвратить истечение срока действия токенов.
B) в этом потоке вы хотите иметь краткосрочный срок действия для вашего access_token и долгосрочный срок действия для вашего refresh_token. Предположим, что для целей тестирования вы установили access_token истекает через 10 секунд (
AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(10)) и refresh_token до 5 минут. Теперь дело доходит до интересной части, устанавливающей время истечения refresh_token: Вы делаете это в своей функции createAsync в классе SimpleRefreshTokenProvider следующим образом:var guid = Guid.NewGuid().ToString(); //copy properties and set the desired lifetime of refresh token var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary) { IssuedUtc = context.Ticket.Properties.IssuedUtc, ExpiresUtc = DateTime.UtcNow.AddMinutes(5) //SET DATETIME to 5 Minutes //ExpiresUtc = DateTime.UtcNow.AddMonths(3) }; /*CREATE A NEW TICKET WITH EXPIRATION TIME OF 5 MINUTES *INCLUDING THE VALUES OF THE CONTEXT TICKET: SO ALL WE *DO HERE IS TO ADD THE PROPERTIES IssuedUtc and *ExpiredUtc to the TICKET*/ var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties); //saving the new refreshTokenTicket to a local var of Type ConcurrentDictionary<string,AuthenticationTicket> // consider storing only the hash of the handle RefreshTokens.TryAdd(guid, refreshTokenTicket); context.SetToken(guid);теперь ваш клиент может отправить почтовый вызов с refresh_token на конечную точку маркера, когда
access_tokenистек. Часть тела может выглядеть так:grant_type=refresh_token&client_id=xxxxxx&refresh_token=xxxxxxxx-xxxx-xxxx-xxxx-xxважно то, что вы можете использовать этот код не только в своей функции CreateAsync, но и в своей функции Create. Поэтому вы должны рассмотреть возможность использования своей собственной функции (например, createtokeninternal) для приведенного выше кода. здесь вы можете найти реализации различных потоков, включая refresh_token flow(но без установки времени истечения маркер обновления)
вот один пример реализации IAuthenticationTokenProvider на github (С установкой времени истечения refresh_token)
мне жаль, что я не могу помочь с дополнительными материалами, чем спецификации OAuth и документация Microsoft API. Я бы разместил ссылки здесь, но моя репутация не позволяет мне размещать более 2 ссылок....
Я надеюсь, что это может помочь некоторым другим сэкономить время при попытке реализовать OAuth2.0 с временем истечения refresh_token, отличным от времени истечения access_token. Я не мог найти пример реализации в интернете (кроме одного из thinktecture, связанного выше), и мне потребовалось несколько часов расследования, пока он не сработал для меня.
новая информация: в моем случае у меня есть две разные возможности для получения токенов. Один, чтобы получить действительный маркер доступа. Там я должен отправить POST-вызов с телом строки в формате application/x-www-form-urlencoded со следующими данными
client_id=YOURCLIENTID&grant_type=password&username=YOURUSERNAME&password=YOURPASSWORDво-вторых, если access_token больше не действует мы можно попробовать refresh_token, отправив вызов POST с телом строки в формате
application/x-www-form-urlencodedСgrant_type=refresh_token&client_id=YOURCLIENTID&refresh_token=YOURREFRESHTOKENGUID
нужно реализовать RefreshTokenProvider. Сначала создайте класс для RefreshTokenProvider ie.
public class ApplicationRefreshTokenProvider : AuthenticationTokenProvider { public override void Create(AuthenticationTokenCreateContext context) { // Expiration time in seconds int expire = 5*60; context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddSeconds(expire)); context.SetToken(context.SerializeTicket()); } public override void Receive(AuthenticationTokenReceiveContext context) { context.DeserializeTicket(context.Token); } }затем добавьте экземпляр в OAuthOptions.
OAuthOptions = new OAuthAuthorizationServerOptions { TokenEndpointPath = new PathString("/authenticate"), Provider = new ApplicationOAuthProvider(), AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(expire), RefreshTokenProvider = new ApplicationRefreshTokenProvider() };
Я не думаю, что вы должны использовать массив, чтобы сохранить маркеры. Вам также не нужен идентификатор guid в качестве маркера.
вы можете легко использовать контекст.SerializeTicket ().
см. мой код ниже.
public class RefreshTokenProvider : IAuthenticationTokenProvider { public async Task CreateAsync(AuthenticationTokenCreateContext context) { Create(context); } public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { Receive(context); } public void Create(AuthenticationTokenCreateContext context) { object inputs; context.OwinContext.Environment.TryGetValue("Microsoft.Owin.Form#collection", out inputs); var grantType = ((FormCollection)inputs)?.GetValues("grant_type"); var grant = grantType.FirstOrDefault(); if (grant == null || grant.Equals("refresh_token")) return; context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(Constants.RefreshTokenExpiryInDays); context.SetToken(context.SerializeTicket()); } public void Receive(AuthenticationTokenReceiveContext context) { context.DeserializeTicket(context.Token); if (context.Ticket == null) { context.Response.StatusCode = 400; context.Response.ContentType = "application/json"; context.Response.ReasonPhrase = "invalid token"; return; } if (context.Ticket.Properties.ExpiresUtc <= DateTime.UtcNow) { context.Response.StatusCode = 401; context.Response.ContentType = "application/json"; context.Response.ReasonPhrase = "unauthorized"; return; } context.Ticket.Properties.ExpiresUtc = DateTime.UtcNow.AddDays(Constants.RefreshTokenExpiryInDays); context.SetTicket(context.Ticket); } }
Фредди помог мне много, чтобы получить эту работу. Для полноты картины вот как можно реализовать хеширование знак:
private string ComputeHash(Guid input) { byte[] source = input.ToByteArray(); var encoder = new SHA256Managed(); byte[] encoded = encoder.ComputeHash(source); return Convert.ToBase64String(encoded); }на
CreateAsync:var guid = Guid.NewGuid(); ... _refreshTokens.TryAdd(ComputeHash(guid), refreshTokenTicket); context.SetToken(guid.ToString());
ReceiveAsync:public async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { Guid token; if (Guid.TryParse(context.Token, out token)) { AuthenticationTicket ticket; if (_refreshTokens.TryRemove(ComputeHash(token), out ticket)) { context.SetTicket(ticket); } } }
Comments