fix(auth0): Unify authentication via auth0 as provider

This commit is contained in:
Mrugesh Mohapatra
2018-06-28 20:27:15 +05:30
committed by Stuart Taylor
parent 38bcc2e250
commit ebba8e9c64
3 changed files with 49 additions and 149 deletions

View File

@ -1,28 +1,19 @@
import { Observable } from 'rx'; import { Observable } from 'rx';
// import debug from 'debug'; // import debug from 'debug';
import dedent from 'dedent'; import dedent from 'dedent';
import { isEmail } from 'validator';
import {
getSocialProvider,
getUsernameFromProvider
} from '../../server/utils/auth';
import { observeMethod, observeQuery } from '../../server/utils/rx'; import { observeMethod, observeQuery } from '../../server/utils/rx';
import { wrapHandledError } from '../../server/utils/create-handled-error.js'; import { wrapHandledError } from '../../server/utils/create-handled-error.js';
// const log = debug('fcc:models:userIdent'); // const log = debug('fcc:models:userIdent');
export default function(UserIdent) { export default function(UserIdent) {
UserIdent.on('dataSourceAttached', () => { UserIdent.on('dataSourceAttached', () => {
UserIdent.findOne$ = observeMethod(UserIdent, 'findOne'); UserIdent.findOne$ = observeMethod(UserIdent, 'findOne');
}); });
// original source
// github.com/strongloop/loopback-component-passport
// find identity if it exist
// if not redirect to email signup
// if yes and github
// update profile
// update username
// update picture
UserIdent.login = function( UserIdent.login = function(
_provider, _provider,
authScheme, authScheme,
@ -33,24 +24,44 @@ export default function(UserIdent) {
) { ) {
const User = UserIdent.app.models.User; const User = UserIdent.app.models.User;
const AccessToken = UserIdent.app.models.AccessToken; const AccessToken = UserIdent.app.models.AccessToken;
const provider = getSocialProvider(_provider);
options = options || {}; options = options || {};
if (typeof options === 'function' && !cb) { if (typeof options === 'function' && !cb) {
cb = options; cb = options;
options = {}; options = {};
} }
// get the social provider data and the external id from auth0
profile.id = profile.id || profile.openid; profile.id = profile.id || profile.openid;
const auth0IdString = '' + profile.id;
const socialExtId = auth0IdString.substring(auth0IdString.indexOf('|') + 1);
const provider = auth0IdString.substring(0, auth0IdString.indexOf('|'));
const query = { const query = {
where: { where: {
provider: provider, provider: provider,
externalId: profile.id externalId: socialExtId
}, },
include: 'user' include: 'user'
}; };
// get the email from the auth0 (its expected from social providers)
const email = (profile && profile.emails && profile.emails[0]) ?
profile.emails[0].value : '';
if (!isEmail('' + email)) {
throw wrapHandledError(
new Error('invalid or empty email recieved from auth0'),
{
message: dedent`
Oops... something is not right. We did not find a valid email from your
${provider} account. Please try again with a different provider that has an
email available with it.
`,
type: 'info',
redirectTo: '/'
}
);
}
if (provider === 'auth0') { if (provider === 'email') {
const email = profile.emails[0].value;
return User.findOne$({ where: { email } }) return User.findOne$({ where: { email } })
.flatMap(user => { .flatMap(user => {
return user ? return user ?
@ -101,61 +112,17 @@ export default function(UserIdent) {
return UserIdent.findOne$(query) return UserIdent.findOne$(query)
.flatMap(identity => { .flatMap(identity => {
if (!identity) { return identity ?
throw wrapHandledError( Observable.of(identity.user()) :
new Error('user identity account not found'), User.findOne$({ where: { email } })
{ .flatMap(user => {
message: dedent` return user ?
We cannot create new user accounts with the deprecated social sign in Observable.of(user) :
methods. If you already have an account with us, please sign in with your User.create$({ email }).toPromise();
email address instead.
`,
type: 'info',
redirectTo: '/'
}
);
}
const modified = new Date();
const user = identity.user();
if (!user) {
const username = getUsernameFromProvider(provider, profile);
return observeQuery(
identity,
'updateAttributes',
{
isOrphaned: username || true
}
)
.do(() => {
throw wrapHandledError(
new Error('user identity is not associated with a user'),
{
type: 'info',
redirectTo: '/',
message: dedent`
The user account associated with the ${provider} user ${username || 'Anon'}
no longer exists.
`
}
);
}); });
} })
.flatMap(user => {
// identity already exists
// find user and log them in
identity.credentials = credentials;
const attributes = {
// we no longer want to keep the profile
// this is information we do not need or use
profile: null,
credentials: credentials,
modified
};
const updateIdentity = observeQuery(
identity,
'updateAttributes',
attributes
);
const createToken = observeQuery( const createToken = observeQuery(
AccessToken, AccessToken,
'create', 'create',
@ -165,17 +132,24 @@ export default function(UserIdent) {
ttl: user.constructor.settings.ttl ttl: user.constructor.settings.ttl
} }
); );
const updateUser = user.update$({
email: email,
emailVerified: true,
emailAuthLinkTTL: null,
emailVerifyTTL: null
});
return Observable.combineLatest( return Observable.combineLatest(
Observable.of(user), Observable.of(user),
updateIdentity,
createToken, createToken,
(user, identity, token) => ({ user, identity, token }) updateUser,
(user, token) => ({ user, token })
); );
}) })
.subscribe( .subscribe(
({ user, identity, token }) => cb(null, user, identity, token), ({ user, token }) => cb(null, user, null, token),
cb cb
); );
} }
}; };
} }

View File

@ -3,23 +3,6 @@ MONGOHQ_URL='mongodb://localhost:27017/freecodecamp'
ROLLBAR_APP_ID='my-rollbar-app-id' ROLLBAR_APP_ID='my-rollbar-app-id'
FACEBOOK_ID=stuff
FACEBOOK_SECRET=stuff
GITHUB_ID=stuff
GITHUB_SECRET=stuff
GOOGLE_ID=stuff
GOOGLE_SECRET=stuff
LINKEDIN_ID=stuff
LINKEDIN_SECRET=stuff
TWITTER_KEY=stuff
TWITTER_SECRET=stuff
TWITTER_TOKEN=stuff
TWITTER_TOKEN_SECRET=stuff
AUTH0_CLIENT_ID=stuff AUTH0_CLIENT_ID=stuff
AUTH0_CLIENT_SECRET=stuff AUTH0_CLIENT_SECRET=stuff
AUTH0_DOMAIN=stuff AUTH0_DOMAIN=stuff

View File

@ -13,63 +13,6 @@ export default {
session: true, session: true,
failureFlash: true failureFlash: true
}, },
'facebook-login': {
provider: 'facebook',
module: 'passport-facebook',
clientID: process.env.FACEBOOK_ID,
clientSecret: process.env.FACEBOOK_SECRET,
authPath: '/auth/facebook',
callbackURL: '/auth/facebook/callback',
callbackPath: '/auth/facebook/callback',
useCustomCallback: true,
successRedirect: successRedirect,
failureRedirect: failureRedirect,
scope: ['email'],
failureFlash: true
},
'google-login': {
provider: 'google',
authScheme: 'oauth2',
module: 'passport-google-oauth2',
clientID: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
authPath: '/auth/google',
callbackURL: '/auth/google/callback',
callbackPath: '/auth/google/callback',
useCustomCallback: true,
successRedirect: successRedirect,
failureRedirect: failureRedirect,
scope: ['email', 'profile'],
failureFlash: true
},
'twitter-login': {
provider: 'twitter',
authScheme: 'oauth',
module: 'passport-twitter',
authPath: '/auth/twitter',
callbackURL: '/auth/twitter/callback',
callbackPath: '/auth/twitter/callback',
useCustomCallback: true,
successRedirect: successRedirect,
failureRedirect: failureRedirect,
consumerKey: process.env.TWITTER_KEY,
consumerSecret: process.env.TWITTER_SECRET,
failureFlash: true
},
'github-login': {
provider: 'github',
authScheme: 'oauth2',
module: 'passport-github',
authPath: '/auth/github',
callbackURL: '/auth/github/callback',
callbackPath: '/auth/github/callback',
useCustomCallback: true,
successRedirect: successRedirect,
failureRedirect: failureRedirect,
clientID: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
failureFlash: true
},
'auth0-login': { 'auth0-login': {
provider: 'auth0', provider: 'auth0',
module: 'passport-auth0', module: 'passport-auth0',
@ -83,7 +26,7 @@ export default {
useCustomCallback: true, useCustomCallback: true,
successRedirect: successRedirect, successRedirect: successRedirect,
failureRedirect: failureRedirect, failureRedirect: failureRedirect,
scope: ['openid email'], scope: ['openid profile email'],
failureFlash: true failureFlash: true
} }
}; };