diff --git a/common/models/User-Identity.js b/common/models/User-Identity.js index 06b99f3e51..da883eaa4a 100644 --- a/common/models/User-Identity.js +++ b/common/models/User-Identity.js @@ -1,28 +1,19 @@ import { Observable } from 'rx'; // import debug from 'debug'; import dedent from 'dedent'; +import { isEmail } from 'validator'; -import { - getSocialProvider, - getUsernameFromProvider -} from '../../server/utils/auth'; import { observeMethod, observeQuery } from '../../server/utils/rx'; import { wrapHandledError } from '../../server/utils/create-handled-error.js'; // const log = debug('fcc:models:userIdent'); export default function(UserIdent) { + UserIdent.on('dataSourceAttached', () => { 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( _provider, authScheme, @@ -33,24 +24,44 @@ export default function(UserIdent) { ) { const User = UserIdent.app.models.User; const AccessToken = UserIdent.app.models.AccessToken; - const provider = getSocialProvider(_provider); options = options || {}; if (typeof options === 'function' && !cb) { cb = options; options = {}; } + + // get the social provider data and the external id from auth0 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 = { where: { provider: provider, - externalId: profile.id + externalId: socialExtId }, 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 } }) .flatMap(user => { return user ? @@ -63,8 +74,8 @@ export default function(UserIdent) { new Error('could not find or create a user'), { message: dedent` - Oops... something is not right. We could not find or create a - user with that email. + Oops... something is not right. We could not find or create a + user with that email. `, type: 'info', redirectTo: '/' @@ -101,61 +112,17 @@ export default function(UserIdent) { return UserIdent.findOne$(query) .flatMap(identity => { - if (!identity) { - throw wrapHandledError( - new Error('user identity account not found'), - { - message: dedent` - We cannot create new user accounts with the deprecated social sign in - methods. If you already have an account with us, please sign in with your - 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. - ` - } - ); + return identity ? + Observable.of(identity.user()) : + User.findOne$({ where: { email } }) + .flatMap(user => { + return user ? + Observable.of(user) : + User.create$({ email }).toPromise(); }); - } + }) + .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( AccessToken, 'create', @@ -165,17 +132,24 @@ export default function(UserIdent) { ttl: user.constructor.settings.ttl } ); + const updateUser = user.update$({ + email: email, + emailVerified: true, + emailAuthLinkTTL: null, + emailVerifyTTL: null + }); return Observable.combineLatest( Observable.of(user), - updateIdentity, createToken, - (user, identity, token) => ({ user, identity, token }) + updateUser, + (user, token) => ({ user, token }) ); }) .subscribe( - ({ user, identity, token }) => cb(null, user, identity, token), + ({ user, token }) => cb(null, user, null, token), cb ); + } }; } diff --git a/sample.env b/sample.env index 9349f20031..037b84803f 100644 --- a/sample.env +++ b/sample.env @@ -3,23 +3,6 @@ MONGOHQ_URL='mongodb://localhost:27017/freecodecamp' 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_SECRET=stuff AUTH0_DOMAIN=stuff diff --git a/server/passport-providers.js b/server/passport-providers.js index 5564b62b32..af300b5279 100644 --- a/server/passport-providers.js +++ b/server/passport-providers.js @@ -13,63 +13,6 @@ export default { session: 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': { provider: 'auth0', module: 'passport-auth0', @@ -83,7 +26,7 @@ export default { useCustomCallback: true, successRedirect: successRedirect, failureRedirect: failureRedirect, - scope: ['openid email'], + scope: ['openid profile email'], failureFlash: true } };