context.js (2576B)
1 import { HttpError } from '../http/http-error.js'; 2 import { resolveClientForHost, resolveHostContext } from '../host-context.js'; 3 import { isVirtualClient } from './clients.js'; 4 5 export function requestOrigin(req) { 6 const forwarded = String(req.headers['x-forwarded-proto'] || '') 7 .split(',')[0] 8 .trim(); 9 const proto = forwarded || (req.socket?.encrypted ? 'https' : 'http'); 10 return `${proto}://${req.headers.host}`; 11 } 12 13 export function canonicalIssuer(publicUrl, realmSlug) { 14 const base = String(publicUrl || '').replace(/\/+$/, ''); 15 return `${base}/oidc/${encodeURIComponent(realmSlug)}`; 16 } 17 18 function pathSegments(req) { 19 const pathname = String(req.url || '/').split('?')[0]; 20 return pathname 21 .split('/') 22 .filter(Boolean) 23 .map((segment) => decodeURIComponent(segment)); 24 } 25 26 export async function resolveOidcContext(req, { publicUrl } = {}) { 27 const segments = pathSegments(req); 28 const origin = requestOrigin(req); 29 const hostContext = await resolveHostContext(req, { publicUrl }); 30 31 if (hostContext.mode === 'client') { 32 if (segments.length !== 1) throw new HttpError(404, 'not_found', 'Not found'); 33 return { 34 mode: 'client', 35 host: hostContext.host, 36 origin, 37 realm: hostContext.realm, 38 clientId: hostContext.clientId, 39 action: segments[0], 40 }; 41 } 42 43 if (segments.length !== 2) throw new HttpError(404, 'not_found', 'Not found'); 44 const clientId = segments[0]; 45 const action = segments[1]; 46 47 if (hostContext.mode === 'realm') { 48 const resolved = isVirtualClient(clientId) 49 ? { realm: hostContext.realm, clientId } 50 : await resolveClientForHost(hostContext, clientId); 51 return { 52 mode: 'realm', 53 host: hostContext.host, 54 origin, 55 realm: resolved.realm, 56 clientId: resolved.clientId, 57 action, 58 }; 59 } 60 61 const resolved = await resolveClientForHost(hostContext, clientId); 62 return { 63 mode: 'canonical', 64 host: hostContext.host, 65 origin, 66 realm: resolved.realm, 67 clientId: resolved.clientId, 68 action, 69 }; 70 } 71 72 export function endpointBase(context) { 73 if (context.mode === 'client') return `${context.origin}/oidc`; 74 return `${context.origin}/oidc/${encodeURIComponent(context.clientId)}`; 75 } 76 77 export function endpointUrls(context) { 78 const base = endpointBase(context); 79 return { 80 openid_configuration: `${base}/openid-configuration`, 81 login: `${base}/login`, 82 authorize: `${base}/authorize`, 83 token: `${base}/token`, 84 userinfo: `${base}/userinfo`, 85 jwks: `${base}/jwks`, 86 logout: `${base}/logout`, 87 }; 88 }