Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add method to refresh a token using a refresh_token #313

Merged
merged 8 commits into from
Nov 9, 2018
55 changes: 55 additions & 0 deletions src/auth/OAuthAuthenticator.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,61 @@ OAuthAuthenticator.prototype.passwordGrant = function(userData, cb) {
return this.oauthWithIDTokenValidation.create(params, data);
};

/**
* Sign in using a refresh token
*
* @method refresh
* @memberOf module:auth.OAuthAuthenticator.prototype
*
* @example <caption>
* Given a refresh token from a previous authentication request
* it will return a JSON with the access_token and id_token.
* More information in the
* <a href="https://auth0.com/docs/api/authentication#refresh-token">
* API Docs
* </a>.
* </caption>
*
* var data = {
* client_id: '{CLIENT_ID}', // Optional field.
* refresh_token: '{REFRESH_TOKEN}',
* };
*
* auth0.oauth.refresh(data, function (err, userData) {
* if (err) {
* // Handle error.
* }
*
* console.log(userData);
* });
*
* @param {Object} userData User credentials object.
* @param {String} userData.refresh_token Refresh token.
*
* @return {Promise|undefined}
*/
OAuthAuthenticator.prototype.refresh = function (userData, cb) {
var params = {
type: 'token'
};
var defaultFields = {
client_id: this.clientId,
grant_type: 'refresh_token'
};
var data = extend(defaultFields, userData);
if (!userData || typeof userData !== 'object') {
throw new ArgumentError('Missing user data object');
}
if (typeof data.refresh_token !== 'string'
|| data.refresh_token.split().length === 0) {
throw new ArgumentError('refresh_token field is required');
}
if (cb && cb instanceof Function) {
return this.oauthWithIDTokenValidation.create(params, data, cb);
}
return this.oauthWithIDTokenValidation.create(params, data);
};

/**
* Sign in using a social provider access token.
*
Expand Down
35 changes: 35 additions & 0 deletions src/auth/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -543,4 +543,39 @@ utils.wrapPropertyMethod(
*/
utils.wrapPropertyMethod(AuthenticationClient, 'passwordGrant', 'oauth.passwordGrant');

/**
* Sign in using a refresh token
*
* @method refresh
* @memberOf module:auth.OAuthAuthenticator.prototype
modeswitch marked this conversation as resolved.
Show resolved Hide resolved
modeswitch marked this conversation as resolved.
Show resolved Hide resolved
*
* @example <caption>
* Given a refresh token from a previous authentication request
* it will return a JSON with the access_token and id_token.
* More information in the
* <a href="https://auth0.com/docs/api/authentication#refresh-token">
* API Docs
* </a>.
* </caption>
*
* var data = {
* client_id: '{CLIENT_ID}', // Optional field.
* refresh_token: '{REFRESH_TOKEN}',
* };
*
* auth0.oauth.refresh(data, function (err, userData) {
modeswitch marked this conversation as resolved.
Show resolved Hide resolved
* if (err) {
* // Handle error.
* }
*
* console.log(userData);
* });
*
* @param {Object} userData User credentials object.
* @param {String} userData.refresh_token Refresh token.
*
* @return {Promise|undefined}
*/
utils.wrapPropertyMethod(AuthenticationClient, 'refresh', 'oauth.refresh');

module.exports = AuthenticationClient;
105 changes: 104 additions & 1 deletion test/auth/oauth.tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('OAuthAuthenticator', function() {
});

describe('instance', function() {
var methods = ['signIn', 'socialSignIn', 'passwordGrant', 'authorizationCodeGrant'];
var methods = ['signIn', 'socialSignIn', 'passwordGrant', 'authorizationCodeGrant', 'refresh'];
var authenticator = new Authenticator(validOptions);

methods.forEach(function(method) {
Expand Down Expand Up @@ -405,6 +405,109 @@ describe('OAuthAuthenticator', function() {
});
});

describe('#refresh', function () {
var path = '/oauth/token';
var userData = {
refresh_token: 'refresh_token'
};
beforeEach(function () {
this.authenticator = new Authenticator(validOptions);
this.request = nock(API_URL)
.post(path)
.reply(200);
});
it('should require an object as first argument', function () {
expect(this.authenticator.refresh)
.to.throw(ArgumentError, 'Missing user data object');
});
it('should require a refresh_token', function () {
var auth = this.authenticator;
var refresh = auth.refresh.bind(auth, {});
expect(refresh)
.to.throw(ArgumentError, 'refresh_token field is required');
});
it('should accept a callback', function (done) {
this
.authenticator
.refresh(userData, done.bind(null, null));
});
it('should return a promise when no callback is provided', function (done) {
this
.authenticator
.refresh(userData)
.then(done.bind(null, null))
.catch(done.bind(null, null));
});
it('should perform a POST request to ' + path, function (done) {
var request = this.request;
this
.authenticator
.refresh(userData)
.then(function () {
expect(request.isDone())
.to.be.true;
done();
})
.catch(done);
});
it('should include the user data in the request', function (done) {
nock.cleanAll();
var request = nock(API_URL)
.post(path, function (body) {
for (var property in userData) {
if (userData[property] !== body[property]) {
return false;
}
}
return true;
})
.reply(200);
this
.authenticator
.refresh(userData)
.then(function () {
expect(request.isDone())
.to.be.true;
done();
})
.catch(done);
});
it('should include the Auth0 client ID in the request', function (done) {
nock.cleanAll();
var request = nock(API_URL)
.post(path, function (body) {
return body.client_id === CLIENT_ID;
})
.reply(200);
this
.authenticator
.refresh(userData)
.then(function () {
expect(request.isDone())
.to.be.true;
done();
})
.catch(done);
});
it('should use refresh_token as default grant type', function (done) {
nock.cleanAll();
var request = nock(API_URL)
.post(path, function (body) {
return body.grant_type === 'refresh_token';
})
.reply(200);
this
.authenticator
.refresh(userData)
.then(function () {
expect(request.isDone())
.to.be.true;
done();
})
.catch(done);
});
});

describe('#socialSignIn', function() {
var path = '/oauth/access_token';
var userData = {
Expand Down