synops/frontend/src/auth.ts
vegard 6bc742cc8d Authentik OIDC login (oppgave 3.2)
Implementerer autentisering med Authentik via @auth/sveltekit:
- OIDC authorization code flow med PKCE og state-verifisering
- JWT-callback lagrer authentik_sub (SHA256-hash, ikke UUID) for
  konsistens med maskinrommets auth_identities-tabell
- Server hooks: alle ruter unntatt /signin og /auth/* krever sesjon
- Uautentiserte brukere redirectes til /signin (303)
- Innloggingsside med client-side signIn('authentik')
- Hovedside viser innlogget bruker med logg ut-knapp
- TypeScript-typer utvidet med JWT.authentik_sub

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 13:45:33 +01:00

55 lines
1.4 KiB
TypeScript

import { SvelteKitAuth } from '@auth/sveltekit';
import type { OIDCConfig } from '@auth/core/providers';
import { env } from '$env/dynamic/private';
interface AuthentikProfile {
sub: string;
email: string;
name: string;
preferred_username: string;
groups: string[];
}
export const { handle, signIn, signOut } = SvelteKitAuth({
trustHost: true,
providers: [
{
id: 'authentik',
name: 'Authentik',
type: 'oidc',
issuer: env.AUTHENTIK_ISSUER,
clientId: env.AUTHENTIK_CLIENT_ID,
clientSecret: env.AUTHENTIK_CLIENT_SECRET,
authorization: {
params: {
scope: 'openid email profile offline_access'
}
},
checks: ['pkce', 'state'],
profile(profile: AuthentikProfile) {
return {
id: profile.sub,
name: profile.name ?? profile.preferred_username,
email: profile.email
};
}
} satisfies OIDCConfig<AuthentikProfile>
],
callbacks: {
jwt({ token, profile }) {
// profile is only available on initial sign-in, not on refresh.
// Store authentik_sub in the token so it persists across refreshes.
if (profile?.sub) {
token.authentik_sub = profile.sub;
}
return token;
},
session({ session, token }) {
if (session.user) {
// Use Authentik sub as user ID (not @auth/sveltekit's internal ID)
session.user.id = (token.authentik_sub ?? token.sub) as string;
}
return session;
}
}
});