api.ts (3902B)
1 export interface RealmInfo { 2 slug: string; 3 display_name: string; 4 branding: Record<string, string>; 5 allow_session_duration_choice: boolean; 6 } 7 8 export interface ClientDiscovery { 9 issuer: string; 10 client_id: string; 11 client_name: string; 12 realm: string; 13 display_name: string; 14 branding: Record<string, string>; 15 access_mode: string; 16 virtual: boolean; 17 require_pkce: boolean; 18 allow_session_duration_choice: boolean; 19 } 20 21 export interface LoginResolution { 22 realm: RealmInfo; 23 next: 'password' | 'oidc'; 24 provider?: { 25 slug: string; 26 display_name: string; 27 }; 28 } 29 30 export interface PasswordLoginResult { 31 realm: RealmInfo; 32 user: { 33 id: string; 34 username: string; 35 display_name: string | null; 36 }; 37 expires_at: number; 38 pending_auth_request: boolean; 39 } 40 41 export interface SessionScope { 42 type: 'global' | 'realm' | 'client'; 43 host: string; 44 realm_id: string | null; 45 client_id: string | null; 46 } 47 48 export interface SessionAccount { 49 public_id: string; 50 active: boolean; 51 user: { 52 id: string; 53 username: string; 54 display_name: string | null; 55 }; 56 created_at: number; 57 expires_at: number; 58 } 59 60 export interface SessionInfo { 61 scope: SessionScope; 62 realm: RealmInfo | null; 63 user: { 64 id: string; 65 username: string; 66 display_name: string | null; 67 }; 68 roles: string[]; 69 is_system_admin: boolean; 70 expires_at: number; 71 accounts: SessionAccount[]; 72 } 73 74 export interface SelectSessionResult { 75 ok: boolean; 76 user: { 77 id: string; 78 username: string; 79 display_name: string | null; 80 }; 81 expires_at: number; 82 pending_auth_request: boolean; 83 } 84 85 export interface ApiError extends Error { 86 code?: string; 87 status?: number; 88 } 89 90 async function request<T>(path: string, options: RequestInit = {}): Promise<T> { 91 const response = await fetch(path, { 92 credentials: 'same-origin', 93 headers: { 94 'content-type': 'application/json', 95 }, 96 ...options, 97 }); 98 99 let payload: any = null; 100 try { 101 payload = await response.json(); 102 } catch { 103 payload = null; 104 } 105 106 if (!response.ok) { 107 const error = new Error(payload?.error?.message || response.statusText) as ApiError; 108 error.code = payload?.error?.code; 109 error.status = response.status; 110 throw error; 111 } 112 113 return payload as T; 114 } 115 116 export function getRealm(realm?: string | null): Promise<RealmInfo> { 117 const query = realm ? `?realm=${encodeURIComponent(realm)}` : ''; 118 return request<RealmInfo>(`/api/v1/realm${query}`); 119 } 120 121 export function getClientDiscovery(clientId?: string | null): Promise<ClientDiscovery> { 122 if (clientId) { 123 return request<ClientDiscovery>(`/oidc/${encodeURIComponent(clientId)}/openid-configuration`); 124 } 125 return request<ClientDiscovery>('/oidc/openid-configuration'); 126 } 127 128 export function resolveLogin( 129 clientId: string, 130 identifier: string 131 ): Promise<LoginResolution> { 132 return request<LoginResolution>('/api/v1/login/resolve', { 133 method: 'POST', 134 body: JSON.stringify({ client_id: clientId, identifier }), 135 }); 136 } 137 138 export function signInPassword( 139 clientId: string, 140 identifier: string, 141 password: string, 142 longSession: boolean 143 ): Promise<PasswordLoginResult> { 144 return request<PasswordLoginResult>('/api/v1/login/password', { 145 method: 'POST', 146 body: JSON.stringify({ 147 client_id: clientId, 148 identifier, 149 password, 150 long_session: longSession, 151 }), 152 }); 153 } 154 155 export function getSession(): Promise<SessionInfo> { 156 return request<SessionInfo>('/api/v1/session'); 157 } 158 159 export function selectSession( 160 publicId: string, 161 clientId?: string | null 162 ): Promise<SelectSessionResult> { 163 return request<SelectSessionResult>('/api/v1/session/select', { 164 method: 'POST', 165 body: JSON.stringify({ 166 public_id: publicId, 167 client_id: clientId || undefined, 168 }), 169 }); 170 } 171 172 export function signOut(): Promise<{ ok: boolean }> { 173 return request('/api/v1/logout', { method: 'POST' }); 174 }