fix(auth0): Unify authentication via auth0 as provider
This commit is contained in:
committed by
Stuart Taylor
parent
38bcc2e250
commit
ebba8e9c64
@ -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
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
17
sample.env
17
sample.env
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user