Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Refresh tokens in oauth2 should not be replaced when getting a new access token

Is the statement in the title correct? I'm basing the question on the work done by Taiseer Joudeh (thanks for your work on this subject, btw) at http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/.

If I understand the behavior of refresh tokens correctly, when an access token expires, we should call the token endpoint of our auth server with something like

'grant_type=refresh_token&refresh_token=' + token

and we will get back a new access token. That request will have a refresh token as part of the payload, but shouldn't that refresh token be the same one we just used? Or at the very least, it should have the same expiration? If not, then really, the refresh lifetime is infinite.

Here are the the frisby.js tests I would expect to pass, but using Taiseer's implementation for Web Api 2, the final expectation fails. We get a new refresh token with a new expiration on that token.

'use strict';

var frisby = require('frisby');
var config = require('../test-config.json');

var args = config[process.env.test || 'local'];
var host = args.host,
    clientId = args.clientId,
    usr = args.user1,
    pwd = args.password1;

frisby.create('Try and fail to get a protected resource')
    .get(host + '/api/test')
    .expectStatus(401)
    .expectHeaderContains('WWW-Authenticate', 'bearer')
    .toss();

frisby.create('Log in and get a protected resource')
    .post(host + '/token', {
        grant_type: 'password',
        username: usr,
        password: pwd,
        client_id: clientId
    })
    .expectJSONTypes({
        access_token: String,
        token_type: String,
        expires_in: Number,
        userName: String,
        refresh_token: String,
        'as:client_id': String,
        '.issued': String,
        '.expires': String
    })
    .expectJSON({
        token_type: 'bearer',
        userName: '[email protected]'
    })
    .afterJSON(function (json) {
        frisby.create('and now get protected resource with attached bearer token')
            .get(host + '/api/test', {
                headers: { 'Authorization': 'Bearer ' + json.access_token }
            })
            .expectStatus(200)
            .toss();
        frisby.create('and try to get a new access token with our refresh token')
            .post(host + '/token', {
                grant_type: 'refresh_token',
                refresh_token: json.refresh_token,
                client_id: clientId
            })
            .afterJSON(function (json2) {
                //we should receive a new access token
                expect(json.access_token).not.toEqual(json2.access_token);
                //but shouldn't the refresh token remain the same until *it* expires?
                expect(json.refresh_token).toEqual(json2.refresh_token);
            })
            .toss();
    })
    .toss();
like image 811
Stuart Avatar asked Dec 31 '14 19:12

Stuart


People also ask

What is an OAuth refresh token?

An OAuth Refresh Token is a string that the OAuth client can use to get a new access token without the user's interaction. A refresh token must not allow the client to gain any access beyond the scope of the original grant.

What happens to the old refresh token when I get another?

The Microsoft identity platform doesn't revoke old refresh tokens when used to fetch new access tokens. Securely delete the old refresh token after acquiring a new one. Refresh tokens need to be stored safely like access tokens or application credentials.

Can a refresh token be used with a confidential client?

Typically, refresh tokens are only used with confidential clients. However, since it is possible to use the authorization code flow without a client secret, the refresh grant may also be used by clients that don’t have a secret. If the client was issued a secret, then the client must authenticate this request.

What happens when an access token expires?

If I understand the behavior of refresh tokens correctly, when an access token expires, we should call the token endpoint of our auth server with something like and we will get back a new access token. That request will have a refresh token as part of the payload, but shouldn't that refresh token be the same one we just used?


1 Answers

You are 100% correct, the current implementation of refresh token has sliding expiration for the refresh token because with each use for grant_type=refresh_token we are issuing new access token and refresh token identifier, and this was perfect for my case because I want the user to be logged in forever as long as he is using the application, if he didn't use the application for a period greater then the expiration date for the refresh token then he will receive 401 when he tries to obtain new access token using the refresh token.

To change this behavior you need to issue only single refresh token identifier and return the same identifier when the user asks for new access token using the refresh token. And you can do this by customizing the business logic in this method and this too.

like image 61
Taiseer Joudeh Avatar answered Oct 08 '22 05:10

Taiseer Joudeh