diff --git a/src/auth/OAuthAuthenticator.js b/src/auth/OAuthAuthenticator.js
index 0d2b33d81..d8df93582 100644
--- a/src/auth/OAuthAuthenticator.js
+++ b/src/auth/OAuthAuthenticator.js
@@ -179,6 +179,60 @@ OAuthAuthenticator.prototype.passwordGrant = function(userData, cb) {
return this.oauthWithIDTokenValidation.create(params, data);
};
+/**
+ * Sign in using a refresh token
+ *
+ * @method refreshToken
+ * @memberOf module:auth.OAuthAuthenticator.prototype
+ *
+ * @example
+ * 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
+ *
+ * API Docs
+ * .
+ *
+ *
+ * var data = {
+ * client_id: '{CLIENT_ID}', // Optional field.
+ * refresh_token: '{REFRESH_TOKEN}',
+ * };
+ *
+ * auth0.oauth.refreshToken(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.refreshToken = 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 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.
*
diff --git a/src/auth/index.js b/src/auth/index.js
index fa410aa9c..acb70f253 100644
--- a/src/auth/index.js
+++ b/src/auth/index.js
@@ -543,4 +543,39 @@ utils.wrapPropertyMethod(
*/
utils.wrapPropertyMethod(AuthenticationClient, 'passwordGrant', 'oauth.passwordGrant');
+/**
+ * Sign in using a refresh token
+ *
+ * @method refreshToken
+ * @memberOf module:auth.AuthenticationClient.prototype
+ *
+ * @example
+ * 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
+ *
+ * API Docs
+ * .
+ *
+ *
+ * var data = {
+ * client_id: '{CLIENT_ID}', // Optional field.
+ * refresh_token: '{REFRESH_TOKEN}',
+ * };
+ *
+ * auth0.refreshToken(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}
+ */
+utils.wrapPropertyMethod(AuthenticationClient, 'refreshToken', 'oauth.refreshToken');
+
module.exports = AuthenticationClient;
diff --git a/test/auth/oauth.tests.js b/test/auth/oauth.tests.js
index 3dcb436a2..f91d4104e 100644
--- a/test/auth/oauth.tests.js
+++ b/test/auth/oauth.tests.js
@@ -44,7 +44,13 @@ describe('OAuthAuthenticator', function() {
});
describe('instance', function() {
- var methods = ['signIn', 'socialSignIn', 'passwordGrant', 'authorizationCodeGrant'];
+ var methods = [
+ 'signIn',
+ 'socialSignIn',
+ 'passwordGrant',
+ 'authorizationCodeGrant',
+ 'refreshToken'
+ ];
var authenticator = new Authenticator(validOptions);
methods.forEach(function(method) {
@@ -405,6 +411,96 @@ describe('OAuthAuthenticator', function() {
});
});
+ describe('#refreshToken', 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.refreshToken).to.throw(ArgumentError, 'Missing user data object');
+ });
+ it('should require a refreshToken', function() {
+ var auth = this.authenticator;
+ var refresh = auth.refreshToken.bind(auth, {});
+ expect(refresh).to.throw(ArgumentError, 'refresh_token is required');
+ });
+ it('should accept a callback', function(done) {
+ this.authenticator.refreshToken(userData, done.bind(null, null));
+ });
+ it('should return a promise when no callback is provided', function(done) {
+ this.authenticator
+ .refreshToken(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
+ .refreshToken(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
+ .refreshToken(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
+ .refreshToken(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
+ .refreshToken(userData)
+ .then(function() {
+ expect(request.isDone()).to.be.true;
+ done();
+ })
+ .catch(done);
+ });
+ });
+
describe('#socialSignIn', function() {
var path = '/oauth/access_token';
var userData = {