0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-27 22:49:56 -05:00

Added endpoints for supporting 2FA

no refs

- Added `POST /session/verify` to send the user a verification code
- Added `PUT /session/verify` to verify the user's verification code
This commit is contained in:
Michael Barrett 2024-10-09 13:32:16 +01:00 committed by Kevin Ansfield
parent 51fa21324d
commit 7a18e829c5
5 changed files with 57 additions and 12 deletions

View file

@ -65,10 +65,15 @@ const controller = {
auth.session.logout(req, res, next); auth.session.logout(req, res, next);
}); });
}, },
verify() { sendVerification() {
return Promise.resolve(function sendAuthCodeMw(req, res, next) { return Promise.resolve(function sendAuthCodeMw(req, res, next) {
auth.session.sendAuthCode(req, res, next); auth.session.sendAuthCode(req, res, next);
}); });
},
verify() {
return Promise.resolve(function verifyAuthCodeMw(req, res, next) {
auth.session.verifyAuthCode(req, res, next);
});
} }
}; };

View file

@ -3,6 +3,7 @@ const createSessionService = require('@tryghost/session-service');
const sessionFromToken = require('@tryghost/mw-session-from-token'); const sessionFromToken = require('@tryghost/mw-session-from-token');
const createSessionMiddleware = require('./middleware'); const createSessionMiddleware = require('./middleware');
const settingsCache = require('../../../../shared/settings-cache'); const settingsCache = require('../../../../shared/settings-cache');
const {GhostMailer} = require('../../mail');
const expressSession = require('./express-session'); const expressSession = require('./express-session');
@ -29,6 +30,8 @@ function getOriginOfRequest(req) {
return null; return null;
} }
const mailer = new GhostMailer();
const sessionService = createSessionService({ const sessionService = createSessionService({
getOriginOfRequest, getOriginOfRequest,
getSession: expressSession.getSession, getSession: expressSession.getSession,
@ -37,7 +40,8 @@ const sessionService = createSessionService({
}, },
getSecret(key) { getSecret(key) {
return settingsCache.get(key); return settingsCache.get(key);
} },
mailer
}); });
module.exports = createSessionMiddleware({sessionService}); module.exports = createSessionMiddleware({sessionService});

View file

@ -34,17 +34,33 @@ function SessionMiddleware({sessionService}) {
async function sendAuthCode(req, res, next) { async function sendAuthCode(req, res, next) {
try { try {
await sessionService.sendAuthCodeToUser(req, res); await sessionService.sendAuthCodeToUser(req, res);
res.sendStatus(201); res.sendStatus(201);
} catch (err) { } catch (err) {
next(err); next(err);
} }
} }
async function verifyAuthCode(req, res, next) {
try {
const verified = await sessionService.verifyAuthCodeForUser(req, res);
if (verified) {
res.sendStatus(200);
} else {
res.sendStatus(401);
}
} catch (err) {
next(err);
}
}
return { return {
createSession: createSession, createSession: createSession,
logout: logout, logout: logout,
authenticate: authenticate, authenticate: authenticate,
sendAuthCode: sendAuthCode sendAuthCode: sendAuthCode,
verifyAuthCode: verifyAuthCode
}; };
} }

View file

@ -243,8 +243,8 @@ module.exports = function apiRoutes() {
http(api.session.add) http(api.session.add)
); );
router.del('/session', mw.authAdminApi, http(api.session.delete)); router.del('/session', mw.authAdminApi, http(api.session.delete));
// resending verification code for 2FA router.post('/session/verify', http(api.session.sendVerification));
router.post('/session/verify', mw.authAdminApi, http(api.session.verify)); router.put('/session/verify', http(api.session.verify));
// ## Identity // ## Identity
router.get('/identities', mw.authAdminApi, http(api.identities.read)); router.get('/identities', mw.authAdminApi, http(api.identities.read));

View file

@ -30,8 +30,7 @@ const {totp} = require('otplib');
* @prop {(req: Req, res: Res, user: User) => Promise<void>} createSessionForUser * @prop {(req: Req, res: Res, user: User) => Promise<void>} createSessionForUser
* @prop {(req: Req, res: Res) => Promise<void>} verifySession * @prop {(req: Req, res: Res) => Promise<void>} verifySession
* @prop {(req: Req, res: Res) => Promise<void>} sendAuthCodeToUser * @prop {(req: Req, res: Res) => Promise<void>} sendAuthCodeToUser
* @prop {(req: Req, res: Res) => string} generateAuthCodeForUser * @prop {(req: Req, res: Res) => Promise<boolean>} verifyAuthCodeForUser
* @prop {(req: Req, res: Res) => Promise<void>} verifyAuthCodeForUser
*/ */
/** /**
@ -40,11 +39,18 @@ const {totp} = require('otplib');
* @param {(data: {id: string}) => Promise<User>} deps.findUserById * @param {(data: {id: string}) => Promise<User>} deps.findUserById
* @param {(req: Req) => string} deps.getOriginOfRequest * @param {(req: Req) => string} deps.getOriginOfRequest
* @param {(key: string) => string} deps.getSecret * @param {(key: string) => string} deps.getSecret
* @param {import('../../core/core/server/services/mail').GhostMailer} deps.mailer
* *
* @returns {SessionService} * @returns {SessionService}
*/ */
module.exports = function createSessionService({getSession, findUserById, getOriginOfRequest, getSecret}) { module.exports = function createSessionService({
getSession,
findUserById,
getOriginOfRequest,
getSecret,
mailer
}) {
/** /**
* cookieCsrfProtection * cookieCsrfProtection
* *
@ -117,10 +123,10 @@ module.exports = function createSessionService({getSession, findUserById, getOri
* @param {Res} res * @param {Res} res
* @returns {Promise<boolean>} * @returns {Promise<boolean>}
*/ */
async function verifyAuthCodeForUser(req, res, token) { async function verifyAuthCodeForUser(req, res) {
const session = await getSession(req, res); // Todo: Do we need to handle "No session found"? const session = await getSession(req, res); // Todo: Do we need to handle "No session found"?
const secret = getSecret('admin_session_secret') + session.user_id; const secret = getSecret('admin_session_secret') + session.user_id;
const isValid = totp.check(token, secret); const isValid = totp.check(req.body.token, secret);
return isValid; return isValid;
} }
@ -133,8 +139,22 @@ module.exports = function createSessionService({getSession, findUserById, getOri
*/ */
async function sendAuthCodeToUser(req, res) { async function sendAuthCodeToUser(req, res) {
const session = await getSession(req, res); // eslint-disable-line const session = await getSession(req, res); // eslint-disable-line
generateAuthCodeForUser(); const token = await generateAuthCodeForUser(req, res);
// send auth code to user
// TODO: Find email address for user associated with user requesting token
const recipient = 'TODO';
// TODO: Generate email
const email = `<html><body><p>Here is your token matey: ${token}</p></body></html>`;
// TODO: Send email
await mailer.send({
to: recipient,
subject: 'tokens4u',
html: email
});
return Promise.resolve();
} }
/** /**