import { redirect } from '@sveltejs/kit'; import { OAuth2Error } from './error'; interface ErrorResponseData { [x: string]: string | undefined; error: string; error_description: string; state?: string; } export interface OAuth2TokenResponse { id_token?: string; access_token?: string; refresh_token?: string; expires_in?: number; token_type?: string; state?: string; } export interface OAuth2IntrospectResponse { active: boolean; scope?: string; username?: string; client_id?: string; exp?: number; } export type OAuth2ResponseType = OAuth2TokenResponse | OAuth2IntrospectResponse; export class OAuth2Response { static error(url: URL, err: OAuth2Error, redirectUri?: string) { if (!(err instanceof OAuth2Error)) { throw err; } OAuth2Response.doErrorRedirect(url, err, redirectUri); return OAuth2Response.createErrorResponse(err); } static errorPlain(url: URL, err: OAuth2Error, redirectUri?: string) { if (!(err instanceof OAuth2Error)) { throw err; } OAuth2Response.doErrorRedirect(url, err, redirectUri); return { error: err.code, error_description: err.message }; } static response( url: URL, obj: OAuth2ResponseType, redirectUri?: string, fragment: boolean = false, state?: string ) { OAuth2Response.doResponseRedirect(url, obj, redirectUri, fragment, state); return OAuth2Response.createResponse(200, obj); } static responsePlain( url: URL, obj: OAuth2ResponseType, redirectUri?: string, fragment: boolean = false, state?: string ) { OAuth2Response.doResponseRedirect(url, obj, redirectUri, fragment, state); return obj; } static createResponse(code: number, data: unknown) { const isJson = typeof data === 'object'; const body = isJson ? JSON.stringify(data) : (data as string); return new Response(body, { status: code, headers: { 'Content-Type': isJson ? 'application/json' : 'application/octet-stream' } }); } static createErrorResponse(err: OAuth2Error) { return OAuth2Response.createResponse(err.status, { error: err.code, error_description: err.message }); } private static doResponseRedirect( url: URL, obj: OAuth2ResponseType, redirectUri?: string, fragment: boolean = false, state?: string ) { if (!redirectUri) return; const searchJoinChar = redirectUri.includes('?') ? '&' : '?'; redirectUri += fragment ? '#' : searchJoinChar; if (state || url.searchParams.has('state')) { (obj as OAuth2TokenResponse).state = state || (url.searchParams.get('state') as string); } redirectUri += new URLSearchParams(obj as Record).toString(); return redirect(302, redirectUri); } private static doErrorRedirect(url: URL, err: OAuth2Error, redirectUri?: string) { if (!redirectUri) return; const obj: ErrorResponseData = { error: err.code, error_description: err.message }; if (url.searchParams.has('state')) { obj.state = url.searchParams.get('state') as string; } redirectUri += '?' + new URLSearchParams(obj as Record).toString(); return redirect(302, redirectUri); } }