aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
authorLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2025-08-20 13:39:01 +0200
committerLibravatarLarge Libravatar memdmp <memdmpestrogenzone>2025-08-20 13:39:01 +0200
commitdddef149aea597a145e3717b2c461b251e0f6a8d (patch)
tree0a38a8d48e2db2501caca6d66358a4f88c1b743f /src/lib
parent7fdaea73c5c67565202e19d6182fc215427919c3 (diff)
downloadcrunched-dddef149aea597a145e3717b2c461b251e0f6a8d.tar.gz
crunched-dddef149aea597a145e3717b2c461b251e0f6a8d.tar.bz2
crunched-dddef149aea597a145e3717b2c461b251e0f6a8d.tar.lz
crunched-dddef149aea597a145e3717b2c461b251e0f6a8d.zip

feat: oidc attempt 82845345

Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/auth.server.ts36
-rw-r--r--src/lib/auth.ts37
-rw-r--r--src/lib/oncePromise.ts16
-rw-r--r--src/lib/util-types.ts4
4 files changed, 87 insertions, 6 deletions
diff --git a/src/lib/auth.server.ts b/src/lib/auth.server.ts
index 77e0dd7..f762cef 100644
--- a/src/lib/auth.server.ts
+++ b/src/lib/auth.server.ts
@@ -2,6 +2,7 @@ import { env as env_priv } from '$env/dynamic/private';
import { env } from '$env/dynamic/public';
import * as client from 'openid-client';
import oncePromise from './oncePromise';
+import type { Cookies } from '@sveltejs/kit';
const server = new URL(env.PUBLIC_AUTH_KEYCLOAK_ISSUER);
const clientId = env_priv.PRIVATE_AUTH_KEYCLOAK_ID;
@@ -10,7 +11,10 @@ const redirectPath = '/login/callback';
// Only trigger discovery on first client.discovery (resetting the function after a failed discovery)
export const getConfig = oncePromise(() =>
- client.discovery(server, clientId, clientSecret)
+ client.discovery(server, clientId, clientSecret).then((config) => {
+ client.useJwtResponseMode(config);
+ return config;
+ })
);
const codeVerifier = client.randomPKCECodeVerifier();
@@ -71,3 +75,33 @@ export const authorizeNewSession = async (
return tokens;
};
+
+export const unsetCookies = (cookies: Cookies) => {
+ for (const v of [
+ 'oid__access_token',
+ 'oid__refresh_token',
+ 'oid__token_type',
+ 'oid__expires_at',
+ 'oid__scopes',
+ ])
+ if (cookies.get(v)) cookies.delete(v, { path: '/' });
+};
+export const setCookies = (
+ cookies: Cookies,
+ tokens: client.TokenEndpointResponse & client.TokenEndpointResponseHelpers
+) => {
+ for (const [k, v] of Object.entries({
+ oid__access_token: tokens.access_token,
+ oid__refresh_token: tokens.refresh_token,
+ oid__token_type: tokens.token_type,
+ oid__expires_at: '' + (Date.now() + (tokens.expiresIn() ?? 0) * 1000),
+ oid__scopes: tokens.scope,
+ }))
+ if (v)
+ cookies.set(k, v, {
+ path: '/',
+ secure: true,
+ httpOnly: true,
+ sameSite: true,
+ });
+};
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
new file mode 100644
index 0000000..dd6b043
--- /dev/null
+++ b/src/lib/auth.ts
@@ -0,0 +1,37 @@
+import { browser } from '$app/environment';
+import { base } from '$app/paths';
+import { redirect } from '@sveltejs/kit';
+import type { ClientSession } from '../hooks.server';
+import { goto } from '$app/navigation';
+
+/**
+ * Returns `true` if scopes are all included in session, otherwise either attempts to re-login with the new scope added (unless `getScopeOnFail` is false) and returns false
+ *
+ * Check the return value of this, even if getScopeOnFail is true; navigating client-side may not stop thread immediately!
+ */
+export const checkScope = (
+ session: ClientSession,
+ /** The scopes we want */
+ neededScopes: string[],
+ /** Redirect to login page if the scopes aren't found */
+ getScopeOnFail = false,
+ /** The target URL if redirecting */
+ next?: string
+) => {
+ const scopes = session.tokens.scope?.split(' ') ?? [];
+ if (!neededScopes.find((v) => !scopes.includes(v))) return true;
+ else if (getScopeOnFail) {
+ const targetUrl = `${base}/login?${
+ next || browser
+ ? `next=${next ?? encodeURIComponent(location.href)}&`
+ : ''
+ }scope=${encodeURIComponent(
+ [...scopes, ...neededScopes]
+ .filter((v, i, a) => a.indexOf(v) === i)
+ .join(' ')
+ )}`;
+ if (browser) goto(targetUrl);
+ else throw redirect(307, targetUrl);
+ }
+ return false;
+};
diff --git a/src/lib/oncePromise.ts b/src/lib/oncePromise.ts
index f6ce775..6ce6287 100644
--- a/src/lib/oncePromise.ts
+++ b/src/lib/oncePromise.ts
@@ -10,13 +10,19 @@ const ensurePromise = <T>(maybePromise: T | PromiseLike<T>): Promise<T> =>
? (maybePromise as Promise<T>)
: Promise.resolve(maybePromise);
/** Returns a function that caches successful promises until time runs out, and throws away unsuccessful ones */
-export const oncePromise = <T>(create: () => Promise<T>, timeout = -1) => {
+export const oncePromise = <T>(
+ create: () => Promise<T>,
+ retries = true,
+ timeout = -1
+) => {
let getPromise = (): Promise<T> => {
const oldGetPromise = getPromise,
- promise = ensurePromise(create()).catch((e) => {
- getPromise = oldGetPromise;
- throw e;
- }),
+ promise = retries
+ ? ensurePromise(create()).catch((e) => {
+ getPromise = oldGetPromise;
+ throw e;
+ })
+ : ensurePromise(create()),
expires = timeout > 0 ? performance.now() + timeout : 0;
return (getPromise = expires
? ((() =>
diff --git a/src/lib/util-types.ts b/src/lib/util-types.ts
new file mode 100644
index 0000000..861edcf
--- /dev/null
+++ b/src/lib/util-types.ts
@@ -0,0 +1,4 @@
+export type Unpromise<T extends Promise<any>> = T extends Promise<infer U>
+ ? U
+ : never;
+export type Defined<T> = T extends undefined | null ? never : T;