|
| 1 | +import { experience } from '@logto/schemas'; |
| 2 | +import { type MiddlewareType } from 'koa'; |
| 3 | +import { type IRouterParamContext } from 'koa-router'; |
| 4 | +import { errors, type Provider } from 'oidc-provider'; |
| 5 | + |
| 6 | +import RequestError from '../errors/RequestError/index.js'; |
| 7 | +import type Libraries from '../tenants/Libraries.js'; |
| 8 | +import type Queries from '../tenants/Queries.js'; |
| 9 | +import assertThat from '../utils/assert-that.js'; |
| 10 | + |
| 11 | +/** |
| 12 | + * Guard before allowing auto-consent |
| 13 | + */ |
| 14 | +export default function koaConsentGuard< |
| 15 | + StateT, |
| 16 | + ContextT extends IRouterParamContext, |
| 17 | + ResponseBodyT, |
| 18 | +>( |
| 19 | + provider: Provider, |
| 20 | + libraries: Libraries, |
| 21 | + query: Queries |
| 22 | +): MiddlewareType<StateT, ContextT, ResponseBodyT> { |
| 23 | + return async (ctx, next) => { |
| 24 | + const interactionDetails = await provider.interactionDetails(ctx.req, ctx.res); |
| 25 | + const { |
| 26 | + params: { token, login_hint: loginHint }, |
| 27 | + session, |
| 28 | + } = interactionDetails; |
| 29 | + |
| 30 | + assertThat(session, new errors.SessionNotFound('session not found')); |
| 31 | + |
| 32 | + if (token && loginHint && typeof token === 'string' && typeof loginHint === 'string') { |
| 33 | + const user = await query.users.findUserById(session.accountId); |
| 34 | + |
| 35 | + assertThat(user.primaryEmail, 'user.email_not_exist'); |
| 36 | + |
| 37 | + if (user.primaryEmail !== loginHint) { |
| 38 | + const searchParams = new URLSearchParams({ |
| 39 | + account: user.primaryEmail, |
| 40 | + }); |
| 41 | + ctx.redirect(`${experience.routes.switchAccount}?${searchParams.toString()}`); |
| 42 | + return; |
| 43 | + } |
| 44 | + |
| 45 | + try { |
| 46 | + await libraries.oneTimeTokens.verifyOneTimeToken(token, loginHint); |
| 47 | + } catch (error: unknown) { |
| 48 | + if (error instanceof RequestError) { |
| 49 | + if (error.code === 'one_time_token.email_mismatch') { |
| 50 | + const searchParams = new URLSearchParams({ |
| 51 | + account: user.primaryEmail, |
| 52 | + }); |
| 53 | + ctx.redirect(`${experience.routes.switchAccount}?${searchParams.toString()}`); |
| 54 | + return; |
| 55 | + } |
| 56 | + const searchParams = new URLSearchParams({ |
| 57 | + code: error.code, |
| 58 | + status: error.status.toString(), |
| 59 | + message: error.message, |
| 60 | + }); |
| 61 | + ctx.redirect(`${experience.routes.error}?${searchParams.toString()}`); |
| 62 | + return; |
| 63 | + } |
| 64 | + |
| 65 | + throw error; |
| 66 | + } |
| 67 | + } |
| 68 | + |
| 69 | + return next(); |
| 70 | + }; |
| 71 | +} |
0 commit comments