Hola a todos, hace un tiempo escribí un post sobre como utilizar autenticación por bearer token en Web API, así que en este post quiero mostrar cómo es posible refrecar el token una vez este se ha vencido.

EL tiempo de vida de un token debería ser corto, para evitar que si el token es comprometido, este pueda ser utilizado por mucho tiempo, cuando el token se vence, es necesario obtener uno nuevo, y esto lo podemos hacer enviando nuevamente el usuario y clave o utilizando el refresh token el cual puede obtenerse cuando obtenemos el token de autenticación.

Si quieren leer un poco más acerca de OAuth les recomiendo el post de Eduard Tomás "Algunas recomendaciones sobre OAuth"

El código implementando toma como base el ejemplo del post anterior. Iniciemos, por defecto, la plantilla de ASPNET Identity no viene con el refresh_token implementado, así que lo primero es crear una clase que para el ejemplo vamos a llamar ApplicationRefreshTokenProvider, dicha clase hereda de AuthenticationTokenProvider y allí vamos a sobre-escribir los métodos Create y Receive:


public class ApplicationRefreshTokenProvider : AuthenticationTokenProvider
{
    public ApplicationRefreshTokenProvider(){ }

    public override void Create(AuthenticationTokenCreateContext context)
    {
        context.Ticket.Properties.ExpiresUtc = new DateTimeOffset(DateTime.Now.AddMinutes(20));
        context.SetToken(context.SerializeTicket());
    }

    public override void Receive(AuthenticationTokenReceiveContext context)
    {
        context.DeserializeTicket(context.Token);
    }
}

El siguiente paso en el archivo Startup.Auth.cs, en el método ConfigureAuth en las opciones de configuración de OAuth adicionar el parámetro RefreshTokenProvider:


OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromSeconds(15),
    AllowInsecureHttp = true,
    RefreshTokenProvider = new ApplicationRefreshTokenProvider()
};

Bueno, y ahora, como implementamos la funcionalidad para refrescar el token? Lo primero a tener presente, es que ahora, cuando se obtiene el token (cuando se envia usuario y clave), se obtiene un parámetro adicional que es refresh_token el cual será el usado para refrescar el token una vez este sea vencido:

alt

Una vez el token este vencido, se hará una petición al mismo endpoing /Token enviando el refresh_token y un parámetro adicional grant_type con el valor refresh_token.

Por lo tanto, re-escribiendo el código JavaScript implementado, para que ahora sea posible usar el refresh_token cuando el token este vencido tenemos lo siguiente:


var token = null;
var refreshToken = null;
$("#btnGetData").on("click", function () {
    if (token !== null) {
        getData(token).then(function (response) {
            console.log(response)
        }).fail(function (response) {
            if (response.status === 401)
            {
                alert('No autorizado');
                updateToken(refreshToken).then(function (response)
                {
                    console.log('Token Actualizado');
                    token = response.access_token;
                    refreshToken = response.refresh_token;
                    getData(token).then(function (response) {
                        console.log(response)
                    }).fail(function (response) {
                        console.log(response);
                    });
                });
            }
        });
    }
    else {
        getToken().then(function (response) {
            token = response.access_token;
            refreshToken = response.refresh_token;
            getData(token).then(function (response) {
                console.log(response)
            }).fail(function (response) {
                console.log(response);
            });
        });
    }
});

function getToken() {
    var data = { username: "julito@gmail.com", password: "Pass1234567*", grant_type: "password" }
    return $.ajax({
        url: "/Token",
        method: "post",
        data: data,
        contentType: "application/json"
    });
}

function updateToken(refreshToken) {
    var data = { refresh_token: refreshToken, grant_type: "refresh_token"}
    return $.ajax({
        url: "/Token",
        method: "post",
        data: data,
        contentType: "application/json"
    });
}

function getData(token) {
    return $.ajax({
        url: "/api/values",
        method: "get",
        headers: { "Authorization": "Bearer " + token },
        contentType: "application/json"
    });
}

Y eso es todo, espero es post les sea interesante.

Saludos!