Pass options from User.login to createAccessToken
It will allow subclass of User to create access token based on additional properties such as 'scope'.
This commit is contained in:
parent
d24da8c37d
commit
78550a9bc5
|
@ -69,11 +69,23 @@ module.exports = function(User) {
|
||||||
* customize how access tokens are generated
|
* customize how access tokens are generated
|
||||||
*
|
*
|
||||||
* @param {Number} ttl The requested ttl
|
* @param {Number} ttl The requested ttl
|
||||||
* @callack {Function} cb The callback function
|
* @param {Object} [options] The options for access token, such as scope, appId
|
||||||
|
* @callback {Function} cb The callback function
|
||||||
* @param {String|Error} err The error string or object
|
* @param {String|Error} err The error string or object
|
||||||
* @param {AccessToken} token The generated access token object
|
* @param {AccessToken} token The generated access token object
|
||||||
*/
|
*/
|
||||||
User.prototype.createAccessToken = function(ttl, cb) {
|
User.prototype.createAccessToken = function(ttl, options, cb) {
|
||||||
|
if (cb === undefined && typeof options === 'function') {
|
||||||
|
// createAccessToken(ttl, cb)
|
||||||
|
cb = options;
|
||||||
|
options = undefined;
|
||||||
|
}
|
||||||
|
if (typeof ttl === 'object' && !options) {
|
||||||
|
// createAccessToken(options, cb)
|
||||||
|
options = ttl;
|
||||||
|
ttl = options.ttl;
|
||||||
|
}
|
||||||
|
options = options || {};
|
||||||
var userModel = this.constructor;
|
var userModel = this.constructor;
|
||||||
ttl = Math.min(ttl || userModel.settings.ttl, userModel.settings.maxTTL);
|
ttl = Math.min(ttl || userModel.settings.ttl, userModel.settings.maxTTL);
|
||||||
this.accessTokens.create({
|
this.accessTokens.create({
|
||||||
|
@ -193,6 +205,20 @@ module.exports = function(User) {
|
||||||
defaultError.statusCode = 401;
|
defaultError.statusCode = 401;
|
||||||
defaultError.code = 'LOGIN_FAILED';
|
defaultError.code = 'LOGIN_FAILED';
|
||||||
|
|
||||||
|
function tokenHandler(err, token) {
|
||||||
|
if (err) return fn(err);
|
||||||
|
if (Array.isArray(include) ? include.indexOf('user') !== -1 : include === 'user') {
|
||||||
|
// NOTE(bajtos) We can't set token.user here:
|
||||||
|
// 1. token.user already exists, it's a function injected by
|
||||||
|
// "AccessToken belongsTo User" relation
|
||||||
|
// 2. ModelBaseClass.toJSON() ignores own properties, thus
|
||||||
|
// the value won't be included in the HTTP response
|
||||||
|
// See also loopback#161 and loopback#162
|
||||||
|
token.__data.user = user;
|
||||||
|
}
|
||||||
|
fn(err, token);
|
||||||
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
debug('An error is reported from User.findOne: %j', err);
|
debug('An error is reported from User.findOne: %j', err);
|
||||||
fn(defaultError);
|
fn(defaultError);
|
||||||
|
@ -210,19 +236,11 @@ module.exports = function(User) {
|
||||||
err.code = 'LOGIN_FAILED_EMAIL_NOT_VERIFIED';
|
err.code = 'LOGIN_FAILED_EMAIL_NOT_VERIFIED';
|
||||||
return fn(err);
|
return fn(err);
|
||||||
} else {
|
} else {
|
||||||
user.createAccessToken(credentials.ttl, function(err, token) {
|
if (user.createAccessToken.length === 2) {
|
||||||
if (err) return fn(err);
|
user.createAccessToken(credentials.ttl, tokenHandler);
|
||||||
if (Array.isArray(include) ? include.indexOf('user') !== -1 : include === 'user') {
|
} else {
|
||||||
// NOTE(bajtos) We can't set token.user here:
|
user.createAccessToken(credentials.ttl, credentials, tokenHandler);
|
||||||
// 1. token.user already exists, it's a function injected by
|
}
|
||||||
// "AccessToken belongsTo User" relation
|
|
||||||
// 2. ModelBaseClass.toJSON() ignores own properties, thus
|
|
||||||
// the value won't be included in the HTTP response
|
|
||||||
// See also loopback#161 and loopback#162
|
|
||||||
token.__data.user = user;
|
|
||||||
}
|
|
||||||
fn(err, token);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug('The password is invalid for user %s', query.email || query.username);
|
debug('The password is invalid for user %s', query.email || query.username);
|
||||||
|
|
|
@ -12,6 +12,7 @@ describe('User', function() {
|
||||||
var validCredentialsEmailVerified = {email: 'foo1@bar.com', password: 'bar1', emailVerified: true};
|
var validCredentialsEmailVerified = {email: 'foo1@bar.com', password: 'bar1', emailVerified: true};
|
||||||
var validCredentialsEmailVerifiedOverREST = {email: 'foo2@bar.com', password: 'bar2', emailVerified: true};
|
var validCredentialsEmailVerifiedOverREST = {email: 'foo2@bar.com', password: 'bar2', emailVerified: true};
|
||||||
var validCredentialsWithTTL = {email: 'foo@bar.com', password: 'bar', ttl: 3600};
|
var validCredentialsWithTTL = {email: 'foo@bar.com', password: 'bar', ttl: 3600};
|
||||||
|
var validCredentialsWithTTLAndScope = {email: 'foo@bar.com', password: 'bar', ttl: 3600, scope: 'all'};
|
||||||
var invalidCredentials = {email: 'foo1@bar.com', password: 'invalid'};
|
var invalidCredentials = {email: 'foo1@bar.com', password: 'invalid'};
|
||||||
var incompleteCredentials = {password: 'bar1'};
|
var incompleteCredentials = {password: 'bar1'};
|
||||||
|
|
||||||
|
@ -248,6 +249,36 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Login a user using a custom createAccessToken with options',
|
||||||
|
function(done) {
|
||||||
|
var createToken = User.prototype.createAccessToken; // Save the original method
|
||||||
|
// Override createAccessToken
|
||||||
|
User.prototype.createAccessToken = function(ttl, options, cb) {
|
||||||
|
// Reduce the ttl by half for testing purpose
|
||||||
|
this.accessTokens.create({ttl: ttl / 2, scopes: options.scope}, cb);
|
||||||
|
};
|
||||||
|
User.login(validCredentialsWithTTLAndScope, function(err, accessToken) {
|
||||||
|
assert(accessToken.userId);
|
||||||
|
assert(accessToken.id);
|
||||||
|
assert.equal(accessToken.ttl, 1800);
|
||||||
|
assert.equal(accessToken.id.length, 64);
|
||||||
|
assert.equal(accessToken.scopes, 'all');
|
||||||
|
|
||||||
|
User.findById(accessToken.userId, function(err, user) {
|
||||||
|
user.createAccessToken(120, {scope: 'default'}, function(err, accessToken) {
|
||||||
|
assert(accessToken.userId);
|
||||||
|
assert(accessToken.id);
|
||||||
|
assert.equal(accessToken.ttl, 60);
|
||||||
|
assert.equal(accessToken.id.length, 64);
|
||||||
|
assert.equal(accessToken.scopes, 'default');
|
||||||
|
// Restore create access token
|
||||||
|
User.prototype.createAccessToken = createToken;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('Login should only allow correct credentials', function(done) {
|
it('Login should only allow correct credentials', function(done) {
|
||||||
User.login(invalidCredentials, function(err, accessToken) {
|
User.login(invalidCredentials, function(err, accessToken) {
|
||||||
assert(err);
|
assert(err);
|
||||||
|
|
Loading…
Reference in New Issue