Ghost/core/server/web/oauth/app.js

127 lines
4.6 KiB
JavaScript

const debug = require('ghost-ignition').debug('web:oauth:app');
const {URL} = require('url');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const express = require('../../../shared/express');
const urlUtils = require('../../../shared/url-utils');
const shared = require('../shared');
const config = require('../../../shared/config');
const settingsCache = require('../../services/settings/cache');
const models = require('../../models');
const auth = require('../../services/auth');
function randomPassword() {
return require('crypto').randomBytes(128).toString('hex');
}
module.exports = function setupOAuthApp() {
debug('OAuth App setup start');
const oauthApp = express('oauth');
if (!config.get('enableDeveloperExperiments')) {
debug('OAuth App setup skipped');
return oauthApp;
}
// send 503 json response in case of maintenance
oauthApp.use(shared.middlewares.maintenance);
function googleOAuthMiddleware(clientId, secret) {
return (req, res, next) => {
const callbackUrl = new URL(urlUtils.getSiteUrl());
callbackUrl.pathname = '/ghost/oauth/google/callback';
passport.authenticate(new GoogleStrategy({
clientID: clientId,
clientSecret: secret,
callbackURL: callbackUrl.href
}, async function (accessToken, refreshToken, profile) {
if (req.user) {
const emails = profile.emails.filter(email => email.verified === true).map(email => email.value);
if (!emails.includes(req.user.get('email'))) {
return res.redirect('/ghost/#/staff/?message=oauth-linking-failed');
}
//Associate logged-in user with oauth account
req.user.set('password', randomPassword());
await req.user.save();
} else {
//Find user in DB and log-in
const emails = profile.emails.filter(email => email.verified === true);
if (emails.length < 1) {
return res.redirect('/ghost/#/signin?message=login-failed');
}
const email = emails[0].value;
let user = await models.User.findOne({
email: email
});
if (!user) {
const options = {context: {internal: true}};
let invite = await models.Invite.findOne({email, status: 'sent'}, options);
if (!invite || invite.get('expires') < Date.now()) {
return res.redirect('/ghost/#/signin?message=login-failed');
}
//Accept invite
user = await models.User.add({
email: email,
name: profile.displayName,
password: randomPassword(),
roles: [invite.toJSON().role_id]
}, options);
await invite.destroy(options);
}
req.user = user;
}
await auth.session.sessionService.createSessionForUser(req, res, req.user);
return res.redirect('/ghost/');
}), {
scope: ['profile', 'email'],
session: false,
prompt: 'consent',
accessType: 'offline'
})(req, res, next);
};
}
oauthApp.get('/:provider', auth.authenticate.authenticateAdminApi, (req, res, next) => {
if (req.params.provider !== 'google') {
return res.sendStatus(404);
}
const clientId = settingsCache.get('oauth_client_id');
const secret = settingsCache.get('oauth_client_secret');
if (clientId && secret) {
return googleOAuthMiddleware(clientId, secret)(req, res, next);
}
res.sendStatus(404);
});
oauthApp.get('/:provider/callback', auth.authenticate.authenticateAdminApi, (req, res, next) => {
if (req.params.provider !== 'google') {
return res.sendStatus(404);
}
const clientId = settingsCache.get('oauth_client_id');
const secret = settingsCache.get('oauth_client_secret');
if (clientId && secret) {
return googleOAuthMiddleware(clientId, secret)(req, res, next);
}
res.sendStatus(404);
});
debug('OAuth App setup end');
return oauthApp;
};