diff --git a/common/models/User-Identity.js b/common/models/User-Identity.js index 3fb7c30a40..0a716a75e7 100644 --- a/common/models/User-Identity.js +++ b/common/models/User-Identity.js @@ -4,8 +4,7 @@ import dedent from 'dedent'; import { getSocialProvider, - getUsernameFromProvider, - createUserUpdatesFromProfile + getUsernameFromProvider } from '../../server/utils/auth'; import { observeMethod, observeQuery } from '../../server/utils/rx'; import { wrapHandledError } from '../../server/utils/create-handled-error.js'; @@ -15,6 +14,7 @@ import { wrapHandledError } from '../../server/utils/create-handled-error.js'; export default function(UserIdent) { UserIdent.on('dataSourceAttached', () => { UserIdent.findOne$ = observeMethod(UserIdent, 'findOne'); + UserIdent.create$ = observeMethod(UserIdent, 'create'); }); // original source // github.com/strongloop/loopback-component-passport @@ -48,84 +48,98 @@ export default function(UserIdent) { }, include: 'user' }; - return UserIdent.findOne$(query) - .flatMap(identity => { - if (!identity) { - throw wrapHandledError( - new Error('user identity account not found'), - { - message: dedent` - New accounts can only be created using an email address. - Please create an account below - `, - type: 'info', - redirectTo: '/signup' - } - ); - } - const modified = new Date(); - const user = identity.user(); - if (!user) { - const username = getUsernameFromProvider(provider, profile); - return observeQuery( + + if (provider === 'auth0') { + + const email = profile.emails[0].value; + return User.findOne$({ where: { email } }) + .flatMap(user => { + if (!user) { + return User.create$({ email }); + } + return Observable.of(user); + }) + .subscribe( + ( user ) => cb(null, user, null, null), + cb + ); + + } else { + return UserIdent.findOne$(query) + .flatMap(identity => { + if (!identity) { + throw wrapHandledError( + new Error('user identity account not found'), + { + message: dedent` + New accounts can only be created using an email address. + Please create an account below + `, + type: 'info', + redirectTo: '/deprecated-signup' + } + ); + } + 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: '/deprecated-signup', + message: dedent` + The user account associated with the ${provider} user ${username || 'Anon'} + no longer exists. + ` + } + ); + }); + } + + // 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', { - isOrphaned: username || true + userId: user.id, + created: new Date(), + ttl: user.constructor.settings.ttl } - ) - .do(() => { - throw wrapHandledError( - new Error('user identity is not associated with a user'), - { - type: 'info', - redirectTo: '/signup', - message: dedent` - The user account associated with the ${provider} user ${username || 'Anon'} - no longer exists. - ` - } - ); - }); - } - const updateUser = User.update$( - { id: user.id }, - createUserUpdatesFromProfile(provider, profile) - ).map(() => 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 + ); + return Observable.combineLatest( + updateIdentity, + createToken, + (user, identity, token) => ({ user, identity, token }) + ); + }) + .subscribe( + ({ user, identity, token }) => cb(null, user, identity, token), + cb ); - const createToken = observeQuery( - AccessToken, - 'create', - { - userId: user.id, - created: new Date(), - ttl: user.constructor.settings.ttl - } - ); - return Observable.combineLatest( - updateUser, - updateIdentity, - createToken, - (user, identity, token) => ({ user, identity, token }) - ); - }) - .subscribe( - ({ user, identity, token }) => cb(null, user, identity, token), - cb - ); + } }; } diff --git a/server/boot/authentication.js b/server/boot/authentication.js index a3a9b05ad5..546bf6c755 100644 --- a/server/boot/authentication.js +++ b/server/boot/authentication.js @@ -30,9 +30,8 @@ module.exports = function enableAuthentication(app) { const { AuthToken, User } = app.models; router.get('/email-signin', (req, res) => res.redirect(301, '/login')); - router.get('/signin', (req, res) => res.redirect(301, '/login')); + router.get('/signin', (req, res) => res.redirect(301, '/signup')); router.get('/signout', (req, res) => res.redirect(301, '/logout')); - router.get('/signup', (req, res) => res.redirect(301, '/deprecated-signup')); function getLegacySignUp(req, res) { if (isSignUpDisabled) { @@ -44,7 +43,7 @@ module.exports = function enableAuthentication(app) { title: 'Sign in to freeCodeCamp using your Email Address' }); } - router.get('/deprecated-signup', ifUserRedirect, getLegacySignUp); + router.get('/signup', ifUserRedirect, getLegacySignUp); router.get('/login', ifUserRedirect, (req, res) => res.redirect(301, '/auth/auth0')); diff --git a/server/component-passport.js b/server/component-passport.js index 53497c92da..788dfe865e 100644 --- a/server/component-passport.js +++ b/server/component-passport.js @@ -2,6 +2,7 @@ import passport from 'passport'; import { PassportConfigurator } from '@freecodecamp/loopback-component-passport'; import passportProviders from './passport-providers'; +import url from 'url'; const passportOptions = { emailOptional: true, @@ -90,26 +91,27 @@ export default function setupPassport(app) { configurator.init(); Object.keys(passportProviders).map(function(strategy) { - var config = passportProviders[strategy]; + let config = passportProviders[strategy]; config.session = config.session !== false; // https://stackoverflow.com/q/37430452 let successRedirect = (req) => { if (!!req && req.session && req.session.returnTo) { - var returnTo = req.session.returnTo; + let returnTo = req.session.returnTo; delete req.session.returnTo; return returnTo; } return config.successRedirect || ''; }; + config.customCallback = !config.redirectWithToken ? null - : function(req, res, next) { - var url = require('url'); + : (req, res, next) => { + passport.authenticate( strategy, - {session: false}, - function(err, user, info) { + { session: false }, + (err, user) => { if (err) { return next(err); } @@ -117,16 +119,24 @@ export default function setupPassport(app) { if (!user) { return res.redirect(config.failureRedirect); } - var redirect = url.parse(successRedirect(req), true); + let redirect = url.parse(successRedirect(req), true); delete redirect.search; - redirect.query = { - /* eslint-disable camelcase */ - access_token: info.accessToken.id, - /* eslint-enable camelcase */ - userId: user.id.toString() - }; + req.flash( + 'success', + 'Success! You have signed in to your account. Happy Coding!' + ); + + // redirect.query = { + // /* eslint-disable camelcase */ + // access_token: info.accessToken.id, + // /* eslint-enable camelcase */ + // userId: user.id.toString() + // }; + + user.loginByRequest(req, res); + redirect = url.format(redirect); return res.redirect(redirect); } diff --git a/server/passport-providers.js b/server/passport-providers.js index 4d408ec5b7..b190b6d53c 100644 --- a/server/passport-providers.js +++ b/server/passport-providers.js @@ -1,4 +1,4 @@ -const successRedirect = '/settings'; +const successRedirect = '/'; const failureRedirect = '/'; const linkSuccessRedirect = '/settings'; const linkFailureRedirect = '/settings'; @@ -176,7 +176,7 @@ export default { callbackURL: '/auth/auth0/callback', authPath: '/auth/auth0', callbackPath: '/auth/auth0/callback', - redirectWithToken: false, + redirectWithToken: true, successRedirect: successRedirect, failureRedirect: failureRedirect, scope: ['openid email'], diff --git a/server/views/account/deprecated-signup.jade b/server/views/account/deprecated-signup.jade index 8d15ddffba..b46594a565 100644 --- a/server/views/account/deprecated-signup.jade +++ b/server/views/account/deprecated-signup.jade @@ -6,10 +6,15 @@ block content .text-center h2 Sign up (or sign in with your existing account) br + br + br a.btn.btn-lg.btn-primary(href='/auth/auth0') - | Get a link on your email + | Continue with your email .row .text-center + br + br + br br a(href="/deprecated-signin") - | Continute with old sign in methods + | Continute with an old sign in method, that you used previously.