sso-core/src/lib/server/oauth2/response.ts

131 lines
3.0 KiB
TypeScript

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<string, string>).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<string, string>).toString();
return redirect(302, redirectUri);
}
}