Permalink
Please sign in to comment.
Showing
with
463 additions
and 395 deletions.
- +1 −0 docs.json
- +20 −5 lib/index.js
- +48 −72 lib/models/application-credential.js
- +25 −0 lib/models/application-credential.json
- +77 −0 lib/models/user-credential.js
- +42 −0 lib/models/user-credential.json
- +113 −232 lib/models/user-identity.js
- +43 −0 lib/models/user-identity.json
- +5 −4 lib/passport-configurator.js
- +88 −0 test/model.user-credential.test.js
- +1 −82 test/model.user-identity.test.js
1
docs.json
25
lib/index.js
@@ -1,10 +1,25 @@ | ||
-var uid = require('./models/user-identity'); | ||
-exports.UserIdentity = uid.UserIdentity; | ||
-exports.UserCredential = uid.UserCredential; | ||
-exports.ApplicationCredential = require('./models/application-credential'); | ||
+var loopback = require('loopback'); | ||
+var DataModel = loopback.PersistedModel || loopback.DataModel; | ||
+ | ||
+function loadModel(jsonFile) { | ||
+ var modelDefinition = require(jsonFile); | ||
+ return DataModel.extend(modelDefinition.name, | ||
+ modelDefinition.properties, | ||
+ { | ||
+ relations: modelDefinition.relations | ||
+ }); | ||
+} | ||
+ | ||
+var UserIdentityModel = loadModel('./models/user-identity.json'); | ||
+var UserCredentialModel = loadModel('./models/user-credential.json'); | ||
+var ApplicationCredentialModel = loadModel('./models/application-credential.json'); | ||
+ | ||
+exports.UserIdentity = require('./models/user-identity')(UserIdentityModel); | ||
+exports.UserCredential = require('./models/user-credential')(UserCredentialModel); | ||
+exports.ApplicationCredential = require('./models/application-credential')(ApplicationCredentialModel); | ||
exports.UserIdentity.autoAttach = 'db'; | ||
exports.UserCredential.autoAttach = 'db'; | ||
exports.ApplicationCredential.autoAttach = 'db'; | ||
-exports.PassportConfigurator = require('./passport-configurator'); | ||
+exports.PassportConfigurator = require('./passport-configurator'); |
120
lib/models/application-credential.js
25
lib/models/application-credential.json
@@ -0,0 +1,25 @@ | ||
+{ | ||
+ "name": "ApplicationCredential", | ||
+ "base": "PersistedModel", | ||
+ "properties": { | ||
+ "provider": { | ||
+ "type": "String", | ||
+ "required": true, | ||
+ "comments": "Facebook, google, twitter, linkedIn" | ||
+ }, | ||
+ "authScheme": { | ||
+ "type": "String", | ||
+ "comments": "oAuth, oAuth 2.0, OpenID, OpenID Connect" | ||
+ }, | ||
+ "credentials": "Object", | ||
+ "created": "Date", | ||
+ "modified": "Date" | ||
+ }, | ||
+ "relations": { | ||
+ "application": { | ||
+ "type": "belongsTo", | ||
+ "model": "Application", | ||
+ "foreignKey": "appId" | ||
+ } | ||
+ } | ||
+} |
77
lib/models/user-credential.js
@@ -0,0 +1,77 @@ | ||
+/** | ||
+ * Tracks third-party logins and profiles. | ||
+ * | ||
+ * @param {String} provider Auth provider name, such as facebook, google, twitter, linkedin. | ||
+ * @param {String} authScheme Auth scheme, such as oAuth, oAuth 2.0, OpenID, OpenID Connect. | ||
+ * @param {String} externalId Provider specific user ID. | ||
+ * @param {Object} profile User profile, see http://passportjs.org/guide/profile. | ||
+ * @param {Object} credentials Credentials. Actual properties depend on the auth scheme being used: | ||
+ * | ||
+ * - oAuth: token, tokenSecret | ||
+ * - oAuth 2.0: accessToken, refreshToken | ||
+ * - OpenID: openId | ||
+ * - OpenID: Connect: accessToken, refreshToken, profile | ||
+ * @param {*} userId The LoopBack user ID. | ||
+ * @param {Date} created The created date | ||
+ * @param {Date} modified The last modified date | ||
+ * | ||
+ * @class | ||
+ * @inherits {DataModel} | ||
+ */ | ||
+module.exports = function(UserCredential) { | ||
+ var utils = require('./utils'); | ||
+ | ||
+ /** | ||
+ * Link a third party account to a LoopBack user | ||
+ * @param {String} provider The provider name | ||
+ * @param {String} authScheme The authentication scheme | ||
+ * @param {Object} profile The profile | ||
+ * @param {Object} credentials The credentials | ||
+ * @param {Object} [options] The options | ||
+ * @callback {Function} cb The callback function | ||
+ * @param {Error|String} err The error object or string | ||
+ * @param {Object} [credential] The user credential object | ||
+ */ | ||
+ UserCredential.link = function (userId, provider, authScheme, profile, | ||
+ credentials, options, cb) { | ||
+ options = options || {}; | ||
+ if(typeof options === 'function' && cb === undefined) { | ||
+ cb = options; | ||
+ options = {}; | ||
+ } | ||
+ var userCredentialModel = utils.getModel(this, UserCredential); | ||
+ userCredentialModel.findOne({where: { | ||
+ userId: userId, | ||
+ provider: provider, | ||
+ externalId: profile.id | ||
+ }}, function (err, extCredential) { | ||
+ if (err) { | ||
+ return cb(err); | ||
+ } | ||
+ | ||
+ var date = new Date(); | ||
+ if (extCredential) { | ||
+ // Find the user for the given extCredential | ||
+ extCredential.credentials = credentials; | ||
+ return extCredential.updateAttributes({profile: profile, | ||
+ credentials: credentials, modified: date}, cb); | ||
+ } | ||
+ | ||
+ // Create the linked account | ||
+ userCredentialModel.create({ | ||
+ provider: provider, | ||
+ externalId: profile.id, | ||
+ authScheme: authScheme, | ||
+ profile: profile, | ||
+ credentials: credentials, | ||
+ userId: userId, | ||
+ created: date, | ||
+ modified: date | ||
+ }, function (err, i) { | ||
+ cb(err, i); | ||
+ }); | ||
+ | ||
+ }); | ||
+ } | ||
+ return UserCredential; | ||
+}; |
42
lib/models/user-credential.json
@@ -0,0 +1,42 @@ | ||
+{ | ||
+ "name": "UserCredential", | ||
+ "base": "PersistedModel", | ||
+ "properties": { | ||
+ "provider": { | ||
+ "type": "String", | ||
+ "comments": "facebook, google, twitter, linkedin" | ||
+ }, | ||
+ "authScheme": { | ||
+ "type": "String", | ||
+ "comments": "oAuth, oAuth 2.0, OpenID, OpenID Connect" | ||
+ }, | ||
+ "externalId": { | ||
+ "type": "String", | ||
+ "comments": "The provider specific id" | ||
+ }, | ||
+ "profile": { | ||
+ "type": "Object" | ||
+ }, | ||
+ "credentials": { | ||
+ "type": "Object" | ||
+ }, | ||
+ "created": "Date", | ||
+ "modified": "Date" | ||
+ }, | ||
+ "acls": [{ | ||
+ "principalType": "ROLE", | ||
+ "principalId": "$everyone", | ||
+ "permission": "DENY" | ||
+ }, { | ||
+ "principalType": "ROLE", | ||
+ "principalId": "$owner", | ||
+ "permission": "ALLOW" | ||
+ }], | ||
+ "relations": { | ||
+ "user": { | ||
+ "type": "belongsTo", | ||
+ "model": "User", | ||
+ "foreignKey": "userId" | ||
+ } | ||
+ } | ||
+} |
345
lib/models/user-identity.js
43
lib/models/user-identity.json
@@ -0,0 +1,43 @@ | ||
+{ | ||
+ "name": "UserIdentity", | ||
+ "plural": "UserIdentities", | ||
+ "base": "PersistedModel", | ||
+ "properties": { | ||
+ "provider": { | ||
+ "type": "String", | ||
+ "comments": "facebook, google, twitter, linkedin" | ||
+ }, | ||
+ "authScheme": { | ||
+ "type": "String", | ||
+ "comments": "oAuth, oAuth 2.0, OpenID, OpenID Connect" | ||
+ }, | ||
+ "externalId": { | ||
+ "type": "String", | ||
+ "comments": "The provider specific id" | ||
+ }, | ||
+ "profile": { | ||
+ "type": "Object" | ||
+ }, | ||
+ "credentials": { | ||
+ "type": "Object" | ||
+ }, | ||
+ "created": "Date", | ||
+ "modified": "Date" | ||
+ }, | ||
+ "acls": [{ | ||
+ "principalType": "ROLE", | ||
+ "principalId": "$everyone", | ||
+ "permission": "DENY" | ||
+ }, { | ||
+ "principalType": "ROLE", | ||
+ "principalId": "$owner", | ||
+ "permission": "ALLOW" | ||
+ }], | ||
+ "relations": { | ||
+ "user": { | ||
+ "type": "belongsTo", | ||
+ "model": "User", | ||
+ "foreignKey": "userId" | ||
+ } | ||
+ } | ||
+} |
9
lib/passport-configurator.js
88
test/model.user-credential.test.js
@@ -0,0 +1,88 @@ | ||
+var m = require('./init'); | ||
+var loopback = require('loopback'); | ||
+var assert = require('assert'); | ||
+var UserCredential = m.UserCredential; | ||
+var User = loopback.User; | ||
+ | ||
+before(function (done) { | ||
+ User.destroyAll(done); | ||
+}); | ||
+ | ||
+describe('UserCredential', function () { | ||
+ var userId = null; | ||
+ before(function (done) { | ||
+ var ds = loopback.createDataSource({ | ||
+ connector: 'memory' | ||
+ }); | ||
+ | ||
+ UserCredential.attachTo(ds); | ||
+ User.attachTo(ds); | ||
+ UserCredential.belongsTo(User); | ||
+ | ||
+ User.create({ | ||
+ username: 'facebook.abc', | ||
+ email: 'uuu@facebook.com', | ||
+ password: 'pass' | ||
+ }, function (err, user) { | ||
+ userId = user.id; | ||
+ done(err); | ||
+ }); | ||
+ }); | ||
+ | ||
+ it('supports linked 3rd party accounts', function (done) { | ||
+ UserCredential.link(userId, 'facebook', 'oAuth 2.0', | ||
+ {emails: [ | ||
+ {value: 'foo@bar.com'} | ||
+ ], id: 'f123', username: 'xyz' | ||
+ }, {accessToken: 'at1', refreshToken: 'rt1'}, function (err, cred) { | ||
+ assert(!err, 'No error should be reported'); | ||
+ | ||
+ assert.equal(cred.externalId, 'f123'); | ||
+ assert.equal(cred.provider, 'facebook'); | ||
+ assert.equal(cred.authScheme, 'oAuth 2.0'); | ||
+ assert.deepEqual(cred.credentials, {accessToken: 'at1', refreshToken: 'rt1'}); | ||
+ | ||
+ assert.equal(userId, cred.userId); | ||
+ | ||
+ // Follow the belongsTo relation | ||
+ cred.user(function (err, user) { | ||
+ assert(!err, 'No error should be reported'); | ||
+ assert.equal(user.username, 'facebook.abc'); | ||
+ assert.equal(user.email, 'uuu@facebook.com'); | ||
+ done(); | ||
+ }); | ||
+ }); | ||
+ }); | ||
+ | ||
+ it('supports linked 3rd party accounts if exists', function (done) { | ||
+ UserCredential.create({ | ||
+ externalId: 'f456', | ||
+ provider: 'facebook', | ||
+ userId: userId, | ||
+ credentials: {accessToken: 'at1', refreshToken: 'rt1'} | ||
+ }, function (err, cred) { | ||
+ UserCredential.link(userId, 'facebook', 'oAuth 2.0', | ||
+ {emails: [ | ||
+ {value: 'abc1@facebook.com'} | ||
+ ], id: 'f456', username: 'xyz' | ||
+ }, {accessToken: 'at2', refreshToken: 'rt2'}, function (err, cred) { | ||
+ assert(!err, 'No error should be reported'); | ||
+ | ||
+ assert.equal(cred.externalId, 'f456'); | ||
+ assert.equal(cred.provider, 'facebook'); | ||
+ assert.deepEqual(cred.credentials, {accessToken: 'at2', refreshToken: 'rt2'}); | ||
+ | ||
+ assert.equal(userId, cred.userId); | ||
+ | ||
+ // Follow the belongsTo relation | ||
+ cred.user(function (err, user) { | ||
+ assert(!err, 'No error should be reported'); | ||
+ assert.equal(user.username, 'facebook.abc'); | ||
+ assert.equal(user.email, 'uuu@facebook.com'); | ||
+ done(); | ||
+ }); | ||
+ }); | ||
+ }); | ||
+ }); | ||
+ | ||
+}); |
83
test/model.user-identity.test.js
0 comments on commit
9ef62a4