Безопасности долг - как реализовать 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?



Я действительно мог бы сделать с некоторыми материалами для чтения / документацией, потому что я не хочу ошибаться и хотел бы следовать какому-то стандарту.

601   4  

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

    Ничего не найдено.