Fiks Authentik OIDC: bruk sub-claim som bruker-ID, fjern debug-logging

Authentik sin OIDC sub-claim er en SHA256-hash, ikke PostgreSQL UUID.
@auth/sveltekit sin interne user.id er en annen UUID som ikke matcher.
Løsning: lagre profile.sub som authentik_sub i JWT-tokenet og bruk
den som session.user.id.

Ny erfaringsfil: docs/erfaringer/authentik_oidc.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
vegard 2026-03-15 02:00:11 +01:00
parent 3d3c99cb0d
commit 9867bda949
5 changed files with 57 additions and 7 deletions

View file

@ -11,6 +11,7 @@ Formålet er å treffe raskere blink med neste komponent. Hver fil dekker én te
| `svelte5_reaktivitet.md` | Svelte 5 $state, SSR, reaktivitet gjennom funksjoner |
| `spacetimedb_integrasjon.md` | SDK-konvensjoner, TypeScript-bindings, BigInt, tilkobling |
| `adapter_moenster.md` | Adapter/factory for PG↔SpacetimeDB, hybrid-tilnærming |
| `authentik_oidc.md` | Authentik sub-claim format, @auth/sveltekit JWT-quirks |
## Retningslinjer

View file

@ -0,0 +1,52 @@
# Erfaring: Authentik OIDC-integrasjon
## 1. `profile.sub` er IKKE Authentik sin PostgreSQL-UUID
Authentik sin OIDC `sub`-claim er en **SHA256-hash**, ikke UUID-kolonnen fra `authentik_core_user`. Eksempel:
| Felt | Verdi |
|---|---|
| Authentik DB `uuid` | `0ac94e00-015b-4e78-9f32-269fa6ce3f44` |
| OIDC `sub` claim | `6af61f43c6647a237cbb381ee7788376a9bc20299c2c06281d9954d763e854f0` |
Bruk **alltid** `sub`-verdien fra OIDC som nøkkel i `users.authentik_id`. For å finne den riktige verdien for en bruker: logg inn og les `profile.sub` fra callback, eller sjekk JWT-tokenet.
## 2. `@auth/sveltekit` sin `user.id` er IKKE `profile.sub`
`@auth/sveltekit` genererer sin egen interne UUID for `user.id` i JWT. Denne overlever ikke mellom sesjoner og matcher ingenting i vår database.
For å bruke Authentik `sub` som bruker-ID:
```typescript
callbacks: {
jwt({ token, user, profile }) {
if (user) token.id = user.id;
if (profile?.sub) token.authentik_sub = profile.sub;
return token;
},
session({ session, token }) {
if (session.user) {
// Bruk Authentik sub, IKKE token.id
session.user.id = (token.authentik_sub ?? token.id) as string;
}
return session;
}
}
```
`profile` er kun tilgjengelig i JWT-callbacken ved innlogging (ikke ved token-refresh), derfor må `authentik_sub` lagres i tokenet.
**Referanse:** `web/src/lib/server/auth.ts`
## 3. Redirect-URI i Authentik
`@auth/sveltekit` bruker callback-URL `https://<domain>/auth/callback/<provider-id>`. For oss: `https://sidelinja.org/auth/callback/authentik`.
Denne MÅ være registrert som redirect-URI i Authentik sin OAuth2-provider. Verifiser via:
```sql
SELECT _redirect_uris FROM authentik_providers_oauth2_oauth2provider
WHERE client_id = '<din client_id>';
```
**Tips:** Legg til en regex-variant for lokal utvikling: `http://localhost:\d+/auth/callback/authentik` med `matching_mode: "regex"`.

View file

@ -10,12 +10,12 @@ INSERT INTO workspaces (id, name, slug) VALUES
-- Vegard (Authentik sub claim + dev-user alias)
INSERT INTO users (authentik_id, display_name) VALUES
('f0c628bf-2dde-42a9-86f9-6a308248a38f', 'Vegard Nøtnæs'),
('6af61f43c6647a237cbb381ee7788376a9bc20299c2c06281d9954d763e854f0', 'Vegard Nøtnæs'),
('dev-user-1', 'Vegard (dev)');
-- Koble begge bruker-IDer til workspace
INSERT INTO workspace_members (workspace_id, user_id, role) VALUES
('a0000000-0000-0000-0000-000000000001', 'f0c628bf-2dde-42a9-86f9-6a308248a38f', 'owner'),
('a0000000-0000-0000-0000-000000000001', '6af61f43c6647a237cbb381ee7788376a9bc20299c2c06281d9954d763e854f0', 'owner'),
('a0000000-0000-0000-0000-000000000001', 'dev-user-1', 'owner');
-- Workspace-rot-node (parent for workspace-level channels)

View file

@ -28,7 +28,6 @@ const workspaceHandle: Handle = async ({ event, resolve }) => {
const user = session?.user;
if (user?.id) {
console.log('[workspace] user.id:', user.id, 'user.name:', user.name);
event.locals.user = {
id: user.id,
name: user.name ?? '',

View file

@ -16,7 +16,6 @@ const authentik: Provider = {
clientId: env.AUTHENTIK_CLIENT_ID,
clientSecret: env.AUTHENTIK_CLIENT_SECRET,
profile(profile) {
console.log('[authentik] profile sub:', profile.sub, 'name:', profile.name);
return {
id: profile.sub,
name: profile.name ?? profile.preferred_username,
@ -55,15 +54,14 @@ export const { handle, signIn, signOut } = SvelteKitAuth({
if (profile?.sub) {
token.authentik_sub = profile.sub;
}
console.log('[jwt] token.id:', token.id, 'token.sub:', token.sub, 'token.authentik_sub:', token.authentik_sub, 'user?.id:', user?.id);
return token;
},
session({ session, token }) {
if (session.user) {
// Bruk Authentik sub som user.id for å matche users-tabellen
// Authentik sub er nøkkelen i users-tabellen.
// token.id er @auth/sveltekit sin interne UUID — ikke brukbar.
session.user.id = (token.authentik_sub ?? token.id) as string;
}
console.log('[session] user.id:', session.user?.id);
return session;
}
}