najm-auth 1.1.15 → 1.1.17
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.
- package/dist/{FetchClient-DbrTaCVq.d.ts → FetchClient-aatsED1a.d.ts} +12 -3
- package/dist/{NajmAuthClient-C6yy4mxL.d.ts → NajmAuthClient-FCwA8LIg.d.ts} +15 -3
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.js +65 -14
- package/dist/client/react/index.d.ts +234 -17
- package/dist/client/react/index.js +268 -36
- package/dist/client/server/index.d.ts +1 -1
- package/dist/client/server/index.js +1 -1
- package/dist/index.d.ts +55 -6
- package/dist/index.js +197 -33
- package/dist/schema/mysql.d.ts +68 -0
- package/dist/schema/mysql.js +10 -4
- package/dist/schema/pg.d.ts +68 -0
- package/dist/schema/pg.js +13 -7
- package/dist/schema/sqlite.d.ts +72 -0
- package/dist/schema/sqlite.js +10 -4
- package/package.json +2 -1
|
@@ -26,6 +26,7 @@ interface AuthState {
|
|
|
26
26
|
interface DecodedToken {
|
|
27
27
|
userId: string;
|
|
28
28
|
jti?: string;
|
|
29
|
+
sessionVersion?: number;
|
|
29
30
|
roles?: string[];
|
|
30
31
|
permissions?: string[];
|
|
31
32
|
exp?: number;
|
|
@@ -79,8 +80,16 @@ interface AuthClientConfig {
|
|
|
79
80
|
/**
|
|
80
81
|
* Auth event types
|
|
81
82
|
*/
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
interface AuthEventMap {
|
|
84
|
+
login: AuthUser;
|
|
85
|
+
logout: null;
|
|
86
|
+
tokenRefresh: null;
|
|
87
|
+
sessionExpired: null;
|
|
88
|
+
stateChange: AuthState;
|
|
89
|
+
userUpdated: AuthUser;
|
|
90
|
+
}
|
|
91
|
+
type AuthEvent = keyof AuthEventMap;
|
|
92
|
+
type AuthEventHandler<K extends AuthEvent = AuthEvent> = (data: AuthEventMap[K]) => void;
|
|
84
93
|
/**
|
|
85
94
|
* Tab sync message types
|
|
86
95
|
*/
|
|
@@ -142,4 +151,4 @@ declare class FetchClient {
|
|
|
142
151
|
private parseBody;
|
|
143
152
|
}
|
|
144
153
|
|
|
145
|
-
export { AuthError as A, type DecodedToken as D, FetchClient as F, type RetryConfig as R, type SyncPayload as S, type TabSyncMessage as T, type AuthClientConfig as a, type
|
|
154
|
+
export { AuthError as A, type DecodedToken as D, FetchClient as F, type RetryConfig as R, type SyncPayload as S, type TabSyncMessage as T, type AuthClientConfig as a, type AuthEventMap as b, type AuthState as c, type AuthUser as d, type AuthEvent as e, type AuthEventHandler as f, type ServerResponse as g, type TokenPair as h, type RequestOptions as i };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { F as FetchClient, a as AuthClientConfig,
|
|
1
|
+
import { F as FetchClient, a as AuthClientConfig, d as AuthUser, c as AuthState, e as AuthEvent, f as AuthEventHandler } from './FetchClient-aatsED1a.js';
|
|
2
2
|
|
|
3
3
|
interface HydrateSession {
|
|
4
4
|
user: AuthUser | null;
|
|
@@ -8,10 +8,14 @@ interface HydrateSession {
|
|
|
8
8
|
}
|
|
9
9
|
declare class NajmAuthClient {
|
|
10
10
|
private config;
|
|
11
|
+
private static readonly MAX_REFRESH_FAILURES;
|
|
12
|
+
private static readonly CIRCUIT_RESET_MS;
|
|
11
13
|
private state;
|
|
12
14
|
private refreshTimer;
|
|
15
|
+
private refreshCircuitTimer;
|
|
13
16
|
private refreshPromise;
|
|
14
17
|
private fetchUserPromise;
|
|
18
|
+
private refreshFailures;
|
|
15
19
|
private _hydrated;
|
|
16
20
|
private listeners;
|
|
17
21
|
private eventListeners;
|
|
@@ -32,6 +36,10 @@ declare class NajmAuthClient {
|
|
|
32
36
|
forgotPassword(data: {
|
|
33
37
|
email: string;
|
|
34
38
|
}): Promise<void>;
|
|
39
|
+
changePassword(data: {
|
|
40
|
+
currentPassword: string;
|
|
41
|
+
newPassword: string;
|
|
42
|
+
}): Promise<void>;
|
|
35
43
|
resetPassword(data: {
|
|
36
44
|
token: string;
|
|
37
45
|
newPassword: string;
|
|
@@ -51,15 +59,19 @@ declare class NajmAuthClient {
|
|
|
51
59
|
*/
|
|
52
60
|
hydrate(session: HydrateSession | null): void;
|
|
53
61
|
isHydrated(): boolean;
|
|
54
|
-
on<
|
|
55
|
-
off(event:
|
|
62
|
+
on<K extends AuthEvent>(event: K, handler: AuthEventHandler<K>): void;
|
|
63
|
+
off<K extends AuthEvent>(event: K, handler: AuthEventHandler<K>): void;
|
|
56
64
|
subscribe(listener: (state: AuthState) => void): () => void;
|
|
57
65
|
destroy(): void;
|
|
66
|
+
private _refreshWithCircuit;
|
|
58
67
|
private _doRefresh;
|
|
59
68
|
private handleUnauthorized;
|
|
60
69
|
private applyTokens;
|
|
61
70
|
private scheduleRefresh;
|
|
62
71
|
private clearRefreshTimer;
|
|
72
|
+
private clearRefreshCircuitTimer;
|
|
73
|
+
private resetRefreshFailures;
|
|
74
|
+
private registerRefreshFailure;
|
|
63
75
|
private resetState;
|
|
64
76
|
private getSyncPayload;
|
|
65
77
|
private handleTabMessage;
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { N as NajmAuthClient, c as createAuthClient } from '../NajmAuthClient-
|
|
2
|
-
import { D as DecodedToken, T as TabSyncMessage, S as SyncPayload } from '../FetchClient-
|
|
3
|
-
export { a as AuthClientConfig, A as AuthError,
|
|
1
|
+
export { H as HydrateSession, N as NajmAuthClient, c as createAuthClient } from '../NajmAuthClient-FCwA8LIg.js';
|
|
2
|
+
import { D as DecodedToken, T as TabSyncMessage, S as SyncPayload } from '../FetchClient-aatsED1a.js';
|
|
3
|
+
export { a as AuthClientConfig, A as AuthError, e as AuthEvent, f as AuthEventHandler, b as AuthEventMap, c as AuthState, d as AuthUser, F as FetchClient, i as RequestOptions, R as RetryConfig, g as ServerResponse, h as TokenPair } from '../FetchClient-aatsED1a.js';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Decode a JWT token payload without verification.
|
package/dist/client/index.js
CHANGED
|
@@ -199,7 +199,7 @@ var INITIAL_STATE = {
|
|
|
199
199
|
roles: [],
|
|
200
200
|
permissions: []
|
|
201
201
|
};
|
|
202
|
-
var NajmAuthClient = class {
|
|
202
|
+
var NajmAuthClient = class _NajmAuthClient {
|
|
203
203
|
constructor(config) {
|
|
204
204
|
this.config = config;
|
|
205
205
|
this.prefix = config.authPrefix ?? "/auth";
|
|
@@ -220,10 +220,14 @@ var NajmAuthClient = class {
|
|
|
220
220
|
static {
|
|
221
221
|
__name(this, "NajmAuthClient");
|
|
222
222
|
}
|
|
223
|
+
static MAX_REFRESH_FAILURES = 3;
|
|
224
|
+
static CIRCUIT_RESET_MS = 6e4;
|
|
223
225
|
state = { ...INITIAL_STATE };
|
|
224
226
|
refreshTimer = null;
|
|
227
|
+
refreshCircuitTimer = null;
|
|
225
228
|
refreshPromise = null;
|
|
226
229
|
fetchUserPromise = null;
|
|
230
|
+
refreshFailures = 0;
|
|
227
231
|
_hydrated = false;
|
|
228
232
|
// Subscriptions (for React useSyncExternalStore)
|
|
229
233
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -270,8 +274,11 @@ var NajmAuthClient = class {
|
|
|
270
274
|
this.emit("logout", null);
|
|
271
275
|
}
|
|
272
276
|
async refresh() {
|
|
277
|
+
if (this.refreshFailures >= _NajmAuthClient.MAX_REFRESH_FAILURES) {
|
|
278
|
+
throw new Error("Session expired (circuit open)");
|
|
279
|
+
}
|
|
273
280
|
if (!this.refreshPromise) {
|
|
274
|
-
this.refreshPromise = this.
|
|
281
|
+
this.refreshPromise = this._refreshWithCircuit().finally(() => {
|
|
275
282
|
this.refreshPromise = null;
|
|
276
283
|
});
|
|
277
284
|
}
|
|
@@ -290,8 +297,8 @@ var NajmAuthClient = class {
|
|
|
290
297
|
const res = await this.api.get(`${this.prefix}/me`);
|
|
291
298
|
this.state = { ...this.state, user: res.data };
|
|
292
299
|
this.notify();
|
|
293
|
-
this.emit("userUpdated",
|
|
294
|
-
return
|
|
300
|
+
this.emit("userUpdated", res.data);
|
|
301
|
+
return res.data;
|
|
295
302
|
} catch {
|
|
296
303
|
return null;
|
|
297
304
|
}
|
|
@@ -299,6 +306,12 @@ var NajmAuthClient = class {
|
|
|
299
306
|
async forgotPassword(data) {
|
|
300
307
|
await this.api.post(`${this.prefix}/forgot-password`, { body: data, skipAuth: true });
|
|
301
308
|
}
|
|
309
|
+
async changePassword(data) {
|
|
310
|
+
await this.api.post(`${this.prefix}/change-password`, { body: data });
|
|
311
|
+
this.resetState();
|
|
312
|
+
this.tabSync?.broadcastLogout();
|
|
313
|
+
this.emit("logout", null);
|
|
314
|
+
}
|
|
302
315
|
async resetPassword(data) {
|
|
303
316
|
await this.api.post(`${this.prefix}/reset-password`, { body: data, skipAuth: true });
|
|
304
317
|
}
|
|
@@ -343,6 +356,7 @@ var NajmAuthClient = class {
|
|
|
343
356
|
hydrate(session) {
|
|
344
357
|
if (this._hydrated) return;
|
|
345
358
|
this._hydrated = true;
|
|
359
|
+
this.resetRefreshFailures();
|
|
346
360
|
if (!session || !session.user) {
|
|
347
361
|
this.state = { ...INITIAL_STATE };
|
|
348
362
|
this.notify();
|
|
@@ -392,8 +406,10 @@ var NajmAuthClient = class {
|
|
|
392
406
|
// =========================================================================
|
|
393
407
|
destroy() {
|
|
394
408
|
this.clearRefreshTimer();
|
|
409
|
+
this.clearRefreshCircuitTimer();
|
|
395
410
|
this.tabSync?.destroy();
|
|
396
411
|
this.tabSync = null;
|
|
412
|
+
this.refreshFailures = 0;
|
|
397
413
|
this.state = { ...INITIAL_STATE };
|
|
398
414
|
this.listeners.clear();
|
|
399
415
|
this.eventListeners.clear();
|
|
@@ -401,24 +417,32 @@ var NajmAuthClient = class {
|
|
|
401
417
|
// =========================================================================
|
|
402
418
|
// Internals
|
|
403
419
|
// =========================================================================
|
|
404
|
-
async
|
|
420
|
+
async _refreshWithCircuit() {
|
|
405
421
|
try {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
{ skipAuth: true }
|
|
409
|
-
);
|
|
410
|
-
this.applyTokens(res.data);
|
|
411
|
-
this.tabSync?.broadcastSync(this.getSyncPayload());
|
|
412
|
-
this.emit("tokenRefresh", null);
|
|
422
|
+
await this._doRefresh();
|
|
423
|
+
this.resetRefreshFailures();
|
|
413
424
|
} catch (err) {
|
|
414
|
-
|
|
425
|
+
const shouldOpenCircuit = this.registerRefreshFailure(err);
|
|
426
|
+
if (shouldOpenCircuit) {
|
|
415
427
|
this.resetState();
|
|
416
428
|
this.emit("sessionExpired", null);
|
|
417
|
-
|
|
429
|
+
if (err instanceof AuthError && err.status === 401) {
|
|
430
|
+
throw new Error("Session expired");
|
|
431
|
+
}
|
|
432
|
+
throw new Error("Session expired (circuit open)");
|
|
418
433
|
}
|
|
419
434
|
throw err;
|
|
420
435
|
}
|
|
421
436
|
}
|
|
437
|
+
async _doRefresh() {
|
|
438
|
+
const res = await this.api.post(
|
|
439
|
+
`${this.prefix}/refresh`,
|
|
440
|
+
{ skipAuth: true }
|
|
441
|
+
);
|
|
442
|
+
this.applyTokens(res.data);
|
|
443
|
+
this.tabSync?.broadcastSync(this.getSyncPayload());
|
|
444
|
+
this.emit("tokenRefresh", null);
|
|
445
|
+
}
|
|
422
446
|
async handleUnauthorized() {
|
|
423
447
|
try {
|
|
424
448
|
await this.refresh();
|
|
@@ -428,6 +452,7 @@ var NajmAuthClient = class {
|
|
|
428
452
|
}
|
|
429
453
|
}
|
|
430
454
|
applyTokens(tokens) {
|
|
455
|
+
this.resetRefreshFailures();
|
|
431
456
|
const decoded = decodeToken(tokens.accessToken);
|
|
432
457
|
this.state = {
|
|
433
458
|
...this.state,
|
|
@@ -454,6 +479,32 @@ var NajmAuthClient = class {
|
|
|
454
479
|
this.refreshTimer = null;
|
|
455
480
|
}
|
|
456
481
|
}
|
|
482
|
+
clearRefreshCircuitTimer() {
|
|
483
|
+
if (this.refreshCircuitTimer) {
|
|
484
|
+
clearTimeout(this.refreshCircuitTimer);
|
|
485
|
+
this.refreshCircuitTimer = null;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
resetRefreshFailures() {
|
|
489
|
+
this.refreshFailures = 0;
|
|
490
|
+
this.clearRefreshCircuitTimer();
|
|
491
|
+
}
|
|
492
|
+
registerRefreshFailure(err) {
|
|
493
|
+
if (err instanceof AuthError && err.status === 401) {
|
|
494
|
+
this.refreshFailures = _NajmAuthClient.MAX_REFRESH_FAILURES;
|
|
495
|
+
} else {
|
|
496
|
+
this.refreshFailures += 1;
|
|
497
|
+
}
|
|
498
|
+
if (this.refreshFailures >= _NajmAuthClient.MAX_REFRESH_FAILURES) {
|
|
499
|
+
this.clearRefreshCircuitTimer();
|
|
500
|
+
this.refreshCircuitTimer = setTimeout(() => {
|
|
501
|
+
this.refreshFailures = 0;
|
|
502
|
+
this.refreshCircuitTimer = null;
|
|
503
|
+
}, _NajmAuthClient.CIRCUIT_RESET_MS);
|
|
504
|
+
return true;
|
|
505
|
+
}
|
|
506
|
+
return false;
|
|
507
|
+
}
|
|
457
508
|
resetState() {
|
|
458
509
|
this.clearRefreshTimer();
|
|
459
510
|
this.state = { ...INITIAL_STATE };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode, CSSProperties, ReactElement } from 'react';
|
|
4
|
-
import { N as NajmAuthClient, H as HydrateSession } from '../../NajmAuthClient-
|
|
5
|
-
import {
|
|
4
|
+
import { N as NajmAuthClient, H as HydrateSession } from '../../NajmAuthClient-FCwA8LIg.js';
|
|
5
|
+
import { c as AuthState, d as AuthUser, A as AuthError, e as AuthEvent, b as AuthEventMap } from '../../FetchClient-aatsED1a.js';
|
|
6
6
|
|
|
7
7
|
interface AuthProviderProps {
|
|
8
8
|
client: NajmAuthClient;
|
|
@@ -16,6 +16,8 @@ interface AuthProviderProps {
|
|
|
16
16
|
}
|
|
17
17
|
declare function AuthProvider({ client, children, initialSession }: AuthProviderProps): react_jsx_runtime.JSX.Element;
|
|
18
18
|
|
|
19
|
+
declare function useAuthClient(): NajmAuthClient;
|
|
20
|
+
|
|
19
21
|
/**
|
|
20
22
|
* Subscribe to the full auth state.
|
|
21
23
|
* Re-renders when any part of the state changes.
|
|
@@ -50,6 +52,19 @@ interface UseSessionReturn extends AuthState {
|
|
|
50
52
|
*/
|
|
51
53
|
declare function useSession(opts?: UseSessionOptions): UseSessionReturn;
|
|
52
54
|
|
|
55
|
+
interface UsePermissionsReturn {
|
|
56
|
+
can: (permission: string) => boolean;
|
|
57
|
+
hasRole: (role: string) => boolean;
|
|
58
|
+
hasAnyRole: (roles: string[]) => boolean;
|
|
59
|
+
permissions: string[];
|
|
60
|
+
roles: string[];
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Access RBAC/PBAC checks as hooks.
|
|
64
|
+
* Permissions are decoded from JWT — no round-trip needed.
|
|
65
|
+
*/
|
|
66
|
+
declare function usePermissions(): UsePermissionsReturn;
|
|
67
|
+
|
|
53
68
|
interface UseLoginOptions {
|
|
54
69
|
onSuccess?: (user: AuthUser) => void;
|
|
55
70
|
onError?: (error: AuthError | Error) => void;
|
|
@@ -64,6 +79,17 @@ interface UseLoginReturn {
|
|
|
64
79
|
}
|
|
65
80
|
declare function useLogin(opts?: UseLoginOptions): UseLoginReturn;
|
|
66
81
|
|
|
82
|
+
interface UseRegisterOptions {
|
|
83
|
+
onSuccess?: (user: AuthUser) => void;
|
|
84
|
+
onError?: (error: AuthError | Error) => void;
|
|
85
|
+
}
|
|
86
|
+
interface UseRegisterReturn {
|
|
87
|
+
register: (data: Record<string, unknown>) => Promise<void>;
|
|
88
|
+
isLoading: boolean;
|
|
89
|
+
error: AuthError | Error | null;
|
|
90
|
+
}
|
|
91
|
+
declare function useRegister(opts?: UseRegisterOptions): UseRegisterReturn;
|
|
92
|
+
|
|
67
93
|
interface UseLogoutOptions {
|
|
68
94
|
onSuccess?: () => void;
|
|
69
95
|
onError?: (error: Error) => void;
|
|
@@ -74,29 +100,89 @@ interface UseLogoutReturn {
|
|
|
74
100
|
}
|
|
75
101
|
declare function useLogout(opts?: UseLogoutOptions): UseLogoutReturn;
|
|
76
102
|
|
|
77
|
-
interface
|
|
78
|
-
onSuccess?: (
|
|
103
|
+
interface UseForgotPasswordOptions {
|
|
104
|
+
onSuccess?: () => void;
|
|
79
105
|
onError?: (error: AuthError | Error) => void;
|
|
80
106
|
}
|
|
81
|
-
interface
|
|
82
|
-
|
|
107
|
+
interface UseForgotPasswordReturn {
|
|
108
|
+
forgotPassword: (data: {
|
|
109
|
+
email: string;
|
|
110
|
+
}) => Promise<void>;
|
|
83
111
|
isLoading: boolean;
|
|
84
112
|
error: AuthError | Error | null;
|
|
113
|
+
isSuccess: boolean;
|
|
114
|
+
reset: () => void;
|
|
85
115
|
}
|
|
86
|
-
declare function
|
|
116
|
+
declare function useForgotPassword(opts?: UseForgotPasswordOptions): UseForgotPasswordReturn;
|
|
87
117
|
|
|
88
|
-
interface
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
118
|
+
interface UseResetPasswordOptions {
|
|
119
|
+
onSuccess?: () => void;
|
|
120
|
+
onError?: (error: AuthError | Error) => void;
|
|
121
|
+
}
|
|
122
|
+
interface UseResetPasswordReturn {
|
|
123
|
+
resetPassword: (data: {
|
|
124
|
+
token: string;
|
|
125
|
+
newPassword: string;
|
|
126
|
+
}) => Promise<void>;
|
|
127
|
+
isLoading: boolean;
|
|
128
|
+
error: AuthError | Error | null;
|
|
129
|
+
isSuccess: boolean;
|
|
130
|
+
reset: () => void;
|
|
131
|
+
}
|
|
132
|
+
declare function useResetPassword(opts?: UseResetPasswordOptions): UseResetPasswordReturn;
|
|
133
|
+
|
|
134
|
+
interface UseChangePasswordOptions {
|
|
135
|
+
onSuccess?: () => void;
|
|
136
|
+
onError?: (error: AuthError | Error) => void;
|
|
137
|
+
}
|
|
138
|
+
interface UseChangePasswordReturn {
|
|
139
|
+
changePassword: (data: {
|
|
140
|
+
currentPassword: string;
|
|
141
|
+
newPassword: string;
|
|
142
|
+
}) => Promise<void>;
|
|
143
|
+
isLoading: boolean;
|
|
144
|
+
error: AuthError | Error | null;
|
|
145
|
+
isSuccess: boolean;
|
|
146
|
+
reset: () => void;
|
|
94
147
|
}
|
|
148
|
+
declare function useChangePassword(opts?: UseChangePasswordOptions): UseChangePasswordReturn;
|
|
149
|
+
|
|
95
150
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
151
|
+
* Subscribe to a specific auth event. Cleans up on unmount.
|
|
152
|
+
* Uses a stable ref for the handler to avoid re-subscribing on every render.
|
|
153
|
+
*
|
|
154
|
+
* @example
|
|
155
|
+
* ```tsx
|
|
156
|
+
* useAuthEvent('sessionExpired', () => {
|
|
157
|
+
* toast.error('Session expired, please log in again');
|
|
158
|
+
* router.push('/login');
|
|
159
|
+
* });
|
|
160
|
+
*
|
|
161
|
+
* useAuthEvent('login', (user) => {
|
|
162
|
+
* analytics.track('login', { userId: user.id });
|
|
163
|
+
* });
|
|
164
|
+
* ```
|
|
98
165
|
*/
|
|
99
|
-
declare function
|
|
166
|
+
declare function useAuthEvent<K extends AuthEvent>(event: K, handler: (data: AuthEventMap[K]) => void): void;
|
|
167
|
+
|
|
168
|
+
interface AuthEventEntry {
|
|
169
|
+
event: AuthEvent;
|
|
170
|
+
data: unknown;
|
|
171
|
+
timestamp: number;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Subscribe to all auth events. Returns a log of recent events.
|
|
175
|
+
* Useful for logging, debug panels, or analytics.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```tsx
|
|
179
|
+
* const events = useAuthEvents({ maxEntries: 50 });
|
|
180
|
+
* // events = [{ event: 'login', data: {...}, timestamp: 1234 }, ...]
|
|
181
|
+
* ```
|
|
182
|
+
*/
|
|
183
|
+
declare function useAuthEvents(opts?: {
|
|
184
|
+
maxEntries?: number;
|
|
185
|
+
}): AuthEventEntry[];
|
|
100
186
|
|
|
101
187
|
interface SignedInProps {
|
|
102
188
|
children: ReactNode;
|
|
@@ -175,6 +261,52 @@ interface CanProps {
|
|
|
175
261
|
*/
|
|
176
262
|
declare function Can({ permission, role, children, fallback }: CanProps): react_jsx_runtime.JSX.Element;
|
|
177
263
|
|
|
264
|
+
interface RoleProps {
|
|
265
|
+
/** Required role name */
|
|
266
|
+
is?: string;
|
|
267
|
+
/** Allow any of these roles */
|
|
268
|
+
any?: string[];
|
|
269
|
+
children: ReactNode;
|
|
270
|
+
fallback?: ReactNode;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Role-based conditional rendering gate.
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```tsx
|
|
277
|
+
* <Role is="admin">
|
|
278
|
+
* <AdminPanel />
|
|
279
|
+
* </Role>
|
|
280
|
+
*
|
|
281
|
+
* <Role any={['admin', 'editor']} fallback={<p>No access</p>}>
|
|
282
|
+
* <ContentEditor />
|
|
283
|
+
* </Role>
|
|
284
|
+
* ```
|
|
285
|
+
*/
|
|
286
|
+
declare function Role(props: RoleProps): react_jsx_runtime.JSX.Element;
|
|
287
|
+
|
|
288
|
+
interface IfAuthProps {
|
|
289
|
+
/** Render when authenticated — receives user */
|
|
290
|
+
authenticated: (user: AuthUser) => ReactNode;
|
|
291
|
+
/** Render when not authenticated */
|
|
292
|
+
unauthenticated?: () => ReactNode;
|
|
293
|
+
/** Render during loading */
|
|
294
|
+
loading?: () => ReactNode;
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Render-prop component for full auth state branching.
|
|
298
|
+
*
|
|
299
|
+
* @example
|
|
300
|
+
* ```tsx
|
|
301
|
+
* <IfAuth
|
|
302
|
+
* loading={() => <Skeleton />}
|
|
303
|
+
* authenticated={(user) => <p>Welcome, {user.name}</p>}
|
|
304
|
+
* unauthenticated={() => <Link href="/login">Sign in</Link>}
|
|
305
|
+
* />
|
|
306
|
+
* ```
|
|
307
|
+
*/
|
|
308
|
+
declare function IfAuth({ authenticated, unauthenticated, loading }: IfAuthProps): react_jsx_runtime.JSX.Element;
|
|
309
|
+
|
|
178
310
|
interface ProtectedProps {
|
|
179
311
|
children: ReactNode;
|
|
180
312
|
/** URL to redirect unauthenticated users to */
|
|
@@ -221,6 +353,32 @@ interface UserNameProps {
|
|
|
221
353
|
*/
|
|
222
354
|
declare function UserName({ fallback }: UserNameProps): react_jsx_runtime.JSX.Element;
|
|
223
355
|
|
|
356
|
+
interface UserEmailProps {
|
|
357
|
+
fallback?: string;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Renders the user's email as plain text.
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```tsx
|
|
364
|
+
* <p>Email: <UserEmail fallback="Not available" /></p>
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
declare function UserEmail({ fallback }: UserEmailProps): react_jsx_runtime.JSX.Element;
|
|
368
|
+
|
|
369
|
+
interface UserRoleProps {
|
|
370
|
+
fallback?: string;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Renders the user's primary role name.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```tsx
|
|
377
|
+
* <span>Role: <UserRole fallback="none" /></span>
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
declare function UserRole({ fallback }: UserRoleProps): react_jsx_runtime.JSX.Element;
|
|
381
|
+
|
|
224
382
|
interface UserAvatarProps {
|
|
225
383
|
/** Pixel size of the avatar (default: 32) */
|
|
226
384
|
size?: number;
|
|
@@ -247,6 +405,24 @@ interface UserAvatarProps {
|
|
|
247
405
|
*/
|
|
248
406
|
declare function UserAvatar({ size, className, style, alt }: UserAvatarProps): react_jsx_runtime.JSX.Element;
|
|
249
407
|
|
|
408
|
+
interface PermissionListProps {
|
|
409
|
+
/** Render function for each permission */
|
|
410
|
+
children: (permission: string, index: number) => ReactNode;
|
|
411
|
+
/** Rendered when the user has no permissions */
|
|
412
|
+
fallback?: ReactNode;
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Iterates over the user's permissions with a render function.
|
|
416
|
+
*
|
|
417
|
+
* @example
|
|
418
|
+
* ```tsx
|
|
419
|
+
* <PermissionList fallback={<p>No permissions</p>}>
|
|
420
|
+
* {(perm) => <Badge key={perm}>{perm}</Badge>}
|
|
421
|
+
* </PermissionList>
|
|
422
|
+
* ```
|
|
423
|
+
*/
|
|
424
|
+
declare function PermissionList({ children, fallback }: PermissionListProps): react_jsx_runtime.JSX.Element;
|
|
425
|
+
|
|
250
426
|
interface SignOutButtonProps {
|
|
251
427
|
/** A single clickable child element. Its `onClick` will be wired to logout. */
|
|
252
428
|
children: ReactNode;
|
|
@@ -275,4 +451,45 @@ declare function SignOutButton({ children, onSuccess, onError }: SignOutButtonPr
|
|
|
275
451
|
disabled?: boolean;
|
|
276
452
|
}, string | react.JSXElementConstructor<any>>;
|
|
277
453
|
|
|
278
|
-
|
|
454
|
+
interface LoginButtonProps {
|
|
455
|
+
/** Where to navigate on click (default: '/login') */
|
|
456
|
+
href?: string;
|
|
457
|
+
children: ReactNode;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Wires a child element's onClick to navigate to the login page.
|
|
461
|
+
* Headless — bring your own button.
|
|
462
|
+
*
|
|
463
|
+
* @example
|
|
464
|
+
* ```tsx
|
|
465
|
+
* <SignedOut>
|
|
466
|
+
* <LoginButton href="/login">
|
|
467
|
+
* <Button>Sign in</Button>
|
|
468
|
+
* </LoginButton>
|
|
469
|
+
* </SignedOut>
|
|
470
|
+
* ```
|
|
471
|
+
*/
|
|
472
|
+
declare function LoginButton({ href, children }: LoginButtonProps): ReactElement<{
|
|
473
|
+
onClick?: (e: unknown) => void;
|
|
474
|
+
}, string | react.JSXElementConstructor<any>>;
|
|
475
|
+
|
|
476
|
+
interface RedirectToLoginProps {
|
|
477
|
+
/** Login URL (default: '/login') */
|
|
478
|
+
to?: string;
|
|
479
|
+
/** Preserve current URL as ?from= param (default: true) */
|
|
480
|
+
preserveFrom?: boolean;
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Immediately redirects to the login page. Renders nothing.
|
|
484
|
+
* Use inside <SignedOut> or <Protected fallback={...}>.
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* ```tsx
|
|
488
|
+
* <SignedOut>
|
|
489
|
+
* <RedirectToLogin to="/login" />
|
|
490
|
+
* </SignedOut>
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
declare function RedirectToLogin({ to, preserveFrom }: RedirectToLoginProps): any;
|
|
494
|
+
|
|
495
|
+
export { type AuthEventEntry, AuthLoading, AuthProvider, Can, IfAuth, LoginButton, PermissionList, Protected, RedirectToLogin, Role, type SessionStatus, SignOutButton, SignedIn, SignedOut, UserAvatar, UserEmail, UserName, UserRole, useAuth, useAuthClient, useAuthEvent, useAuthEvents, useChangePassword, useForgotPassword, useLogin, useLogout, usePermissions, useRegister, useResetPassword, useSession, useUser };
|