UNPKG

start-oauth

Version:

Lightweight and Secure OAuth2 for SolidStart

73 lines (64 loc) 2.71 kB
import { redirect, useLocation, useSearchParams } from "@solidjs/router"; import type { APIEvent } from "@solidjs/start/server"; import { makeState, decodeState, parseError } from "./utils"; import { providers, isProvider, type Providers } from "./providers"; import type { Configuration } from "./types"; export default function OAuth(config: Configuration) { const { password, handler } = config; if (!password) throw new Error("Missing OAuth password"); if (typeof handler !== "function") throw new Error("Invalid handler"); return async ({ request: { url }, params: { oauth } }: APIEvent) => { if (!isProvider(oauth)) return new Response("Unknown provider", { status: 404 }); const client = config[oauth]; if (!client?.id || !client.secret) return new Response(`Missing ${oauth} credentials`, { status: 400 }); const { requestCode, requestToken, requestUser } = providers[oauth]; const { searchParams, origin, pathname } = new URL(url); const redirect_uri = origin + pathname; const params: Record<string, string | undefined> = Object.fromEntries( searchParams.entries() ); if (params.fallback && !params.code && !params.error) { const { state, challenge } = await makeState( { fallback: params.fallback, redirect: params.redirect }, password ); return redirect( requestCode({ id: client.id, redirect_uri, state, challenge }) ); } if (!params.state) return redirect("/?error=Invalid state"); const decoded = await decodeState(params.state, password); if (!decoded) return redirect("/?error=Invalid state"); if (params.error) return redirect( `${decoded.fallback}?error=${encodeURIComponent(params.error)}` ); if (!params.code) return redirect(decoded.fallback + "?error=Missing code"); try { const { token_type, access_token } = await requestToken({ id: client.id, secret: client.secret, redirect_uri, code: params.code, verifier: decoded.verifier }); const user = await requestUser(`${token_type} ${access_token}`); return handler(user, decoded.redirect); } catch (error: unknown) { return redirect(`${decoded.fallback}?error=${parseError(error)}`); } }; } export function useOAuthLogin() { const location = useLocation(); const [searchParams] = useSearchParams(); return (provider: Providers) => { const params = new URLSearchParams(); params.set("fallback", location.pathname); if (typeof searchParams.redirect === "string") params.set("redirect", searchParams.redirect); return `/api/oauth/${provider}?${params.toString()}`; }; }