@tidecloak/js 0.13.1 → 0.13.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +170 -5
  2. package/dist/cjs/lib/tidecloak.js +1759 -1629
  3. package/dist/cjs/lib/tidecloak.js.map +1 -1
  4. package/dist/cjs/src/IAMService.js +526 -32
  5. package/dist/cjs/src/IAMService.js.map +1 -1
  6. package/dist/cjs/src/index.js +4 -21
  7. package/dist/cjs/src/index.js.map +1 -1
  8. package/dist/cjs/src/policy-react.js +3 -0
  9. package/dist/cjs/src/policy-react.js.map +1 -0
  10. package/dist/cjs/src/policy.css +1 -0
  11. package/dist/cjs/src/utils/fetch.js +36 -0
  12. package/dist/cjs/src/utils/fetch.js.map +1 -0
  13. package/dist/cjs/src/utils/index.js +3 -0
  14. package/dist/cjs/src/utils/index.js.map +1 -0
  15. package/dist/cjs/src/utils/pkce.js +43 -0
  16. package/dist/cjs/src/utils/pkce.js.map +1 -0
  17. package/dist/esm/lib/tidecloak.js +1760 -1619
  18. package/dist/esm/lib/tidecloak.js.map +1 -1
  19. package/dist/esm/src/IAMService.js +523 -23
  20. package/dist/esm/src/IAMService.js.map +1 -1
  21. package/dist/esm/src/index.js +2 -6
  22. package/dist/esm/src/index.js.map +1 -1
  23. package/dist/esm/src/policy-react.js +3 -0
  24. package/dist/esm/src/policy-react.js.map +1 -0
  25. package/dist/esm/src/policy.css +1 -0
  26. package/dist/esm/src/utils/fetch.js +36 -0
  27. package/dist/esm/src/utils/fetch.js.map +1 -0
  28. package/dist/esm/src/utils/index.js +3 -0
  29. package/dist/esm/src/utils/index.js.map +1 -0
  30. package/dist/esm/src/utils/pkce.js +43 -0
  31. package/dist/esm/src/utils/pkce.js.map +1 -0
  32. package/dist/types/IAMService.d.ts +328 -0
  33. package/dist/types/index.d.ts +3 -0
  34. package/dist/types/lib/tidecloak.d.ts +325 -31
  35. package/dist/types/policy-react.d.ts +1 -0
  36. package/dist/types/src/IAMService.d.ts +245 -23
  37. package/dist/types/src/index.d.ts +2 -2
  38. package/dist/types/src/policy-react.d.ts +1 -0
  39. package/dist/types/src/utils/fetch.d.ts +11 -0
  40. package/dist/types/src/utils/index.d.ts +2 -0
  41. package/dist/types/src/utils/pkce.d.ts +24 -0
  42. package/dist/types/utils/fetch.d.ts +11 -0
  43. package/dist/types/utils/index.d.ts +2 -0
  44. package/dist/types/utils/pkce.d.ts +24 -0
  45. package/package.json +18 -29
  46. package/scripts/postinstall.cjs +36 -0
  47. package/silent-check-sso.html +1 -0
  48. package/scripts/postinstall.js +0 -43
@@ -1,33 +1,327 @@
1
- export function getHumanReadableObject(modelId: any, data: any, expiry: any): any;
2
- export default TideCloak;
3
- declare function TideCloak(config: any): void;
4
- declare class TideCloak {
5
- constructor(config: any);
1
+ export default class TideCloak {
2
+ /**
3
+ * @param {KeycloakConfig} config
4
+ */
5
+ constructor(config: KeycloakConfig);
6
6
  didInitialize: boolean;
7
- init: (initOptions?: {}) => any;
8
- login: (options: any) => any;
9
- ensureTokenReady: () => Promise<void>;
10
- encrypt: (toEncrypt: any) => Promise<any>;
11
- initEnclave: () => void;
12
- decrypt: (toDecrypt: any) => Promise<any>;
13
- createLoginUrl: (options: any) => Promise<string>;
14
- logout: (options: any) => any;
15
- createLogoutUrl: (options: any) => any;
16
- register: (options: any) => any;
17
- createRegisterUrl: (options: any) => Promise<string>;
18
- createAccountUrl: (options: any) => string | undefined;
19
- accountManagement: () => any;
20
- hasRealmRole: (role: any) => boolean;
21
- hasResourceRole: (role: any, resource: any) => boolean;
22
- loadUserProfile: () => any;
23
- loadUserInfo: () => any;
24
- isTokenExpired: (minValidity: any) => boolean;
25
- updateToken: (minValidity: any) => any;
26
- clearToken: () => void;
27
- checkThresholdRule: (key: any, idSubstring: any, ruleSettings: any, draftJson: any) => any;
28
- createCardanoTxDraft: (txBody: any) => string;
29
- sign: (signModel: any, authFlow: any, draft: any, authorizers: any, ruleSetting: any, expiry: any) => Promise<any>;
30
- signCardanoTx: (txBody: any, authorizers: any, ruleSettings: any, expiry: any) => Promise<string>;
31
- createRuleSettingsDraft: (ruleSettings: any, previousRuleSetting: any, previousRuleSettingCert: any) => string;
7
+ authenticated: boolean;
8
+ loginRequired: boolean;
9
+ /** @type {KeycloakResponseMode} */
10
+ responseMode: KeycloakResponseMode;
11
+ /** @type {KeycloakResponseType} */
12
+ responseType: KeycloakResponseType;
13
+ /** @type {KeycloakFlow} */
14
+ flow: KeycloakFlow;
15
+ /**
16
+ * Matches Keycloak: number | undefined (unset when skew unknown)
17
+ * @type {number | undefined}
18
+ */
19
+ timeSkew: number | undefined;
20
+ /** @type {string=} */
21
+ redirectUri: string | undefined;
22
+ /** @type {string=} */
23
+ silentCheckSsoRedirectUri: string | undefined;
24
+ /** @type {boolean} */
25
+ silentCheckSsoFallback: boolean;
26
+ /** @type {KeycloakPkceMethod} */
27
+ pkceMethod: KeycloakPkceMethod;
28
+ enableLogging: boolean;
29
+ /** @type {'GET' | 'POST'} */
30
+ logoutMethod: "GET" | "POST";
31
+ /** @type {string=} */
32
+ scope: string | undefined;
33
+ /** @type {string | undefined} */
34
+ acrValues: string | undefined;
35
+ messageReceiveTimeout: number;
36
+ /** @type {string=} */
37
+ idToken: string | undefined;
38
+ /** @type {KeycloakTokenParsed=} */
39
+ idTokenParsed: KeycloakTokenParsed | undefined;
40
+ /** @type {string=} */
41
+ token: string | undefined;
42
+ /** @type {KeycloakTokenParsed=} */
43
+ tokenParsed: KeycloakTokenParsed | undefined;
44
+ /** @type {string=} */
45
+ refreshToken: string | undefined;
46
+ /** @type {KeycloakTokenParsed=} */
47
+ refreshTokenParsed: KeycloakTokenParsed | undefined;
48
+ /** @type {string | undefined} */
49
+ doken: string | undefined;
50
+ /** @type {KeycloakTokenParsed | undefined} */
51
+ dokenParsed: KeycloakTokenParsed | undefined;
52
+ /** @type {any} */
53
+ requestEnclave: any;
54
+ /** @type {any} */
55
+ approvalEnclave: any;
56
+ /** @type {string=} */
57
+ clientId: string | undefined;
58
+ /** @type {string=} */
59
+ sessionId: string | undefined;
60
+ /** @type {string=} */
61
+ subject: string | undefined;
62
+ /** @type {string=} */
63
+ authServerUrl: string | undefined;
64
+ /** @type {string=} */
65
+ realm: string | undefined;
66
+ /** @type {KeycloakRoles=} */
67
+ realmAccess: KeycloakRoles | undefined;
68
+ /** @type {KeycloakResourceAccess=} */
69
+ resourceAccess: KeycloakResourceAccess | undefined;
70
+ /** @type {KeycloakProfile=} */
71
+ profile: KeycloakProfile | undefined;
72
+ /** @type {KeycloakUserInfo | undefined} */
73
+ userInfo: KeycloakUserInfo | undefined;
74
+ /** @type {Endpoints} */
75
+ endpoints: Endpoints;
76
+ /** @type {number=} */
77
+ tokenTimeoutHandle: number | undefined;
78
+ /** @type {() => void=} */
79
+ onAuthSuccess: (() => void) | undefined;
80
+ /** @type {(errorData?: KeycloakError) => void=} */
81
+ onAuthError: ((errorData?: KeycloakError) => void) | undefined;
82
+ /** @type {() => void=} */
83
+ onAuthRefreshSuccess: (() => void) | undefined;
84
+ /** @type {() => void=} */
85
+ onAuthRefreshError: (() => void) | undefined;
86
+ /** @type {() => void=} */
87
+ onTokenExpired: (() => void) | undefined;
88
+ /** @type {() => void=} */
89
+ onAuthLogout: (() => void) | undefined;
90
+ /** @type {(authenticated: boolean) => void=} */
91
+ onReady: ((authenticated: boolean) => void) | undefined;
92
+ /** @type {(status: 'success' | 'cancelled' | 'error', action: string) => void=} */
93
+ onActionUpdate: ((status: "success" | "cancelled" | "error", action: string) => void) | undefined;
94
+ /**
95
+ * @param {KeycloakInitOptions} initOptions
96
+ * @returns {Promise<boolean>}
97
+ */
98
+ init(initOptions?: KeycloakInitOptions): Promise<boolean>;
99
+ /**
100
+ * @param {KeycloakLoginOptions} [options]
101
+ * @returns {Promise<void>}
102
+ */
103
+ login(options?: KeycloakLoginOptions): Promise<void>;
104
+ /**
105
+ * Ensure the access token is valid, refreshing if needed.
106
+ * @returns {Promise<void>}
107
+ */
108
+ ensureTokenReady(): Promise<void>;
109
+ /**
110
+ * @param {KeycloakLoginOptions} [options]
111
+ * @returns {Promise<string>}
112
+ */
113
+ createLoginUrl(options?: KeycloakLoginOptions): Promise<string>;
114
+ /**
115
+ * @param {KeycloakLogoutOptions} [options]
116
+ * @returns {Promise<void>}
117
+ */
118
+ logout(options?: KeycloakLogoutOptions): Promise<void>;
119
+ /**
120
+ * @param {KeycloakLogoutOptions} [options]
121
+ * @returns {string}
122
+ */
123
+ createLogoutUrl(options?: KeycloakLogoutOptions): string;
124
+ /**
125
+ * @param {KeycloakRegisterOptions} [options]
126
+ * @returns {Promise<void>}
127
+ */
128
+ register(options?: KeycloakRegisterOptions): Promise<void>;
129
+ /**
130
+ * @param {KeycloakRegisterOptions} [options]
131
+ * @returns {Promise<string>}
132
+ */
133
+ createRegisterUrl(options?: KeycloakRegisterOptions): Promise<string>;
134
+ /**
135
+ * @param {KeycloakAccountOptions} [options]
136
+ * @returns {string}
137
+ */
138
+ createAccountUrl(options?: KeycloakAccountOptions): string;
139
+ /**
140
+ * @returns {Promise<void>}
141
+ */
142
+ accountManagement(): Promise<void>;
143
+ /**
144
+ * @param {string} role
145
+ * @returns {boolean}
146
+ */
147
+ hasRealmRole(role: string): boolean;
148
+ /**
149
+ * @param {string} role
150
+ * @param {string} [resource]
151
+ * @returns {boolean}
152
+ */
153
+ hasResourceRole(role: string, resource?: string): boolean;
154
+ /**
155
+ * @returns {Promise<KeycloakProfile>}
156
+ */
157
+ loadUserProfile(): Promise<KeycloakProfile>;
158
+ /**
159
+ * @returns {Promise<KeycloakUserInfo>}
160
+ */
161
+ loadUserInfo(): Promise<KeycloakUserInfo>;
162
+ /**
163
+ * @param {number} [minValidity]
164
+ * @returns {boolean}
165
+ */
166
+ isTokenExpired(minValidity?: number): boolean;
167
+ /**
168
+ * Matches Keycloak: minValidity is optional.
169
+ * @param {number} [minValidity]
170
+ * @returns {Promise<boolean>}
171
+ */
172
+ updateToken(minValidity?: number): Promise<boolean>;
173
+ clearToken(): void;
174
+ /**
175
+ * Initialize Tide RequestEnclave.
176
+ */
177
+ initRequestEnclave(): void;
178
+ /**
179
+ * Initialize Tide ApprovalEnclave.
180
+ */
181
+ initApprovalEnclave(): void;
182
+ /**
183
+ * Role-based encryption via Tide RequestEnclave.
184
+ * @param {{ data: string | Uint8Array, tags: string[] }[]} toEncrypt
185
+ * @returns {Promise<(string | Uint8Array)[]>}
186
+ */
187
+ encrypt(toEncrypt: {
188
+ data: string | Uint8Array;
189
+ tags: string[];
190
+ }[]): Promise<(string | Uint8Array)[]>;
191
+ /**
192
+ * Initialize a Tide request that requires operator approvals.
193
+ * @param {Uint8Array} encodedRequest
194
+ * @returns {Promise<Uint8Array>}
195
+ */
196
+ createTideRequest(encodedRequest: Uint8Array): Promise<Uint8Array>;
197
+ /**
198
+ * Request Tide operator approval.
199
+ * @param {{id: string, request: Uint8Array}[]} requests
200
+ * @returns {Promise<{approved: {id: string, request: Uint8Array}[], denied: {id: string}[], pending: {id: string}[]}>}
201
+ */
202
+ requestTideOperatorApproval(requests: {
203
+ id: string;
204
+ request: Uint8Array;
205
+ }[]): Promise<{
206
+ approved: {
207
+ id: string;
208
+ request: Uint8Array;
209
+ }[];
210
+ denied: {
211
+ id: string;
212
+ }[];
213
+ pending: {
214
+ id: string;
215
+ }[];
216
+ }>;
217
+ /**
218
+ * Execute a Tide Sign Request
219
+ * @param {Uint8Array} request
220
+ * @param {boolean} [waitForAll=false]
221
+ * @returns {Promise<Array>} Array of signatures
222
+ */
223
+ executeSignRequest(request: Uint8Array, waitForAll?: boolean): Promise<any[]>;
224
+ /**
225
+ * Role-based decryption via Tide RequestEnclave.
226
+ * @param {{ encrypted: string | Uint8Array, tags: string[] }[]} toDecrypt
227
+ * @returns {Promise<(string | Uint8Array)[]>}
228
+ */
229
+ decrypt(toDecrypt: {
230
+ encrypted: string | Uint8Array;
231
+ tags: string[];
232
+ }[]): Promise<(string | Uint8Array)[]>;
233
+ #private;
32
234
  }
33
- export { RequestEnclave, ApprovalEnclave } from "heimdall-tide";
235
+ /**
236
+ * @typedef {Object} NetworkErrorOptionsProperties
237
+ * @property {Response} response
238
+ * @typedef {ErrorOptions & NetworkErrorOptionsProperties} NetworkErrorOptions
239
+ */
240
+ export class NetworkError extends Error {
241
+ /**
242
+ * @param {string} message
243
+ * @param {NetworkErrorOptions} options
244
+ */
245
+ constructor(message: string, options: NetworkErrorOptions);
246
+ /** @type {Response} */
247
+ response: Response;
248
+ }
249
+ /**
250
+ * The JSON version of the adapter configuration.
251
+ */
252
+ export type JsonConfig = {
253
+ /**
254
+ * The URL of the authentication server.
255
+ */
256
+ "auth-server-url": string;
257
+ /**
258
+ * The name of the realm.
259
+ */
260
+ realm: string;
261
+ /**
262
+ * The name of the resource, usually the client ID.
263
+ */
264
+ resource: string;
265
+ };
266
+ /**
267
+ * The successful token response from the authorization server, based on the {@link https://datatracker.ietf.org/doc/html/rfc6749#section-5.1 OAuth 2.0 Authorization Framework specification}.
268
+ */
269
+ export type AccessTokenResponse = {
270
+ /**
271
+ * The access token issued by the authorization server.
272
+ */
273
+ access_token: string;
274
+ /**
275
+ * The type of the token issued by the authorization server.
276
+ */
277
+ token_type: string;
278
+ /**
279
+ * The lifetime in seconds of the access token.
280
+ */
281
+ expires_in?: number | undefined;
282
+ /**
283
+ * The refresh token issued by the authorization server.
284
+ */
285
+ refresh_token?: string | undefined;
286
+ /**
287
+ * The ID token issued by the authorization server, if requested.
288
+ */
289
+ id_token?: string | undefined;
290
+ /**
291
+ * The scope of the access token.
292
+ */
293
+ scope?: string | undefined;
294
+ };
295
+ export type Endpoints = {
296
+ authorize: () => string;
297
+ token: () => string;
298
+ logout: () => string;
299
+ checkSessionIframe: () => string;
300
+ thirdPartyCookiesIframe?: (() => string) | undefined;
301
+ register: () => string;
302
+ userinfo: () => string;
303
+ };
304
+ export type LoginIframe = {
305
+ enable: boolean;
306
+ callbackList: ((error: Error | null, value?: boolean) => void)[];
307
+ interval: number;
308
+ iframe?: HTMLIFrameElement | undefined;
309
+ iframeOrigin?: string | undefined;
310
+ };
311
+ export type CallbackState = {
312
+ state: string;
313
+ nonce: string;
314
+ redirectUri: string;
315
+ loginOptions?: KeycloakLoginOptions;
316
+ prompt?: KeycloakLoginOptions["prompt"];
317
+ pkceCodeVerifier?: string | undefined;
318
+ };
319
+ export type CallbackStorage = {
320
+ get: (state?: string) => CallbackState | null;
321
+ add: (state: CallbackState) => void;
322
+ };
323
+ export type NetworkErrorOptionsProperties = {
324
+ response: Response;
325
+ };
326
+ export type NetworkErrorOptions = ErrorOptions & NetworkErrorOptionsProperties;
327
+ export { RequestEnclave, ApprovalEnclave, ApprovalEnclaveNew, TideMemory, BaseTideRequest, PolicySignRequest, Policy, PolicyParameters } from "heimdall-tide";
@@ -0,0 +1 @@
1
+ export {};
@@ -4,9 +4,16 @@ declare const IAMServiceInstance: IAMService;
4
4
  /**
5
5
  * Singleton IAMService wrapping the TideCloak client.
6
6
  *
7
+ * Supports two modes:
8
+ * - **Front-channel mode**: Browser handles all token operations (standard OIDC)
9
+ * - **Hybrid/BFF mode**: Browser handles PKCE, backend exchanges code for tokens (more secure)
10
+ *
11
+ * ---
12
+ * ## Front-channel Mode
13
+ *
7
14
  * Usage A: pass an onReady callback directly
8
15
  * ```js
9
- * import { IAMService } from 'tidecloak-js';
16
+ * import { IAMService } from '@tidecloak/js';
10
17
  * import tidecloakConfig from './tidecloakAdapter.json';
11
18
  *
12
19
  * IAMService.initIAM(tidecloakConfig, authenticated => {
@@ -22,11 +29,99 @@ declare const IAMServiceInstance: IAMService;
22
29
  *
23
30
  * await IAMService.initIAM(tidecloakConfig);
24
31
  * ```
32
+ *
33
+ * ---
34
+ * ## Hybrid/BFF Mode (Backend-For-Frontend)
35
+ *
36
+ * In hybrid mode, the browser generates PKCE and redirects to the IdP, but the
37
+ * backend exchanges the authorization code for tokens. This keeps tokens server-side
38
+ * for improved security.
39
+ *
40
+ * ### Config shape:
41
+ * ```js
42
+ * const hybridConfig = {
43
+ * authMode: "hybrid",
44
+ * oidc: {
45
+ * authorizationEndpoint: "https://auth.example.com/realms/myrealm/protocol/openid-connect/auth",
46
+ * clientId: "my-client",
47
+ * redirectUri: "https://app.example.com/auth/callback",
48
+ * scope: "openid profile email", // optional, defaults to "openid profile email"
49
+ * prompt: "login" // optional
50
+ * },
51
+ * tokenExchange: {
52
+ * endpoint: "/api/authenticate", // Backend endpoint that exchanges code for tokens
53
+ * provider: "tidecloak-auth", // optional, defaults to "tidecloak-auth"
54
+ * headers: () => ({ // optional, custom headers (e.g., CSRF token)
55
+ * "anti-csrf-token": getCSRFToken()
56
+ * })
57
+ * }
58
+ * };
59
+ * ```
60
+ *
61
+ * ### Login Page:
62
+ * ```js
63
+ * // Load config and trigger login
64
+ * await IAMService.loadConfig(hybridConfig);
65
+ * IAMService.doLogin("/dashboard"); // returnUrl after successful auth
66
+ * ```
67
+ *
68
+ * ### Redirect/Callback Page:
69
+ * ```js
70
+ * // initIAM handles the callback automatically - exchanges code for tokens via backend
71
+ * const authenticated = await IAMService.initIAM(hybridConfig);
72
+ * if (authenticated) {
73
+ * const returnUrl = IAMService.getReturnUrl() || "/";
74
+ * window.location.assign(returnUrl);
75
+ * }
76
+ * ```
77
+ *
78
+ * ### Token Exchange Request Format:
79
+ * The backend endpoint receives a POST request with:
80
+ * ```json
81
+ * {
82
+ * "accessToken": "{\"code\":\"...\",\"code_verifier\":\"...\",\"redirect_uri\":\"...\"}",
83
+ * "provider": "tidecloak-auth"
84
+ * }
85
+ * ```
86
+ *
87
+ * ---
88
+ * ## Events
89
+ * - `ready` - Emitted when initialization completes (with authenticated boolean)
90
+ * - `initError` - Emitted when initialization fails
91
+ * - `authSuccess` - Emitted on successful authentication
92
+ * - `authError` - Emitted on authentication failure
93
+ * - `authRefreshSuccess` - Emitted when token refresh succeeds (front-channel only)
94
+ * - `authRefreshError` - Emitted when token refresh fails (front-channel only)
95
+ * - `logout` - Emitted on logout
96
+ * - `tokenExpired` - Emitted when token expires (front-channel only)
25
97
  */
26
98
  declare class IAMService {
27
99
  _tc: TideCloak | null;
28
100
  _config: Object | null;
29
101
  _listeners: {};
102
+ _hybridAuthenticated: boolean;
103
+ _hybridReturnUrl: string | null;
104
+ _hybridCallbackHandled: boolean;
105
+ _hybridCallbackPromise: any;
106
+ _cachedCallbackData: {
107
+ isCallback: boolean;
108
+ code: string;
109
+ verifier: string;
110
+ redirectUri: any;
111
+ returnUrl: string;
112
+ provider: any;
113
+ error: string | null;
114
+ errorDescription: string | null;
115
+ } | {
116
+ isCallback: boolean;
117
+ code: string;
118
+ verifier: string;
119
+ redirectUri: any;
120
+ returnUrl: string;
121
+ provider: any;
122
+ error: null;
123
+ errorDescription: null;
124
+ } | null;
30
125
  /**
31
126
  * Register an event listener.
32
127
  * @param {'ready'|'initError'|'authSuccess'|'authError'|'authRefreshSuccess'|'authRefreshError'|'logout'|'tokenExpired'} event
@@ -43,6 +138,11 @@ declare class IAMService {
43
138
  off(event: string, handler: Function): this;
44
139
  /** @private */
45
140
  private _emit;
141
+ /**
142
+ * Check if running in hybrid mode.
143
+ * @returns {boolean}
144
+ */
145
+ isHybridMode(): boolean;
46
146
  /**
47
147
  * Load TideCloak configuration and instantiate the client once.
48
148
  * @param {Object} config - TideCloak configuration object.
@@ -51,6 +151,7 @@ declare class IAMService {
51
151
  loadConfig(config: Object): Promise<Object | null>;
52
152
  /**
53
153
  * Initialize the TideCloak SSO client with silent SSO check.
154
+ * In hybrid mode, handles the redirect callback if present.
54
155
  * @param {Object} config - TideCloak configuration object.
55
156
  * @param {Function} [onReady] - Optional callback for the 'ready' event.
56
157
  * @returns {Promise<boolean>} true if authenticated, else false.
@@ -60,47 +161,168 @@ declare class IAMService {
60
161
  private getTideCloakClient;
61
162
  /** @returns {Object} Loaded config */
62
163
  getConfig(): Object;
63
- /** @returns {boolean} Whether there's a valid token */
164
+ /** @returns {boolean} Whether there's a valid token (or session in hybrid mode) */
64
165
  isLoggedIn(): boolean;
65
- /** @returns {Promise<string>} Valid token (refreshing if needed) */
166
+ /**
167
+ * Get valid token (refreshing if needed).
168
+ * @returns {Promise<string>}
169
+ * @throws {Error} In hybrid mode (tokens are server-side)
170
+ */
66
171
  getToken(): Promise<string>;
67
- /** Seconds until token expiry */
172
+ /**
173
+ * Seconds until token expiry.
174
+ * @returns {number}
175
+ * @throws {Error} In hybrid mode (tokens are server-side)
176
+ */
68
177
  getTokenExp(): number;
69
- /** @returns {string} ID token */
178
+ /**
179
+ * Get ID token.
180
+ * @returns {string}
181
+ * @throws {Error} In hybrid mode (tokens are server-side)
182
+ */
70
183
  getIDToken(): string;
71
- /** @returns {string} Username (preferred_username claim) */
184
+ /**
185
+ * Get username (preferred_username claim).
186
+ * @returns {string}
187
+ * @throws {Error} In hybrid mode (tokens are server-side)
188
+ */
72
189
  getName(): string;
73
190
  /**
74
- * @param {string} role - the name of the role to check
75
- * @returns {boolean} Whether the user has a given realm role */
191
+ * Get the return URL after successful hybrid authentication.
192
+ * Only available in hybrid mode after successful auth.
193
+ * @returns {string|null}
194
+ */
195
+ getReturnUrl(): string | null;
196
+ /**
197
+ * Check if user has a realm role.
198
+ * @param {string} role - the name of the role to check
199
+ * @returns {boolean} Whether the user has a given realm role
200
+ * @throws {Error} In hybrid mode (role checks not available client-side)
201
+ */
76
202
  hasRealmRole(role: string): boolean;
77
203
  /**
204
+ * Check if user has a client role.
78
205
  * @param {string} role - the name of the role to check
79
206
  * @param {string} [client] - optional client-ID (defaults to the configured adapter resource)
80
207
  * @returns {boolean} - whether the user has that role
208
+ * @throws {Error} In hybrid mode (role checks not available client-side)
81
209
  */
82
210
  hasClientRole(role: string, client?: string): boolean;
83
211
  /**
212
+ * Get custom claim from access token.
84
213
  * @param {string} key - The name of the claim to retrieve from the Access token's payload.
85
- * @returns {*} Custom claim from access token */
214
+ * @returns {*} Custom claim from access token
215
+ * @throws {Error} In hybrid mode (tokens are server-side)
216
+ */
86
217
  getValueFromToken(key: string): any;
87
218
  /**
219
+ * Get custom claim from ID token.
88
220
  * @param {string} key - The name of the claim to retrieve from the ID token's payload.
89
- * @returns {*} Custom claim from access token */
221
+ * @returns {*} Custom claim from ID token
222
+ * @throws {Error} In hybrid mode (tokens are server-side)
223
+ */
90
224
  getValueFromIDToken(key: string): any;
91
- /** Refreshes token if expired or about to expire */
92
- updateIAMToken(): Promise<any>;
93
- /** Force immediate refresh (min validity = -1) */
94
- forceUpdateToken(): Promise<any>;
95
- /** Start login redirect */
96
- doLogin(): void;
97
- /** Encrypt data via adapter */
98
- doEncrypt(data: any): Promise<any>;
99
- /** Decrypt data via adapter */
100
- doDecrypt(data: any): Promise<any>;
101
- /** Logout, clear cookie, then redirect */
225
+ /**
226
+ * Refreshes token if expired or about to expire.
227
+ * @returns {Promise<boolean>}
228
+ * @throws {Error} In hybrid mode (token refresh handled server-side)
229
+ */
230
+ updateIAMToken(): Promise<boolean>;
231
+ /**
232
+ * Force immediate refresh (min validity = -1).
233
+ * @returns {Promise<boolean>}
234
+ * @throws {Error} In hybrid mode (token refresh handled server-side)
235
+ */
236
+ forceUpdateToken(): Promise<boolean>;
237
+ /**
238
+ * Start login redirect.
239
+ * In hybrid mode, initiates PKCE flow and redirects to IdP.
240
+ * @param {string} [returnUrl] - URL to redirect to after successful auth (hybrid mode only)
241
+ */
242
+ doLogin(returnUrl?: string): Promise<void> | undefined;
243
+ /**
244
+ * Encrypt data via adapter.
245
+ * Not available in hybrid mode (encryption requires client-side doken).
246
+ */
247
+ doEncrypt(data: any): Promise<(string | Uint8Array<ArrayBufferLike>)[]>;
248
+ /**
249
+ * Decrypt data via adapter.
250
+ * Not available in hybrid mode (decryption requires client-side doken).
251
+ */
252
+ doDecrypt(data: any): Promise<(string | Uint8Array<ArrayBufferLike>)[]>;
253
+ /**
254
+ * Logout, clear cookie/session, then redirect.
255
+ * In hybrid mode, clears local state and emits logout event.
256
+ */
102
257
  doLogout(): void;
103
- /** Base URL for Tidecloak realm (no trailing slash) */
258
+ /**
259
+ * Base URL for Tidecloak realm (no trailing slash).
260
+ * In hybrid mode returns empty string.
261
+ */
104
262
  getBaseUrl(): any;
263
+ /**
264
+ * Start hybrid login flow: generate PKCE, store verifier, redirect to IdP.
265
+ * @private
266
+ * @param {string} returnUrl - URL to redirect to after successful auth
267
+ */
268
+ private _startHybridLogin;
269
+ /**
270
+ * Get hybrid callback data for custom token exchange.
271
+ * Use this when you need to handle token exchange with your own auth system
272
+ * (e.g., using a custom useLogin hook) instead of IAMService's built-in fetchJson.
273
+ *
274
+ * @param {Object} [opts] - Options
275
+ * @param {boolean} [opts.clearStorage=true] - Whether to clear sessionStorage after reading
276
+ * @param {string} [opts.redirectUri] - Override redirect URI (use when config isn't loaded)
277
+ * @param {string} [opts.provider] - Override provider (defaults to "tidecloak-auth")
278
+ * @returns {{
279
+ * isCallback: boolean,
280
+ * code: string,
281
+ * verifier: string,
282
+ * redirectUri: string,
283
+ * returnUrl: string,
284
+ * provider: string,
285
+ * error: string|null,
286
+ * errorDescription: string|null
287
+ * }}
288
+ *
289
+ * @example
290
+ * // With redirectUri override (recommended for callback pages after full navigation)
291
+ * const data = IAMService.getHybridCallbackData({
292
+ * redirectUri: process.env.KEYCLOAK_REDIRECTURI,
293
+ * });
294
+ * if (data.isCallback && data.code && data.verifier) {
295
+ * login.execute({
296
+ * accessToken: JSON.stringify({
297
+ * code: data.code,
298
+ * code_verifier: data.verifier,
299
+ * redirect_uri: data.redirectUri,
300
+ * }),
301
+ * provider: data.provider,
302
+ * });
303
+ * }
304
+ */
305
+ getHybridCallbackData(opts?: {
306
+ clearStorage?: boolean | undefined;
307
+ redirectUri?: string | undefined;
308
+ provider?: string | undefined;
309
+ }): {
310
+ isCallback: boolean;
311
+ code: string;
312
+ verifier: string;
313
+ redirectUri: string;
314
+ returnUrl: string;
315
+ provider: string;
316
+ error: string | null;
317
+ errorDescription: string | null;
318
+ };
319
+ /**
320
+ * Handle hybrid redirect callback: exchange code for tokens via backend endpoint.
321
+ * @private
322
+ * @param {Object} opts - Options
323
+ * @param {string} [opts.onMissingVerifierRedirectTo] - URL to redirect if verifier is missing
324
+ * @returns {Promise<{handled: boolean, authenticated: boolean, returnUrl: string}>}
325
+ */
326
+ private _handleHybridRedirectCallback;
105
327
  }
106
- import TideCloak from "../lib/tidecloak";
328
+ import TideCloak from "../lib/tidecloak.js";