- Add OIDC login flow with Authentik provider - Implement session-based auth with Redis store - Add avatar display from OIDC claims - Fix input field performance with react-textarea-autosize - Stabilize callbacks to prevent unnecessary re-renders - Fix history loading to skip empty session files - Add 2-row default height for input textarea 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
147 lines
3.2 KiB
JavaScript
147 lines
3.2 KiB
JavaScript
// OIDC Client Wrapper using openid-client
|
|
|
|
import { Issuer, generators } from 'openid-client';
|
|
import { authConfig } from '../config/auth.js';
|
|
|
|
let oidcClient = null;
|
|
let issuer = null;
|
|
|
|
/**
|
|
* Initialize the OIDC client by discovering the issuer
|
|
*/
|
|
export async function initializeOIDC() {
|
|
if (!authConfig.app.authEnabled) {
|
|
console.log('[OIDC] Authentication disabled, skipping initialization');
|
|
return null;
|
|
}
|
|
|
|
try {
|
|
console.log(`[OIDC] Discovering issuer: ${authConfig.oidc.issuer}`);
|
|
issuer = await Issuer.discover(authConfig.oidc.issuer);
|
|
console.log(`[OIDC] Discovered issuer: ${issuer.issuer}`);
|
|
|
|
oidcClient = new issuer.Client({
|
|
client_id: authConfig.oidc.clientId,
|
|
client_secret: authConfig.oidc.clientSecret,
|
|
redirect_uris: [authConfig.oidc.redirectUri],
|
|
response_types: ['code'],
|
|
});
|
|
|
|
console.log('[OIDC] Client initialized successfully');
|
|
return oidcClient;
|
|
} catch (error) {
|
|
console.error('[OIDC] Failed to initialize client:', error.message);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the initialized OIDC client
|
|
*/
|
|
export function getClient() {
|
|
if (!oidcClient) {
|
|
throw new Error('OIDC client not initialized. Call initializeOIDC() first.');
|
|
}
|
|
return oidcClient;
|
|
}
|
|
|
|
/**
|
|
* Generate authorization URL for login
|
|
*/
|
|
export function getAuthorizationUrl(state, nonce, codeVerifier) {
|
|
const client = getClient();
|
|
const codeChallenge = generators.codeChallenge(codeVerifier);
|
|
|
|
return client.authorizationUrl({
|
|
scope: authConfig.oidc.scopes.join(' '),
|
|
state,
|
|
nonce,
|
|
code_challenge: codeChallenge,
|
|
code_challenge_method: 'S256',
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Exchange authorization code for tokens
|
|
*/
|
|
export async function exchangeCode(code, codeVerifier, nonce) {
|
|
const client = getClient();
|
|
|
|
const tokenSet = await client.callback(
|
|
authConfig.oidc.redirectUri,
|
|
{ code },
|
|
{ code_verifier: codeVerifier, nonce }
|
|
);
|
|
|
|
return tokenSet;
|
|
}
|
|
|
|
/**
|
|
* Validate and decode ID token claims
|
|
*/
|
|
export function getIdTokenClaims(tokenSet) {
|
|
return tokenSet.claims();
|
|
}
|
|
|
|
/**
|
|
* Get user info from the userinfo endpoint
|
|
*/
|
|
export async function getUserInfo(accessToken) {
|
|
const client = getClient();
|
|
return await client.userinfo(accessToken);
|
|
}
|
|
|
|
/**
|
|
* Refresh access token using refresh token
|
|
*/
|
|
export async function refreshTokens(refreshToken) {
|
|
const client = getClient();
|
|
return await client.refresh(refreshToken);
|
|
}
|
|
|
|
/**
|
|
* Get end session URL for logout
|
|
*/
|
|
export function getEndSessionUrl(idTokenHint, postLogoutRedirectUri) {
|
|
const client = getClient();
|
|
return client.endSessionUrl({
|
|
id_token_hint: idTokenHint,
|
|
post_logout_redirect_uri: postLogoutRedirectUri,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Generate random state for CSRF protection
|
|
*/
|
|
export function generateState() {
|
|
return generators.state();
|
|
}
|
|
|
|
/**
|
|
* Generate random nonce for ID token validation
|
|
*/
|
|
export function generateNonce() {
|
|
return generators.nonce();
|
|
}
|
|
|
|
/**
|
|
* Generate code verifier for PKCE
|
|
*/
|
|
export function generateCodeVerifier() {
|
|
return generators.codeVerifier();
|
|
}
|
|
|
|
export default {
|
|
initializeOIDC,
|
|
getClient,
|
|
getAuthorizationUrl,
|
|
exchangeCode,
|
|
getIdTokenClaims,
|
|
getUserInfo,
|
|
refreshTokens,
|
|
getEndSessionUrl,
|
|
generateState,
|
|
generateNonce,
|
|
generateCodeVerifier,
|
|
};
|