/** * OIDC interaction handler — checks for an existing Ghost Guild session. * * Flow: * 1. Outline redirects user to /oidc/auth * 2. oidc-provider creates an interaction and redirects here * 3. If the user has a valid auth-token cookie → complete the interaction (SSO) * 4. Otherwise → redirect to the OIDC login page */ import jwt from "jsonwebtoken"; import Member from "../../../models/member.js"; import { connectDB } from "../../../utils/mongoose.js"; import { getOidcProvider } from "../../../utils/oidc-provider.js"; export default defineEventHandler(async (event) => { const provider = await getOidcProvider(); const uid = getRouterParam(event, "uid")!; // Load the interaction details from oidc-provider const interactionDetails = await provider.interactionDetails( event.node.req, event.node.res ); const { prompt } = interactionDetails; // ----- Login prompt ----- if (prompt.name === "login") { // Check for existing Ghost Guild session const token = getCookie(event, "auth-token"); if (token) { try { const config = useRuntimeConfig(); const decoded = jwt.verify(token, config.jwtSecret) as { memberId: string; }; await connectDB(); const member = await (Member as any).findById(decoded.memberId); if ( member && member.status !== "suspended" && member.status !== "cancelled" ) { // Auto-complete the login interaction (SSO) const result = { login: { accountId: member._id.toString() }, }; await provider.interactionFinished( event.node.req, event.node.res, result, { mergeWithLastSubmission: false } ); return; } } catch { // Token invalid — fall through to login page } } // No valid session — redirect to login page return sendRedirect(event, `/auth/wiki-login?uid=${uid}`, 302); } // ----- Consent prompt ----- if (prompt.name === "consent") { // Auto-approve consent for our first-party client const grant = interactionDetails.grantId ? await provider.Grant.find(interactionDetails.grantId) : new provider.Grant({ accountId: interactionDetails.session!.accountId, clientId: interactionDetails.params.client_id as string, }); if (grant) { grant.addOIDCScope("openid profile email"); await grant.save(); const result = { consent: { grantId: grant.jti } }; await provider.interactionFinished( event.node.req, event.node.res, result, { mergeWithLastSubmission: true } ); return; } } // Fallback — shouldn't reach here normally throw createError({ statusCode: 400, statusMessage: "Unknown interaction" }); });