add auth moodule

This commit is contained in:
2026-04-18 13:43:13 +01:00
parent f163f22987
commit ba17904895
10 changed files with 131 additions and 106 deletions
+21
View File
@@ -0,0 +1,21 @@
/**
* KEYCLOAK CONFIGURATION
* Logic: Environment variable validation and OIDC configuration.
*/
export const keycloakConfig = {
clientId: process.env.KEYCLOAK_CLIENT_ID!,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
issuer: `${process.env.KEYCLOAK_ISSUER_URL}/realms/${process.env.KEYCLOAK_REALM}`,
// Scopes needed for OIDC and profile access
scope: 'openid profile email',
// Endpoint for global logout
endSessionEndpoint: `${process.env.KEYCLOAK_ISSUER_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/logout`,
};
// Simple check to ensure environment variables are present
if (!process.env.KEYCLOAK_CLIENT_ID || !process.env.KEYCLOAK_ISSUER_URL) {
console.warn("Auth Warning: Keycloak environment variables are missing.");
}
+21
View File
@@ -0,0 +1,21 @@
/**
* SESSION MAPPER
* Logic: Data transformation (JWT -> Clean Profile).
* Purpose: Prevents leaking sensitive JWT metadata to the UI layer.
*/
import { UserProfile, RawKeycloakToken } from '../../types/auth.types';
export const mapKeycloakProfile = (token: RawKeycloakToken): UserProfile => {
return {
id: token.sub,
name: token.name || 'Guest User',
email: token.email,
username: token.preferred_username,
// Extracting roles for domain-specific logic (e.g., Editor, Admin)
roles: token.realm_access?.roles || [],
avatar: token.picture || null,
// Custom logic to check for premium status
isPremium: token.realm_access?.roles.includes('premium_subscriber') ?? false,
};
};
+41
View File
@@ -0,0 +1,41 @@
/**
* TOKEN REFRESHER
* Logic: Silent background token rotation.
* Role: Communicates with Keycloak to exchange a Refresh Token for a new Access Token.
*/
import { keycloakConfig } from './keycloak-config';
export const refreshAccessToken = async (token: any) => {
try {
const url = `${process.env.KEYCLOAK_ISSUER_URL}/realms/${process.env.KEYCLOAK_REALM}/protocol/openid-connect/token`;
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: keycloakConfig.clientId,
client_secret: keycloakConfig.clientSecret,
grant_type: 'refresh_token',
refresh_token: token.refreshToken,
}),
});
const refreshedTokens = await response.json();
if (!response.ok) throw refreshedTokens;
return {
...token,
accessToken: refreshedTokens.access_token,
accessTokenExpires: Date.now() + refreshedTokens.expires_in * 1000,
refreshToken: refreshedTokens.refresh_token ?? token.refreshToken, // Fallback to old refresh token
};
} catch (error) {
console.error('Error refreshing access token', error);
return {
...token,
error: 'RefreshAccessTokenError',
};
}
};