This commit is contained in:
2026-02-18 19:19:42 +01:00
parent 9b783037ff
commit 5c86afd640
6 changed files with 39 additions and 45 deletions

View File

@@ -1,30 +1,29 @@
import { Issuer, generators, Client, TokenSet } from 'openid-client';
import { config } from '../config';
import type { AuthenticatedUser } from '../types';
import { Issuer, generators, Client, TokenSet } from "openid-client";
import { config } from "../config";
import type { AuthenticatedUser } from "../types";
let oidcClient: Client | null = null;
export async function initializeOIDC(): Promise<void> {
try {
const issuer = await Issuer.discover(config.oidc.issuerUrl);
oidcClient = new issuer.Client({
client_id: config.oidc.clientId,
redirect_uris: [config.oidc.redirectUri],
response_types: ['code'],
token_endpoint_auth_method: 'none', // PKCE flow - no client secret
response_types: ["code"],
token_endpoint_auth_method: "none", // PKCE flow - no client secret
});
console.log('OIDC client initialized');
console.log("OIDC client initialized");
} catch (error) {
console.error('Failed to initialize OIDC client:', error);
console.error("Failed to initialize OIDC client:", error);
throw error;
}
}
export function getOIDCClient(): Client {
if (!oidcClient) {
throw new Error('OIDC client not initialized');
throw new Error("OIDC client not initialized");
}
return oidcClient;
}
@@ -46,40 +45,38 @@ export function createAuthSession(): AuthSession {
export function getAuthorizationUrl(session: AuthSession): string {
const client = getOIDCClient();
const codeChallenge = generators.codeChallenge(session.codeVerifier);
return client.authorizationUrl({
scope: 'openid profile email',
scope: "openid profile email",
state: session.state,
nonce: session.nonce,
code_challenge: codeChallenge,
code_challenge_method: 'S256',
code_challenge_method: "S256",
});
}
export async function handleCallback(
params: Record<string, string>,
session: AuthSession
session: AuthSession,
): Promise<TokenSet> {
const client = getOIDCClient();
const tokenSet = await client.callback(
config.oidc.redirectUri,
params,
{
code_verifier: session.codeVerifier,
state: session.state,
nonce: session.nonce,
}
);
const tokenSet = await client.callback(undefined, params, {
code_verifier: session.codeVerifier,
state: session.state,
nonce: session.nonce,
});
return tokenSet;
}
export async function getUserInfo(tokenSet: TokenSet): Promise<AuthenticatedUser> {
export async function getUserInfo(
tokenSet: TokenSet,
): Promise<AuthenticatedUser> {
const client = getOIDCClient();
const claims = tokenSet.claims();
// Try to get more detailed userinfo if available
let userInfo: Record<string, unknown> = {};
try {
@@ -88,16 +85,21 @@ export async function getUserInfo(tokenSet: TokenSet): Promise<AuthenticatedUser
// Some providers don't support userinfo endpoint
// We'll use the claims from the ID token
}
const id = String(claims.sub);
const username = String(userInfo.preferred_username || claims.preferred_username || claims.name || id);
const email = String(userInfo.email || claims.email || '');
const fullName = String(userInfo.name || claims.name || '') || null;
const username = String(
userInfo.preferred_username ||
claims.preferred_username ||
claims.name ||
id,
);
const email = String(userInfo.email || claims.email || "");
const fullName = String(userInfo.name || claims.name || "") || null;
if (!email) {
throw new Error('Email not provided by OIDC provider');
throw new Error("Email not provided by OIDC provider");
}
return {
id,
username,
@@ -114,4 +116,4 @@ export async function verifyToken(tokenSet: TokenSet): Promise<boolean> {
} catch {
return false;
}
}
}

View File

@@ -14,9 +14,6 @@ export const config = {
oidc: {
issuerUrl: process.env.OIDC_ISSUER_URL || "",
clientId: process.env.OIDC_CLIENT_ID || "",
redirectUri:
process.env.OIDC_REDIRECT_URI ||
"http://localhost:3001/api/auth/callback",
},
session: {