aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes/login
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes/login')
-rw-r--r--src/routes/login/+server.ts55
-rw-r--r--src/routes/login/callback/+server.ts80
2 files changed, 135 insertions, 0 deletions
diff --git a/src/routes/login/+server.ts b/src/routes/login/+server.ts
new file mode 100644
index 0000000..4a032d4
--- /dev/null
+++ b/src/routes/login/+server.ts
@@ -0,0 +1,55 @@
+import { getAuthorizeUrl } from '$lib/auth.server.js';
+import { error, redirect } from '@sveltejs/kit';
+
+export const GET = async (event) => {
+ let target = event.url.searchParams.get('next') ?? '/';
+ let desiredScopes =
+ event.url.searchParams.get('scope') ?? 'profile vm-own-read';
+ if (new URL(target, event.url.href).host !== event.url.host) target = '/';
+ const existingScopes = (event.cookies.get('oid__scopes') ?? '').split(' ');
+ const authed = await event.locals.auth();
+ const missingScopes = !!desiredScopes
+ .split(' ')
+ .find((v) => !existingScopes.includes(v));
+ if (
+ // if we're not authenticated
+ !authed ||
+ // or we're missing scopes
+ missingScopes
+ ) {
+ const { nonce, redirectTo } = await getAuthorizeUrl(
+ event.url.href,
+ desiredScopes.split(' ')
+ );
+ if (nonce) {
+ let existingNonces = [];
+ try {
+ const n = JSON.parse(event.cookies.get('pending-auth-nonces') ?? '[]');
+ if (Array.isArray(n) && n.length && typeof n[0] === 'string')
+ existingNonces = n;
+ } catch (error) {
+ // revoke all existing nonces
+ }
+ event.cookies.set(
+ 'pending-auth-nonces',
+ JSON.stringify([...existingNonces, nonce]),
+ {
+ path: '/',
+ httpOnly: true,
+ secure: true,
+ sameSite: true,
+ }
+ );
+ } else
+ event.cookies.delete('pending-auth-nonces', {
+ path: '/',
+ });
+ event.cookies.delete('next', {
+ path: target,
+ });
+ throw redirect(303, redirectTo);
+ } else {
+ throw redirect(303, target);
+ }
+};
+export const POST = GET;
diff --git a/src/routes/login/callback/+server.ts b/src/routes/login/callback/+server.ts
new file mode 100644
index 0000000..32b1647
--- /dev/null
+++ b/src/routes/login/callback/+server.ts
@@ -0,0 +1,80 @@
+import * as auth from '$lib/auth.server.js';
+import { error, json, redirect } from '@sveltejs/kit';
+import * as client from 'openid-client';
+
+// Pre-checker for nonce, not the primary implementation
+const handleNonce = (nonce: string | null, nonceCookie: string | undefined) => {
+ if (nonce) {
+ try {
+ const n = JSON.parse(nonceCookie ?? '[]');
+ if (Array.isArray(n) && n.length && typeof n[0] === 'string') {
+ if (!n.includes(nonce)) throw error(400, 'Nonce not in array');
+ else return n.filter((v) => v !== nonce);
+ } else throw error(400, 'Nonce provided, but nonce cookie not found');
+ } catch (e) {
+ throw error(400, `Failed parsing nonce: ${e}`);
+ }
+ } else if (nonceCookie) throw error(400, 'Missing Nonce');
+};
+export const GET = async (event) => {
+ const sp = event.url.searchParams;
+ const params = {
+ sessionState: sp.get('session_state'),
+ iss: sp.get('iss'),
+ code: sp.get('code'),
+ nonce: sp.get('nonce'),
+ };
+ if (!params.sessionState || !params.iss || !params.code)
+ throw error(400, 'Missing one of session_state, iss, code');
+
+ const remainingNonces = handleNonce(
+ params.nonce,
+ event.cookies.get('pending-auth-nonces')
+ );
+
+ try {
+ const tk = await auth.authorizeNewSession(
+ new URL(event.url.href),
+ params.nonce ?? undefined
+ );
+
+ for (const [k, v] of Object.entries({
+ oid__access_token: tk.access_token,
+ oid__token_type: tk.token_type,
+ oid__expires_at: '' + (Date.now() + (tk.expiresIn() ?? 0) * 1000),
+ oid__refresh_token: tk.refresh_token,
+ oid__sub: tk.claims()!.sub,
+ 'pending-auth-nonces': JSON.stringify(remainingNonces),
+ }))
+ if (v)
+ event.cookies.set(k, v, {
+ path: '/',
+ secure: true,
+ httpOnly: true,
+ sameSite: true,
+ });
+ if (tk.scope)
+ event.cookies.set('oid__scopes', tk.scope, {
+ path: '/',
+ secure: true,
+ httpOnly: true,
+ sameSite: true,
+ });
+
+ console.warn(
+ 'New Session:',
+ await client.fetchUserInfo(
+ await auth.getConfig(),
+ tk.access_token,
+ tk.claims()!.sub
+ )
+ );
+
+ return json({
+ sub: tk.claims()!.sub,
+ at: tk.access_token,
+ });
+ } catch (e) {
+ throw redirect(307, '/login');
+ }
+};