131 lines
3.0 KiB
TypeScript
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);
|
|
}
|
|
}
|