diff --git a/src/lib/index.ts b/src/lib/index.ts index 95d0c57..8f3ec4f 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -1,8 +1,13 @@ -import { redirect } from "@sveltejs/kit" +import { error, redirect, type Cookies } from "@sveltejs/kit" import configuration from "./configuration" -const states = new Map>() +// Map of OAuth2 states +const states = new Map }>() +/** + * @description Launch an OAuth2 login request for this request. + * @param req Request to launch the login for; required to obtain the Host header. + */ export function launchLogin(req: Request) { // Create a state to be used in the OAuth2 authorization request const state = crypto.randomUUID() @@ -11,7 +16,7 @@ export function launchLogin(req: Request) { const searchParams = new URLSearchParams({ response_type: "code", client_id: configuration.oauth2.client.id, - redirect_uri: new URL(`/set`, req.url).toString(), + redirect_uri: req.url, scope: "openid profile email", state }) @@ -20,9 +25,61 @@ export function launchLogin(req: Request) { `?${searchParams.toString()}`, configuration.oauth2.endpoints.authenticate ) - - states - .set(state, setTimeout(() => states.delete(state), 60000)) - return redirect(302, target.toString()) + // cache state + // NO IDEA IF THIS WORKS IN SERVERLESS LOL + // not like this is going to be running serverless anyway + states + .set( + state, + { + timeout: setTimeout( + () => states.delete(state), + 2*60*1000 + ), + redirect_uri: req.url + } + ) + + throw redirect(302, target.toString()) +} + +/** + * @description Request a token from the OAuth server + * @param params + * @returns Access token, its time-to-expiration, and refresh token if applicable + */ +export async function retrieveToken( + params: + {grant_type: "authorization_code", redirect_uri: string, code: string} + | {grant_type: "refresh_token", refresh_token: string} +) { + // Generate a query string for the request + const searchParams = new URLSearchParams({ + ...params, + client_id: configuration.oauth2.client.id, + client_secret: configuration.oauth2.client.secret + }) + const url = new URL( + `?${searchParams.toString()}`, + configuration.oauth2.endpoints.token + ) + + let res = await fetch(url) + if (!res.ok) + throw error(401, "Couldn't retrieve token for user") + else + return (await res.json()) as { access_token: string, expires_in: number, refresh_token?: string } +} + +export async function getRequestUser(request: Request, cookies: Cookies) { + const params = new URLSearchParams(request.url.split("?").slice(1).join("?")) + let token = cookies.get("token") + if (!token && params.has("code") && params.has("state")) { + if (!states.has(params.get("state")!)) + throw error(401, "bad state") + + token = params.get("code")! + cookies.set("token", token, { path: "/" }) + } } \ No newline at end of file diff --git a/src/lib/types.ts b/src/lib/types.ts new file mode 100644 index 0000000..9b37fe3 --- /dev/null +++ b/src/lib/types.ts @@ -0,0 +1,4 @@ +export interface User { + name: string + sub: string +} \ No newline at end of file diff --git a/src/routes/set/+page.server.ts b/src/routes/set/+page.server.ts index 60fb579..f744a97 100644 --- a/src/routes/set/+page.server.ts +++ b/src/routes/set/+page.server.ts @@ -3,5 +3,5 @@ export async function load({ request, parent }) { //const { user } = await parent(); let user = null if (!user) - throw launchLogin(request) + launchLogin(request) } \ No newline at end of file