diff --git a/src/backend/src/middleware/configurable_auth.js b/src/backend/src/middleware/configurable_auth.js index 0bb002c2a..9dfa27c3a 100644 --- a/src/backend/src/middleware/configurable_auth.js +++ b/src/backend/src/middleware/configurable_auth.js @@ -21,7 +21,6 @@ const config = require('../config'); const { LegacyTokenError } = require('../services/auth/AuthService'); const { AccessTokenActorType } = require('../services/auth/Actor'); const { Context } = require('../util/context'); -const jwt = require('jsonwebtoken'); // The "/whoami" endpoint is a special case where we want to allow // a legacy token to be used for authentication. The "/whoami" @@ -47,6 +46,7 @@ const configurable_auth = options => async (req, res, next) => { } const optional = options?.optional; + const allow_cached_user = options?.allow_cached_user; // Request might already have been authed (PreAuthService) if ( req.actor ) return next(); @@ -159,6 +159,10 @@ const configurable_auth = options => async (req, res, next) => { // === Populate Context === context.set('actor', actor); if ( actor.type.user ) { + if ( allow_cached_user === false ) { + const svc_getUser = services.get('get-user'); + actor.type.user = await svc_getUser.get_user({ id: actor.type.user.id, force: true }); + } if ( actor.type.user?.suspended ) { throw APIError.create('forbidden'); } diff --git a/src/backend/src/routers/change_email.js b/src/backend/src/routers/change_email.js index 7107151ce..3e86b64fa 100644 --- a/src/backend/src/routers/change_email.js +++ b/src/backend/src/routers/change_email.js @@ -44,13 +44,17 @@ const CHANGE_EMAIL_CONFIRM = eggspress('/change_email/confirm', { const db = req.services.get('database').get(DB_WRITE, 'auth'); const rows = await db.read( - 'SELECT `unconfirmed_change_email` FROM `user` WHERE `id` = ? AND `change_email_confirm_token` = ?', + 'SELECT `unconfirmed_change_email`, `suspended` FROM `user` WHERE `id` = ? AND `change_email_confirm_token` = ?', [user_id, token], ); if ( rows.length === 0 ) { throw APIError.create('token_invalid'); } + if ( rows[0].suspended ) { + throw APIError.create('forbidden'); + } + const svc_cleanEmail = req.services.get('clean-email'); const clean_email = svc_cleanEmail.clean(rows[0].unconfirmed_change_email); diff --git a/src/backend/src/services/web/UserProtectedEndpointsService.js b/src/backend/src/services/web/UserProtectedEndpointsService.js index 7e67f0e46..9c10b275c 100644 --- a/src/backend/src/services/web/UserProtectedEndpointsService.js +++ b/src/backend/src/services/web/UserProtectedEndpointsService.js @@ -84,8 +84,8 @@ class UserProtectedEndpointsService extends BaseService { next(); }); - // Require authenticated session - router.use(configurable_auth({ no_options_auth: true })); + // Require authenticated session; bypass user cache to enforce suspension reliably + router.use(configurable_auth({ no_options_auth: true, allow_cached_user: false })); // Only allow user sessions with HTTP powers (session token), not GUI tokens or API tokens router.use((req, res, next) => {