aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes
diff options
context:
space:
mode:
Diffstat (limited to 'src/routes')
-rw-r--r--src/routes/+error.svelte1
-rw-r--r--src/routes/+layout.server.ts4
-rw-r--r--src/routes/+layout.svelte12
-rw-r--r--src/routes/+page.svelte26
-rw-r--r--src/routes/+server.ts3
-rw-r--r--src/routes/api/v1/whoami/+server.ts9
-rw-r--r--src/routes/home/+page.svelte34
-rw-r--r--src/routes/login/+server.ts16
-rw-r--r--src/routes/login/callback/+server.ts79
-rw-r--r--src/routes/login/callback/ok/+page.svelte13
-rw-r--r--src/routes/login/undo/+server.ts16
-rw-r--r--src/routes/logout/+server.ts14
-rw-r--r--src/routes/vms/+page.svelte16
13 files changed, 162 insertions, 81 deletions
diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte
index a19e467..c7cd13f 100644
--- a/src/routes/+error.svelte
+++ b/src/routes/+error.svelte
@@ -4,3 +4,4 @@
<h2 class="text-2xl">HTTP {page.status}</h2>
<p>{page.error?.message}</p>
+<p><a href="/home">Go Home</a></p>
diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts
index afdac71..c6f56da 100644
--- a/src/routes/+layout.server.ts
+++ b/src/routes/+layout.server.ts
@@ -1,5 +1,7 @@
+import { filterSession } from '../hooks.server';
+
export const load = async ({ locals }) => {
return {
- // session: await locals.auth(),
+ session: filterSession(await locals.auth()),
};
};
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index 1980c3a..dc81706 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -13,8 +13,16 @@
<nav class="header">
<h1 class="text-4xl">crunched</h1>
<p>
- <a href="/">home</a> - {#if page.data.session}<a href="/vms">vms</a
- >{:else}<a href="/login?scope=profile%20vm-own-read">login</a>{/if}
+ <a href="/">home</a> - {#if page.data.session}<a href="/vms">vms</a> -
+ <a
+ href="/login/undo?next={encodeURIComponent(
+ page.url.pathname + page.url.search
+ )}">logout</a
+ >{:else}<a
+ href="/login?scope=default&next={encodeURIComponent(
+ page.url.pathname + page.url.search
+ )}">login</a
+ >{/if}
</p>
<div class="my-2">
<hr />
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
deleted file mode 100644
index 2634622..0000000
--- a/src/routes/+page.svelte
+++ /dev/null
@@ -1,26 +0,0 @@
-<script>
- import { page } from '$app/state';
- let s = $derived(page.data.session);
-</script>
-
-{@debug s}
-
-<h1>SvelteKit Auth Example</h1>
-<div>
- {#if page.data.session}
- {#if page.data.session.user?.image}
- <img
- src={page.data.session.user.image}
- class="avatar"
- alt="User Avatar"
- />
- {/if}
- <span>
- <small>Signed in as</small><br />
- <strong>{page.data.session.user?.name ?? 'User'}</strong>
- </span>
- <!-- <div slot="submitButton" class="buttonPrimary">Sign out</div> -->
- {:else}
- <span class="notSignedInText">You are not signed in</span>
- {/if}
-</div>
diff --git a/src/routes/+server.ts b/src/routes/+server.ts
new file mode 100644
index 0000000..c1ee9f0
--- /dev/null
+++ b/src/routes/+server.ts
@@ -0,0 +1,3 @@
+import { redirect } from '@sveltejs/kit';
+
+export const GET = () => redirect(308, '/home');
diff --git a/src/routes/api/v1/whoami/+server.ts b/src/routes/api/v1/whoami/+server.ts
new file mode 100644
index 0000000..98809a4
--- /dev/null
+++ b/src/routes/api/v1/whoami/+server.ts
@@ -0,0 +1,9 @@
+import { error, json } from '@sveltejs/kit';
+import { filterSession, type Session } from '../../../../hooks.server.js';
+
+export const GET = async ({ locals }) => {
+ const data = (await locals.auth()) as Session;
+ if (data === undefined) throw error(403, 'Unauthorized');
+ if (data === null) throw error(401, 'Session Expired');
+ return json(filterSession(data));
+};
diff --git a/src/routes/home/+page.svelte b/src/routes/home/+page.svelte
new file mode 100644
index 0000000..8fa9ddd
--- /dev/null
+++ b/src/routes/home/+page.svelte
@@ -0,0 +1,34 @@
+<script lang="ts">
+ import { page } from '$app/state';
+ import { checkScope } from '$lib/auth';
+ import type { Session } from '../../hooks.server';
+ let session = $derived(page.data.session as Session);
+</script>
+
+<svelte:head>
+ <title>Crunched - Home</title>
+</svelte:head>
+
+<article>
+ <h2 class="text-2xl">Home</h2>
+ <div>
+ {#if session}
+ <div>
+ <small>Signed in as</small><br />
+ <strong>{session.userInfo.preferred_username ?? 'User'}</strong>
+ </div>
+ <div>
+ <small>Scope</small><br />
+ <strong>{session.tokens.scope}</strong>
+ </div>
+ <button
+ onclick={() => {
+ alert(checkScope(session, ['vm-own-write'], true));
+ }}>need scope</button
+ >
+ <!-- <div slot="submitButton" class="buttonPrimary">Sign out</div> -->
+ {:else}
+ <span class="notSignedInText">You are not signed in</span>
+ {/if}
+ </div>
+</article>
diff --git a/src/routes/login/+server.ts b/src/routes/login/+server.ts
index 4a032d4..7313f13 100644
--- a/src/routes/login/+server.ts
+++ b/src/routes/login/+server.ts
@@ -1,10 +1,13 @@
import { getAuthorizeUrl } from '$lib/auth.server.js';
-import { error, redirect } from '@sveltejs/kit';
+import { 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';
+ let desiredScopes = event.url.searchParams.get('scope') ?? 'default';
+ desiredScopes = desiredScopes
+ .split(' ')
+ .flatMap((v) => (v === 'default' ? 'vm-own-read vm-own-write' : ''))
+ .join(' ');
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();
@@ -44,11 +47,14 @@ export const GET = async (event) => {
event.cookies.delete('pending-auth-nonces', {
path: '/',
});
- event.cookies.delete('next', {
- path: target,
+ event.cookies.set('next', target, {
+ path: '/',
});
throw redirect(303, redirectTo);
} else {
+ event.cookies.delete('next', {
+ path: '/',
+ });
throw redirect(303, target);
}
};
diff --git a/src/routes/login/callback/+server.ts b/src/routes/login/callback/+server.ts
index 32b1647..1de7811 100644
--- a/src/routes/login/callback/+server.ts
+++ b/src/routes/login/callback/+server.ts
@@ -1,5 +1,6 @@
+import { base } from '$app/paths';
import * as auth from '$lib/auth.server.js';
-import { error, json, redirect } from '@sveltejs/kit';
+import { error, isHttpError, isRedirect, json, redirect } from '@sveltejs/kit';
import * as client from 'openid-client';
// Pre-checker for nonce, not the primary implementation
@@ -18,63 +19,47 @@ const handleNonce = (nonce: string | null, nonceCookie: string | undefined) => {
};
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,
+ sp.get('nonce'),
event.cookies.get('pending-auth-nonces')
);
try {
- const tk = await auth.authorizeNewSession(
+ const tokens = await auth.authorizeNewSession(
new URL(event.url.href),
- params.nonce ?? undefined
+ sp.get('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
- )
- );
+ auth.setCookies(event.cookies, tokens);
+ event.cookies.set('pending-auth-nonces', JSON.stringify(remainingNonces), {
+ path: '/',
+ secure: true,
+ sameSite: true,
+ httpOnly: true,
+ });
- return json({
- sub: tk.claims()!.sub,
- at: tk.access_token,
+ let target = event.cookies.get('next') ?? '/';
+ if (new URL(target, event.url.href).host !== event.url.host) target = '/';
+ event.cookies.delete('next', {
+ path: '/',
});
+ throw redirect(
+ 303,
+ `${base}/login/callback/ok?next=${encodeURIComponent(target)}`
+ );
} catch (e) {
- throw redirect(307, '/login');
+ if (isRedirect(e) || isHttpError(e)) throw e;
+ // @ts-ignore
+ if (e?.cause?.error === 'invalid_grant')
+ throw error(
+ 403,
+ 'Invalid Grant Provided - Does your account have access to all requested resources?'
+ );
+ else
+ throw error(
+ 500,
+ `Could not authorize new session: ${JSON.stringify(e, null, 2)}`
+ );
}
};
diff --git a/src/routes/login/callback/ok/+page.svelte b/src/routes/login/callback/ok/+page.svelte
new file mode 100644
index 0000000..a71d962
--- /dev/null
+++ b/src/routes/login/callback/ok/+page.svelte
@@ -0,0 +1,13 @@
+<script lang="ts">
+ import { page } from '$app/state';
+
+ let target = page.url.searchParams.get('next') ?? '/';
+ if (new URL(target, page.url.href).host !== page.url.host) target = '/';
+</script>
+
+<svelte:head>
+ <meta http-equiv="refresh" content="0.1; url={target}" />
+</svelte:head>
+
+<h2 class="text-xl">Redirecting...</h2>
+<p>If nothing happens, click <a href={target}>here</a>.</p>
diff --git a/src/routes/login/undo/+server.ts b/src/routes/login/undo/+server.ts
new file mode 100644
index 0000000..a3559d6
--- /dev/null
+++ b/src/routes/login/undo/+server.ts
@@ -0,0 +1,16 @@
+import * as auth from '$lib/auth.server.js';
+import { error, redirect } from '@sveltejs/kit';
+import * as client from 'openid-client';
+export const GET = async (event) => {
+ const token = event.cookies.get('oid__access_token');
+ if (!token) throw error(403, 'Logout requires an access token!');
+ await client.tokenRevocation(await auth.getConfig(), token);
+ let target =
+ event.url.searchParams.get('next') ?? event.cookies.get('next') ?? '/';
+ if (new URL(target, event.url.href).host !== event.url.host) target = '/';
+ event.cookies.delete('next', {
+ path: '/',
+ });
+ auth.unsetCookies(event.cookies);
+ throw redirect(303, `/login/callback/ok?next=${encodeURIComponent(target)}`);
+};
diff --git a/src/routes/logout/+server.ts b/src/routes/logout/+server.ts
new file mode 100644
index 0000000..4880c2a
--- /dev/null
+++ b/src/routes/logout/+server.ts
@@ -0,0 +1,14 @@
+import { base } from '$app/paths';
+import { redirect } from '@sveltejs/kit';
+
+export const GET = (event) =>
+ redirect(
+ 307,
+ `${base}/login/undo${
+ event.url.searchParams.get('next')
+ ? `?next=${encodeURIComponent(
+ event.url.searchParams.get('next') ?? '/'
+ )}`
+ : ''
+ }`
+ );
diff --git a/src/routes/vms/+page.svelte b/src/routes/vms/+page.svelte
new file mode 100644
index 0000000..928b4f4
--- /dev/null
+++ b/src/routes/vms/+page.svelte
@@ -0,0 +1,16 @@
+<script lang="ts">
+ import { page } from '$app/state';
+ import { onDestroy, onMount } from 'svelte';
+
+ let mounted = $state(false);
+ onMount(() => (mounted = true));
+ onDestroy(() => (mounted = false));
+</script>
+
+<svelte:head>
+ {#if !mounted}
+ <noscript>
+ <meta http-equiv="refresh" content="5; url={page.url.href}" />
+ </noscript>
+ {/if}
+</svelte:head>