import type { KeycloakConfig, TokenResponse, UserInfo } from '~/utils/types'; export class KeycloakClient { private config: KeycloakConfig; constructor(config: KeycloakConfig) { this.config = config; } getAuthUrl(state: string): string { const params = new URLSearchParams({ client_id: this.config.clientId, redirect_uri: this.config.callbackUrl, response_type: 'code', scope: 'openid email profile', state, }); return `${this.config.issuer}/protocol/openid-connect/auth?${params}`; } async exchangeCodeForTokens(code: string): Promise { const response = await fetch(`${this.config.issuer}/protocol/openid-connect/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'authorization_code', client_id: this.config.clientId, client_secret: this.config.clientSecret, code, redirect_uri: this.config.callbackUrl, }), }); if (!response.ok) { throw new Error(`Token exchange failed: ${response.statusText}`); } return response.json(); } async getUserInfo(accessToken: string): Promise { const response = await fetch(`${this.config.issuer}/protocol/openid-connect/userinfo`, { headers: { Authorization: `Bearer ${accessToken}`, }, }); if (!response.ok) { throw new Error(`Failed to get user info: ${response.statusText}`); } return response.json(); } async refreshToken(refreshToken: string): Promise { const response = await fetch(`${this.config.issuer}/protocol/openid-connect/token`, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ grant_type: 'refresh_token', client_id: this.config.clientId, client_secret: this.config.clientSecret, refresh_token: refreshToken, }), }); if (!response.ok) { throw new Error(`Token refresh failed: ${response.statusText}`); } return response.json(); } } export function createKeycloakClient(): KeycloakClient { const config = useRuntimeConfig(); return new KeycloakClient(config.keycloak); }