diff --git a/src/controllers/authenticate.js b/src/controllers/authenticate.js index bcb9d46f..c6b86746 100644 --- a/src/controllers/authenticate.js +++ b/src/controllers/authenticate.js @@ -88,13 +88,36 @@ export async function login(options, req, res) { }); } -export async function socialLoginCallback(options, req, res) { +export async function socialLoginCallback(options, service, req, res) { const { user } = req; if (await user.isBanned()) { throw new PermissionError('You have been banned.'); } + let script = ''; + if (user.pendingActivation) { + script = ` + var opener = window.opener; + if (opener) { + opener.postMessage({ + pending: true, + socialAvatar: ${JSON.stringify(user.avatar)}, + type: ${JSON.stringify(service)} + }, '*'); + } + window.close(); + `; + } else { + script = ` + var opener = window.opener; + if (opener) { + opener.postMessage({ pending: false }, '*'); + } + window.close(); + `; + } + await refreshSession(res, req.uwaveHttp, user, { ...options, session: 'cookie', @@ -107,14 +130,41 @@ export async function socialLoginCallback(options, req, res) { Success - + You can now close this window. - + `); } +export async function socialLoginFinish(options, service, req, res) { + const { user } = req; + const sessionType = req.query.session === 'cookie' ? 'cookie' : 'token'; + + if (await user.isBanned()) { + throw new PermissionError('You have been banned.'); + } + + const { username } = req.body; + + user.username = username; + user.pendingActivation = undefined; + await user.save(); + + const { token, socketToken } = await refreshSession(res, req.uwaveHttp, user, { + ...options, + session: sessionType, + }); + + return toItemResponse(user, { + meta: { + jwt: sessionType === 'token' ? token : 'cookie', + socketToken, + }, + }); +} + export async function getSocketToken(req) { const { user } = req; const { authRegistry } = req.uwaveHttp; diff --git a/src/passport.js b/src/passport.js index 9c323c47..718b4a01 100644 --- a/src/passport.js +++ b/src/passport.js @@ -18,6 +18,13 @@ export default function configurePassport(uw, options) { }); } + async function googleLogin(accessToken, refreshToken, profile) { + return socialLogin(accessToken, refreshToken, { + id: profile.id, + photos: profile.photos, + }); + } + async function serializeUser(user) { return user.id; } @@ -36,7 +43,7 @@ export default function configurePassport(uw, options) { callbackURL: '/auth/service/google/callback', ...options.auth.google, scope: ['profile'], - }, callbackify(socialLogin))); + }, callbackify(googleLogin))); } passport.use('jwt', new JWTStrategy(options.secret, user => uw.getUser(user.id))); diff --git a/src/routes/authenticate.js b/src/routes/authenticate.js index b745e0d4..22c8511c 100644 --- a/src/routes/authenticate.js +++ b/src/routes/authenticate.js @@ -70,11 +70,16 @@ export default function authenticateRoutes(api, options) { passport.authenticate('google'), route(controller.login.bind(null, options)), ) - // GET /auth/service/google/callback - Finish a social login using Google. + // GET /auth/service/google/callback - Receive social login data from Google. .get( '/service/google/callback', passport.authenticate('google'), - route(controller.socialLoginCallback.bind(null, options)), + route(controller.socialLoginCallback.bind(null, options, 'google')), + ) + // POST /auth/service/google/finish - Finish creating an account with Google. + .post( + '/service/google/finish', + route(controller.socialLoginFinish.bind(null, options, 'google')), ); }