ab-ecosystem-sso 0.1.18 → 0.2.0
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/README.md +475 -15
- package/dist/ab-ecosystem-sso.cjs +28 -26
- package/dist/ab-ecosystem-sso.js +5817 -4114
- package/dist/index.d.ts +974 -18
- package/dist/style.css +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,214 @@
|
|
|
1
1
|
import { JSX as JSX_2 } from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* POST /invitations/:token/accept — joins the inviting account using an
|
|
6
|
+
* existing login. The caller's login email must match the invite. Follow it
|
|
7
|
+
* with `switchAccountApi` on the returned `account_id` to drop the user
|
|
8
|
+
* straight into the new account.
|
|
9
|
+
*/
|
|
10
|
+
export declare function acceptInvitationApi(projectId: string, inviteToken: string, token: string, apiBaseUrl?: string): Promise<string>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The invitee side of an invitation link — drop it on your
|
|
14
|
+
* `/accept-invite?token=…` route.
|
|
15
|
+
*
|
|
16
|
+
* It resolves the token first (`GET /invitations/:token`) and then renders the
|
|
17
|
+
* branch the backend asked for:
|
|
18
|
+
*
|
|
19
|
+
* - `mode: "signup"` — no login here yet, so it renders `Signup` with the
|
|
20
|
+
* invite token and the invited address prefilled and read-only.
|
|
21
|
+
* - `mode: "accept"` — they already have a login. Given a `token` it shows the
|
|
22
|
+
* invite details and an accept button (then switches the session onto the new
|
|
23
|
+
* account); without one it calls `onNeedLogin` so you can route them through
|
|
24
|
+
* your own login first.
|
|
25
|
+
*
|
|
26
|
+
* ```tsx
|
|
27
|
+
* <AcceptInvite
|
|
28
|
+
* projectId="…"
|
|
29
|
+
* inviteToken={new URLSearchParams(location.search).get('token') ?? ''}
|
|
30
|
+
* token={session}
|
|
31
|
+
* onAccepted={({ accessToken }) => accessToken && save(accessToken)}
|
|
32
|
+
* onNeedLogin={() => navigate('/login?next=' + encodeURIComponent(location.href))}
|
|
33
|
+
* />
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function AcceptInvite(props: AcceptInviteProps): JSX_2.Element;
|
|
37
|
+
|
|
38
|
+
export declare interface AcceptInviteProps {
|
|
39
|
+
projectId: string;
|
|
40
|
+
/** The `token` query param from the invite link. */
|
|
41
|
+
inviteToken: string;
|
|
42
|
+
/**
|
|
43
|
+
* A live session token, when the invitee is already signed in here. Required
|
|
44
|
+
* for the `"accept"` branch — without it the screen asks them to sign in
|
|
45
|
+
* first (see {@link onNeedLogin}).
|
|
46
|
+
*/
|
|
47
|
+
token?: string;
|
|
48
|
+
/**
|
|
49
|
+
* After accepting with an existing login, switch the session onto the joined
|
|
50
|
+
* account straight away (that's `/auth/switch-account`, which returns a fresh
|
|
51
|
+
* token). Defaults to `true`.
|
|
52
|
+
*/
|
|
53
|
+
switchOnAccept?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Called once the invitation has been accepted. `accessToken` is present when
|
|
56
|
+
* the session was switched onto the new account — store it in place of the
|
|
57
|
+
* old token.
|
|
58
|
+
*/
|
|
59
|
+
onAccepted?: (result: {
|
|
60
|
+
accountId: string;
|
|
61
|
+
accessToken?: string;
|
|
62
|
+
}) => void;
|
|
63
|
+
/**
|
|
64
|
+
* Called when the invite needs an existing login and none was given — send
|
|
65
|
+
* the user to your login route, then come back here with `token` set.
|
|
66
|
+
*/
|
|
67
|
+
onNeedLogin?: (invitation: InvitationLookup) => void;
|
|
68
|
+
/** Forwarded to the embedded `Signup` on the "signup" branch. */
|
|
69
|
+
onSuccess?: (data: unknown) => void;
|
|
70
|
+
onError?: (error: Error) => void;
|
|
71
|
+
/** Forwarded to the embedded `Signup` on the "signup" branch. */
|
|
72
|
+
countries?: CountryOption[];
|
|
73
|
+
classNames?: AuthClassNames;
|
|
74
|
+
mode?: AuthMode;
|
|
75
|
+
config?: AuthConfig;
|
|
76
|
+
apiBaseUrl?: string;
|
|
77
|
+
/** Persist the token issued when the session switches onto the joined account. Default `true`. */
|
|
78
|
+
persistToken?: boolean;
|
|
79
|
+
tokenStorageKey?: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** `{ data: { accessToken } }` from select-account / switch-account. */
|
|
83
|
+
export declare interface AccessTokenResponse {
|
|
84
|
+
success?: boolean;
|
|
85
|
+
message?: string;
|
|
86
|
+
data?: {
|
|
87
|
+
accessToken?: string;
|
|
88
|
+
[key: string]: unknown;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* One account the user can sign in to, as returned by the login picker and by
|
|
94
|
+
* `GET /accounts` (both are built by the same backend helper, so the two lists
|
|
95
|
+
* can't disagree).
|
|
96
|
+
*/
|
|
97
|
+
export declare interface AccountOption {
|
|
98
|
+
account_id: string;
|
|
99
|
+
name: string;
|
|
100
|
+
/** True for the user's own account; owners also get the roles/members admin. */
|
|
101
|
+
is_owner: boolean;
|
|
102
|
+
/** Role title, or null for an owner / a member with no role. */
|
|
103
|
+
role: string | null;
|
|
104
|
+
/** ISO date the member's access lapses, or null for no expiry. */
|
|
105
|
+
access_expiry: string | null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Lets a user pick which account to open. It serves both halves of the
|
|
110
|
+
* multi-account flow:
|
|
111
|
+
*
|
|
112
|
+
* **After login** — pass the `selectionTicket` + `accounts` from a response
|
|
113
|
+
* that came back with `accountSelectionRequired`. Picking exchanges the ticket
|
|
114
|
+
* for a session token. (`Login` renders this step itself, so you only need
|
|
115
|
+
* this when you handle the branch in your own `onSuccess`.)
|
|
116
|
+
*
|
|
117
|
+
* ```tsx
|
|
118
|
+
* <AccountPicker
|
|
119
|
+
* projectId="…"
|
|
120
|
+
* selectionTicket={res.selectionTicket}
|
|
121
|
+
* accounts={res.accounts}
|
|
122
|
+
* onSelect={({ accessToken }) => save(accessToken)}
|
|
123
|
+
* />
|
|
124
|
+
* ```
|
|
125
|
+
*
|
|
126
|
+
* **On a live session** — pass `token` instead. The list comes from
|
|
127
|
+
* `GET /accounts` and picking switches the session, handing you a fresh token
|
|
128
|
+
* to store in place of the old one.
|
|
129
|
+
*
|
|
130
|
+
* ```tsx
|
|
131
|
+
* <AccountPicker projectId="…" token={token} variant="list"
|
|
132
|
+
* currentAccountId={accountId}
|
|
133
|
+
* onSelect={({ accessToken }) => save(accessToken)} />
|
|
134
|
+
* ```
|
|
135
|
+
*/
|
|
136
|
+
export declare function AccountPicker(props: AccountPickerProps): JSX_2.Element;
|
|
137
|
+
|
|
138
|
+
export declare interface AccountPickerProps {
|
|
139
|
+
projectId: string;
|
|
140
|
+
/**
|
|
141
|
+
* Ticket mode (straight after login). The `selectionTicket` from a login or
|
|
142
|
+
* verify-2fa response that came back with `accountSelectionRequired`, plus
|
|
143
|
+
* the `accounts` it listed. Picking one exchanges the ticket for a session
|
|
144
|
+
* token via `/auth/select-account`.
|
|
145
|
+
*
|
|
146
|
+
* The ticket lives **five minutes**; after that the user has to log in again.
|
|
147
|
+
*/
|
|
148
|
+
selectionTicket?: string;
|
|
149
|
+
/** The accounts to choose from. In session mode they're fetched instead. */
|
|
150
|
+
accounts?: AccountOption[];
|
|
151
|
+
/**
|
|
152
|
+
* Session mode (account switcher). A live session token: the list is loaded
|
|
153
|
+
* from `GET /accounts` and picking calls `/auth/switch-account`, which issues
|
|
154
|
+
* a **fresh token** — store it in place of the old one. The previous token
|
|
155
|
+
* stays valid until it expires or is logged out.
|
|
156
|
+
*/
|
|
157
|
+
token?: string;
|
|
158
|
+
/** Recorded on the session by select-account, e.g. "webapp". */
|
|
159
|
+
client?: string;
|
|
160
|
+
/** "card" (default) renders the full auth card; "list" renders just the list. */
|
|
161
|
+
variant?: 'card' | 'list';
|
|
162
|
+
/** Card heading. Defaults to "Choose an account". */
|
|
163
|
+
title?: string;
|
|
164
|
+
/** Card subheading. */
|
|
165
|
+
description?: string;
|
|
166
|
+
/** Highlights the account the session is already on (session mode). */
|
|
167
|
+
currentAccountId?: string;
|
|
168
|
+
/** Called with the new token once an account has been chosen. */
|
|
169
|
+
onSelect?: (result: {
|
|
170
|
+
accessToken: string;
|
|
171
|
+
account: AccountOption;
|
|
172
|
+
}) => void;
|
|
173
|
+
onError?: (error: Error) => void;
|
|
174
|
+
/** Renders a secondary action under the list (e.g. "Back to sign in"). */
|
|
175
|
+
onCancel?: () => void;
|
|
176
|
+
cancelLabel?: string;
|
|
177
|
+
classNames?: AuthClassNames;
|
|
178
|
+
mode?: AuthMode;
|
|
179
|
+
config?: AuthConfig;
|
|
180
|
+
apiBaseUrl?: string;
|
|
181
|
+
/**
|
|
182
|
+
* Persist the issued token. Default `true` — important here, since
|
|
183
|
+
* switch-account returns a *fresh* token that has to replace the old one.
|
|
184
|
+
*/
|
|
185
|
+
persistToken?: boolean;
|
|
186
|
+
tokenStorageKey?: string;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export declare function AccountSettings({ open, onOpenChange, initialSection, customComponents, syncUrlHash, data, title, mode, isOwner, inviteLinkBase, onAccountSwitch, currentAccountId, projectId, token: tokenProp, apiBaseUrl, onSaveProfile, onUpdateWhatsApp, onChangePassword, onToggleTwoFactor, onRevokeSession, onAddFunds, onChangePlan, onLogout, }: AccountSettingsProps): JSX_2.Element | null;
|
|
190
|
+
|
|
191
|
+
/** Where a custom panel's trigger appears in the modal chrome. */
|
|
192
|
+
export declare type AccountSettingsCustomLocation = 'sidebar' | 'sidebar-bottom' | 'header';
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* A host-supplied panel added to the Account Settings modal. Its trigger appears
|
|
196
|
+
* in the sidebar (as a nav item), pinned to the bottom of the sidebar, or in the
|
|
197
|
+
* header next to the close button; activating it renders `component` in the modal
|
|
198
|
+
* body in place of the built-in sections.
|
|
199
|
+
*/
|
|
200
|
+
export declare interface AccountSettingsCustomPanel {
|
|
201
|
+
/** Stable id — used to track which panel is active. Must be unique. */
|
|
202
|
+
id: string;
|
|
203
|
+
/** Trigger placement. Defaults to "sidebar". */
|
|
204
|
+
location?: AccountSettingsCustomLocation;
|
|
205
|
+
/** Trigger text (sidebar label / header button tooltip + aria-label). */
|
|
206
|
+
label: string;
|
|
207
|
+
/** Optional trigger icon (an inline SVG or any node). */
|
|
208
|
+
icon?: ReactNode;
|
|
209
|
+
/** Rendered in the modal body when this panel is active. */
|
|
210
|
+
component: ReactNode;
|
|
211
|
+
}
|
|
5
212
|
|
|
6
213
|
export declare interface AccountSettingsData {
|
|
7
214
|
user: {
|
|
@@ -85,6 +292,12 @@ export declare interface AccountSettingsProps {
|
|
|
85
292
|
onOpenChange: (open: boolean) => void;
|
|
86
293
|
/** Section to show first. Defaults to "general". */
|
|
87
294
|
initialSection?: AccountSettingsSection;
|
|
295
|
+
/**
|
|
296
|
+
* Host-supplied panels added to the modal. Each renders a trigger in the
|
|
297
|
+
* sidebar, sidebar bottom, or header, and shows its `component` in the body
|
|
298
|
+
* when activated. See {@link AccountSettingsCustomPanel}.
|
|
299
|
+
*/
|
|
300
|
+
customComponents?: AccountSettingsCustomPanel[];
|
|
88
301
|
/**
|
|
89
302
|
* Sync the open state + active section to the URL hash as `#settings-<section>`,
|
|
90
303
|
* so a page refresh re-opens the modal on the same section. The hash is cleared
|
|
@@ -95,9 +308,35 @@ export declare interface AccountSettingsProps {
|
|
|
95
308
|
data?: Partial<AccountSettingsData>;
|
|
96
309
|
/** Modal heading shown above the nav. Defaults to "Settings". */
|
|
97
310
|
title?: string;
|
|
311
|
+
/** Colour scheme — `"light"` (default) or `"dark"`. */
|
|
312
|
+
mode?: AuthMode;
|
|
313
|
+
/**
|
|
314
|
+
* Show the owner-only Members / Roles / Invitations sections. Those endpoints
|
|
315
|
+
* are owner-only and answer a non-owner with "Only the account owner can do
|
|
316
|
+
* this", which the sections display — so the default is to show the tabs and
|
|
317
|
+
* let the API be the authority. Pass `false` to hide them outright.
|
|
318
|
+
*/
|
|
319
|
+
isOwner?: boolean;
|
|
320
|
+
/**
|
|
321
|
+
* Where invite links point; the token is appended as `?token=…`. Defaults to
|
|
322
|
+
* `<your origin>/accept-invite`. Build that route with `AcceptInvite`.
|
|
323
|
+
*/
|
|
324
|
+
inviteLinkBase?: string;
|
|
325
|
+
/**
|
|
326
|
+
* Enables the **Accounts** section (the account switcher). Called with a
|
|
327
|
+
* fresh token after `/auth/switch-account` — store it in place of the old
|
|
328
|
+
* one. Without this prop the switcher is hidden, since there'd be nowhere to
|
|
329
|
+
* put the new token.
|
|
330
|
+
*/
|
|
331
|
+
onAccountSwitch?: (result: {
|
|
332
|
+
accessToken: string;
|
|
333
|
+
accountId: string;
|
|
334
|
+
}) => void;
|
|
335
|
+
/** The account the session is currently on — marked "Current" in the switcher. */
|
|
336
|
+
currentAccountId?: string;
|
|
98
337
|
/** When provided (with token), the component loads/saves via the live API instead of sample data. */
|
|
99
338
|
projectId?: string;
|
|
100
|
-
/** Bearer token for API calls. */
|
|
339
|
+
/** Bearer token for API calls. Omit to use the one stored at login. */
|
|
101
340
|
token?: string;
|
|
102
341
|
/** API base URL override (defaults to VITE_AUTH_API_BASE_URL). */
|
|
103
342
|
apiBaseUrl?: string;
|
|
@@ -121,11 +360,17 @@ export declare interface AccountSettingsProps {
|
|
|
121
360
|
onLogout?: () => void;
|
|
122
361
|
}
|
|
123
362
|
|
|
124
|
-
export declare type AccountSettingsSection = 'general' | 'account' | 'billing' | 'wallet' | 'plans' | 'credits'
|
|
363
|
+
export declare type AccountSettingsSection = 'general' | 'account' | 'billing' | 'wallet' | 'plans' | 'credits'
|
|
364
|
+
/** Account switcher — only shown when `onAccountSwitch` is given. */
|
|
365
|
+
| 'accounts'
|
|
366
|
+
/** Owner-only team administration. */
|
|
367
|
+
| 'members' | 'roles' | 'invitations';
|
|
125
368
|
|
|
126
369
|
export declare class AuthApiError extends Error {
|
|
127
370
|
code: string | undefined;
|
|
128
|
-
|
|
371
|
+
/** HTTP status, when the caller had one to hand (select/switch answer 401). */
|
|
372
|
+
status: number | undefined;
|
|
373
|
+
constructor(code: string | undefined, fallbackMessage: string, status?: number);
|
|
129
374
|
}
|
|
130
375
|
|
|
131
376
|
/** CSS class overrides for all auth component sub-elements */
|
|
@@ -157,6 +402,18 @@ export declare interface AuthComponentBaseProps {
|
|
|
157
402
|
onError?: (error: Error) => void;
|
|
158
403
|
/** API base URL override */
|
|
159
404
|
apiBaseUrl?: string;
|
|
405
|
+
/** Colour scheme — `"light"` (default) or `"dark"`. */
|
|
406
|
+
mode?: AuthMode;
|
|
407
|
+
/**
|
|
408
|
+
* Persist the issued session token so the rest of the package (and your own
|
|
409
|
+
* calls, via `getAuthHeader`) can use it without being handed one. Default
|
|
410
|
+
* `true`. Set `false` to keep handling the token entirely yourself.
|
|
411
|
+
*
|
|
412
|
+
* Configure the key/storage globally with `configureTokenStorage()`.
|
|
413
|
+
*/
|
|
414
|
+
persistToken?: boolean;
|
|
415
|
+
/** Storage key override for this component. Default `"ab-sso:accessToken"`. */
|
|
416
|
+
tokenStorageKey?: string;
|
|
160
417
|
}
|
|
161
418
|
|
|
162
419
|
export declare interface AuthConfig {
|
|
@@ -178,6 +435,54 @@ export declare interface AuthErrorResponse {
|
|
|
178
435
|
errors?: Record<string, string[]>;
|
|
179
436
|
}
|
|
180
437
|
|
|
438
|
+
/**
|
|
439
|
+
* `fetch` with the session token attached and 401 handling wired in.
|
|
440
|
+
*
|
|
441
|
+
* ```ts
|
|
442
|
+
* const res = await authFetch("/api/orders");
|
|
443
|
+
* ```
|
|
444
|
+
*
|
|
445
|
+
* An `Authorization` header you set yourself is never overwritten. Everything
|
|
446
|
+
* else behaves exactly like `fetch`, including the returned unread body.
|
|
447
|
+
*/
|
|
448
|
+
export declare function authFetch(input: RequestInfo | URL, init?: AuthFetchInit): Promise<Response>;
|
|
449
|
+
|
|
450
|
+
export declare interface AuthFetchInit extends RequestInit {
|
|
451
|
+
/** Token override; defaults to the stored session token. */
|
|
452
|
+
token?: string;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export declare interface AuthHandlers {
|
|
456
|
+
/**
|
|
457
|
+
* The session is no longer valid (401, and not the account-access case) —
|
|
458
|
+
* send the user to login.
|
|
459
|
+
*/
|
|
460
|
+
onUnauthorized?: (info: UnauthorizedInfo) => void;
|
|
461
|
+
/**
|
|
462
|
+
* Access to *this account* was revoked or expired (`A403`). The login is
|
|
463
|
+
* still good: show the account picker rather than logging out.
|
|
464
|
+
*/
|
|
465
|
+
onAccountAccessLost?: (info: UnauthorizedInfo) => void;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export declare interface AuthInterceptorOptions extends AuthHandlers {
|
|
469
|
+
/**
|
|
470
|
+
* **Required.** Origins allowed to receive the token — your own API hosts.
|
|
471
|
+
* `"self"` means the page's own origin (covers relative URLs like
|
|
472
|
+
* `/api/...`). There is no wildcard: anything not listed is left untouched,
|
|
473
|
+
* which is what keeps the token away from third-party scripts.
|
|
474
|
+
*/
|
|
475
|
+
origins: string[];
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Colour scheme for a rendered component. Defaults to `"light"`.
|
|
480
|
+
*
|
|
481
|
+
* Each component root carries `data-aas-theme`, so modes are per-component —
|
|
482
|
+
* a dark login card and a light settings modal can coexist on one page.
|
|
483
|
+
*/
|
|
484
|
+
export declare type AuthMode = 'light' | 'dark';
|
|
485
|
+
|
|
181
486
|
export declare function AuthProvider({ projectId, config: externalConfig, apiBaseUrl, children, }: AuthProviderProps): JSX_2.Element;
|
|
182
487
|
|
|
183
488
|
declare interface AuthProviderProps {
|
|
@@ -188,11 +493,34 @@ declare interface AuthProviderProps {
|
|
|
188
493
|
children: ReactNode;
|
|
189
494
|
}
|
|
190
495
|
|
|
496
|
+
/**
|
|
497
|
+
* Response of `/auth/login` and `/auth/verify-2fa`.
|
|
498
|
+
*
|
|
499
|
+
* It has **three** branches, which must be checked in this order:
|
|
500
|
+
* 1. `twoFactorRequired` — collect an OTP and call `/auth/verify-2fa`.
|
|
501
|
+
* 2. `accountSelectionRequired` — the user belongs to more than one account;
|
|
502
|
+
* there is no token yet. Let them pick from `accounts`, then exchange
|
|
503
|
+
* `selectionTicket` via `/auth/select-account` (the ticket lives 5 minutes).
|
|
504
|
+
* 3. otherwise — signed in, token at `data.accessToken`.
|
|
505
|
+
*/
|
|
191
506
|
export declare interface AuthResponse {
|
|
507
|
+
success?: boolean;
|
|
508
|
+
message?: string;
|
|
509
|
+
/** Present on a completed login; the session token is `data.accessToken`. */
|
|
510
|
+
data?: {
|
|
511
|
+
accessToken?: string;
|
|
512
|
+
[key: string]: unknown;
|
|
513
|
+
};
|
|
192
514
|
token: string;
|
|
193
515
|
user: unknown;
|
|
194
516
|
/** If true, the user must complete 2FA (re-sending email + password + OTP + captcha) */
|
|
195
517
|
twoFactorRequired?: boolean;
|
|
518
|
+
/** If true, no token was issued — the user must choose an account first. */
|
|
519
|
+
accountSelectionRequired?: boolean;
|
|
520
|
+
/** The accounts to choose from. Only present with `accountSelectionRequired`. */
|
|
521
|
+
accounts?: AccountOption[];
|
|
522
|
+
/** Short-lived (5 min) credential for `/auth/select-account`. */
|
|
523
|
+
selectionTicket?: string;
|
|
196
524
|
}
|
|
197
525
|
|
|
198
526
|
export declare interface CaptchaConfig {
|
|
@@ -212,6 +540,34 @@ export declare interface CardLayoutConfig {
|
|
|
212
540
|
signupEnabled: boolean;
|
|
213
541
|
}
|
|
214
542
|
|
|
543
|
+
/** Removes the stored token. Called by logout and on an invalid session. */
|
|
544
|
+
export declare function clearStoredToken(options?: TokenAccessOptions): void;
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* Registers what should happen when a call made through `authFetch` (or an
|
|
548
|
+
* installed interceptor) comes back unauthorized. Call it once at bootstrap.
|
|
549
|
+
*/
|
|
550
|
+
export declare function configureAuthHandlers(next: AuthHandlers): void;
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Remaps any of the paths, for apps whose routes differ (`/sign-in` instead of
|
|
554
|
+
* `/login`, say). Call it once at bootstrap — `SessionProvider` and
|
|
555
|
+
* `SsoAuthRoutes` both read the result.
|
|
556
|
+
*/
|
|
557
|
+
export declare function configureSsoRoutes(overrides: Partial<SsoRoutePaths>): void;
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Overrides how the token is stored, for the whole package. Call it once
|
|
561
|
+
* before rendering (e.g. next to your app's bootstrap).
|
|
562
|
+
*
|
|
563
|
+
* ```ts
|
|
564
|
+
* configureTokenStorage({ key: 'authToken' }); // match an existing app
|
|
565
|
+
* configureTokenStorage({ storage: 'sessionStorage' }); // per-tab only
|
|
566
|
+
* configureTokenStorage({ enabled: false }); // opt out entirely
|
|
567
|
+
* ```
|
|
568
|
+
*/
|
|
569
|
+
export declare function configureTokenStorage(options: Partial<TokenStorageConfig>): void;
|
|
570
|
+
|
|
215
571
|
/** A country option for the signup country dropdown. */
|
|
216
572
|
export declare interface CountryOption {
|
|
217
573
|
/** ISO 3166-1 alpha-2 country code, e.g. "US". */
|
|
@@ -219,6 +575,47 @@ export declare interface CountryOption {
|
|
|
219
575
|
name: string;
|
|
220
576
|
}
|
|
221
577
|
|
|
578
|
+
/**
|
|
579
|
+
* The one and only time the invite `token` is returned — it never appears in
|
|
580
|
+
* the list. Build the link yourself as
|
|
581
|
+
* `{your-frontend}/accept-invite?token={token}`; **no email is sent.**
|
|
582
|
+
*/
|
|
583
|
+
export declare interface CreatedInvite {
|
|
584
|
+
id: string;
|
|
585
|
+
email: string;
|
|
586
|
+
token: string;
|
|
587
|
+
/** Link expiry — 72 hours out. */
|
|
588
|
+
expires_at?: string;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
export declare function createInvitationApi(projectId: string, payload: CreateInvitePayload, token?: string, apiBaseUrl?: string): Promise<CreatedInvite>;
|
|
592
|
+
|
|
593
|
+
export declare interface CreateInvitePayload {
|
|
594
|
+
email: string;
|
|
595
|
+
/** "member" (default) or "user". */
|
|
596
|
+
type?: 'member' | 'user';
|
|
597
|
+
/** Role id, or null/omitted for no permissions. */
|
|
598
|
+
role?: string | null;
|
|
599
|
+
/** Future ISO date, or null/omitted for no expiry. */
|
|
600
|
+
access_expiry?: string | null;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export declare function createRoleApi(projectId: string, payload: CreateRolePayload, token?: string, apiBaseUrl?: string): Promise<{
|
|
604
|
+
id: string;
|
|
605
|
+
}>;
|
|
606
|
+
|
|
607
|
+
export declare interface CreateRolePayload {
|
|
608
|
+
title: string;
|
|
609
|
+
description?: string;
|
|
610
|
+
/**
|
|
611
|
+
* Permission strings. The server trims, uppercases, dedupes and sorts them,
|
|
612
|
+
* so "billing.view", "BILLING.VIEW" and " user.list " are stored as
|
|
613
|
+
* BILLING.VIEW + USER.LIST. Max 200 after dedupe. They are not validated
|
|
614
|
+
* against a catalogue yet — an unknown string simply grants nothing.
|
|
615
|
+
*/
|
|
616
|
+
permission?: string[];
|
|
617
|
+
}
|
|
618
|
+
|
|
222
619
|
export declare interface CreditWallet {
|
|
223
620
|
id: string;
|
|
224
621
|
label: string;
|
|
@@ -234,6 +631,15 @@ export declare interface CreditWallet {
|
|
|
234
631
|
rechargeable: boolean;
|
|
235
632
|
}
|
|
236
633
|
|
|
634
|
+
/** The defaults. `inviteLinkBase` and the backend's email links assume these. */
|
|
635
|
+
export declare const DEFAULT_SSO_ROUTES: SsoRoutePaths;
|
|
636
|
+
|
|
637
|
+
/**
|
|
638
|
+
* Refused while the role is still assigned, with
|
|
639
|
+
* "This role is assigned to N members. Reassign them first."
|
|
640
|
+
*/
|
|
641
|
+
export declare function deleteRoleApi(projectId: string, roleId: string, token?: string, apiBaseUrl?: string): Promise<unknown>;
|
|
642
|
+
|
|
237
643
|
declare function fetchAuthConfig(projectId: string, apiBaseUrl?: string): Promise<AuthConfig>;
|
|
238
644
|
export { fetchAuthConfig }
|
|
239
645
|
export { fetchAuthConfig as getAuthConfig }
|
|
@@ -257,6 +663,16 @@ export declare interface ForgotPasswordRequest {
|
|
|
257
663
|
captchaToken?: string;
|
|
258
664
|
}
|
|
259
665
|
|
|
666
|
+
/**
|
|
667
|
+
* `Authorization` header for the given token, falling back to the stored one.
|
|
668
|
+
* Returns an empty object when there is no token, so it's safe to spread.
|
|
669
|
+
*
|
|
670
|
+
* ```ts
|
|
671
|
+
* fetch(url, { headers: { ...getAuthHeader(), 'Content-Type': 'application/json' } });
|
|
672
|
+
* ```
|
|
673
|
+
*/
|
|
674
|
+
export declare function getAuthHeader(token?: string): Record<string, string>;
|
|
675
|
+
|
|
260
676
|
/**
|
|
261
677
|
* GET /currency — the currencies enabled for the project (shown in signup).
|
|
262
678
|
* Response envelope: `{ success, data: [{ name, code }] }`. No token required.
|
|
@@ -264,44 +680,161 @@ export declare interface ForgotPasswordRequest {
|
|
|
264
680
|
*/
|
|
265
681
|
export declare function getCurrenciesApi(projectId: string, apiBaseUrl?: string): Promise<ProjectCurrency[]>;
|
|
266
682
|
|
|
683
|
+
/**
|
|
684
|
+
* GET /invitations/:token — resolves an invite link. No auth. Call it before
|
|
685
|
+
* rendering anything so you know whether to collect a password (`mode:
|
|
686
|
+
* "signup"`) or just have them accept (`mode: "accept"`).
|
|
687
|
+
*/
|
|
688
|
+
export declare function getInvitationApi(projectId: string, inviteToken: string, apiBaseUrl?: string): Promise<InvitationLookup>;
|
|
689
|
+
|
|
690
|
+
/** The paths currently in effect. */
|
|
691
|
+
export declare function getSsoRoutes(): SsoRoutePaths;
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* The stored session token, or `""` when there is none (not signed in, storage
|
|
695
|
+
* disabled, or persistence turned off).
|
|
696
|
+
*/
|
|
697
|
+
export declare function getStoredToken(options?: TokenAccessOptions): string;
|
|
698
|
+
|
|
699
|
+
/** The storage settings currently in effect. */
|
|
700
|
+
export declare function getTokenStorageConfig(): TokenStorageConfig;
|
|
701
|
+
|
|
267
702
|
/**
|
|
268
703
|
* Fetches the current user from GET /api/v1/projects/:projectId/me.
|
|
269
704
|
* Returns the raw response envelope, e.g.
|
|
270
705
|
* { success, data: { id, username, profile: { name, email, address, ... }, mainWallet, coinWallet } }
|
|
271
706
|
* Throws AuthApiError on a non-2xx response.
|
|
272
707
|
*/
|
|
273
|
-
export declare function getUser(projectId: string, token
|
|
708
|
+
export declare function getUser(projectId: string, token?: string, apiBaseUrl?: string): Promise<Record<string, unknown>>;
|
|
274
709
|
|
|
275
710
|
/**
|
|
276
711
|
* GET /credit-wallets → the user's credit wallets (mapped CreditWallet[]).
|
|
277
712
|
* Alias of `getCreditWalletsApi` with a consumer-friendly name.
|
|
278
713
|
*/
|
|
279
|
-
export declare function getUserCreditWallet(projectId: string, token
|
|
714
|
+
export declare function getUserCreditWallet(projectId: string, token?: string, apiBaseUrl?: string): Promise<CreditWallet[]>;
|
|
280
715
|
|
|
281
716
|
/**
|
|
282
717
|
* GET /me → the user's profile as flat, display-ready fields (MappedProfile).
|
|
283
718
|
* Includes name, email/username, address, phone, language and wallet summaries.
|
|
284
719
|
*/
|
|
285
|
-
export declare function getUserProfile(projectId: string, token
|
|
720
|
+
export declare function getUserProfile(projectId: string, token?: string, apiBaseUrl?: string): Promise<MappedProfile>;
|
|
286
721
|
|
|
287
722
|
/**
|
|
288
723
|
* GET /subscription → the user's active subscription, as returned by the API
|
|
289
724
|
* (unmodified). `request()` unwraps the `{ success, data }` envelope, so this
|
|
290
725
|
* resolves to the raw subscription `data` object.
|
|
291
726
|
*/
|
|
292
|
-
export declare function getUserSubscription(projectId: string, token
|
|
727
|
+
export declare function getUserSubscription(projectId: string, token?: string, apiBaseUrl?: string): Promise<unknown>;
|
|
293
728
|
|
|
294
729
|
/**
|
|
295
730
|
* GET /me → just the wallet balances ({ mainWallet, coinWallet }).
|
|
296
731
|
* A convenience over `getUserProfile` when you only need the balances.
|
|
297
732
|
*/
|
|
298
|
-
export declare function getUserWallet(projectId: string, token
|
|
733
|
+
export declare function getUserWallet(projectId: string, token?: string, apiBaseUrl?: string): Promise<UserWallets>;
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* Attaches the session token to requests your app makes to the listed origins,
|
|
737
|
+
* by patching `fetch` and `XMLHttpRequest`.
|
|
738
|
+
*
|
|
739
|
+
* Prefer `getAuthHeader()` or `authFetch()` — this exists for apps with too
|
|
740
|
+
* many call sites to touch. The allowlist is mandatory precisely because a
|
|
741
|
+
* blanket patch would hand your bearer token to every third-party endpoint the
|
|
742
|
+
* page talks to.
|
|
743
|
+
*
|
|
744
|
+
* ```ts
|
|
745
|
+
* const stop = installAuthInterceptor({
|
|
746
|
+
* origins: ["self", "https://api.example.com"],
|
|
747
|
+
* onUnauthorized: () => navigate("/login"),
|
|
748
|
+
* onAccountAccessLost: () => showAccountPicker(),
|
|
749
|
+
* });
|
|
750
|
+
* ```
|
|
751
|
+
*
|
|
752
|
+
* Returns a function that restores the originals. Calling it twice is a no-op:
|
|
753
|
+
* the second install replaces the first.
|
|
754
|
+
*/
|
|
755
|
+
export declare function installAuthInterceptor(options: AuthInterceptorOptions): () => void;
|
|
756
|
+
|
|
757
|
+
export declare interface Invitation {
|
|
758
|
+
id: string;
|
|
759
|
+
email: string;
|
|
760
|
+
type?: 'member' | 'user' | string;
|
|
761
|
+
role?: {
|
|
762
|
+
id: string;
|
|
763
|
+
title: string;
|
|
764
|
+
} | null;
|
|
765
|
+
access_expiry?: string | null;
|
|
766
|
+
status?: InvitationStatus | string;
|
|
767
|
+
/** When the invite link stops working (72 hours after creation). */
|
|
768
|
+
invite_expires_at?: string;
|
|
769
|
+
created_at?: string;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* `GET /invitations/:token` — what an invite link resolves to. Call this before
|
|
774
|
+
* rendering anything, and branch on `mode`:
|
|
775
|
+
* "signup" → collect a password and POST `/auth/signup` with `inviteToken`.
|
|
776
|
+
* "accept" → the person already has a login here; sign them in, then POST
|
|
777
|
+
* `/invitations/:token/accept`.
|
|
778
|
+
*/
|
|
779
|
+
export declare interface InvitationLookup {
|
|
780
|
+
/** The invited address. Render it read-only — a different one is ignored/rejected. */
|
|
781
|
+
email: string;
|
|
782
|
+
/** Display name of the account owner who sent the invite. */
|
|
783
|
+
invited_by: string;
|
|
784
|
+
/** Role title they'll be given, or null for no permissions. */
|
|
785
|
+
role: string | null;
|
|
786
|
+
access_expiry: string | null;
|
|
787
|
+
mode: 'signup' | 'accept';
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
export declare function InvitationsSection({ projectId, token, apiBaseUrl, inviteLinkBase }: InvitationsSectionProps): JSX_2.Element;
|
|
791
|
+
|
|
792
|
+
export declare interface InvitationsSectionProps extends TeamSectionProps {
|
|
793
|
+
/**
|
|
794
|
+
* Where invite links point. The token is appended as `?token=…`.
|
|
795
|
+
* Defaults to `<your origin>/accept-invite`.
|
|
796
|
+
*/
|
|
797
|
+
inviteLinkBase?: string;
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/** Invite status as reported by the API. A lapsed pending invite reports "expired" immediately. */
|
|
801
|
+
export declare type InvitationStatus = 'pending' | 'accepted' | 'expired' | 'revoked';
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* True when an error means "this account is no longer reachable" — revoked or
|
|
805
|
+
* expired member access, a disabled account, or an account that doesn't belong
|
|
806
|
+
* to this product. The backend answers 401 with code `A403`.
|
|
807
|
+
*
|
|
808
|
+
* Treat it as "send the user back to the account picker", **not** as a logout:
|
|
809
|
+
* their login is intact and their other accounts still work.
|
|
810
|
+
*/
|
|
811
|
+
export declare function isAccountAccessError(error: unknown): boolean;
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* GET /accounts — every account the caller can reach, for an account switcher.
|
|
815
|
+
* Built by the same backend helper as the login picker, so the two agree.
|
|
816
|
+
*/
|
|
817
|
+
export declare function listAccountsApi(projectId: string, token: string, apiBaseUrl?: string): Promise<AccountOption[]>;
|
|
818
|
+
|
|
819
|
+
declare type Listener = (token: string) => void;
|
|
820
|
+
|
|
821
|
+
export declare function listInvitationsApi(projectId: string, token?: string, status?: InvitationStatus, apiBaseUrl?: string): Promise<Invitation[]>;
|
|
822
|
+
|
|
823
|
+
/** Defaults to active members; pass "revoked" to list revoked ones. */
|
|
824
|
+
export declare function listMembersApi(projectId: string, token?: string, status?: MemberStatus, apiBaseUrl?: string): Promise<Member[]>;
|
|
825
|
+
|
|
826
|
+
export declare function listRolesApi(projectId: string, token?: string, apiBaseUrl?: string): Promise<Role[]>;
|
|
299
827
|
|
|
300
828
|
export declare function Login(props: LoginProps): JSX_2.Element;
|
|
301
829
|
|
|
302
830
|
export declare interface LoginProps extends AuthComponentBaseProps {
|
|
303
831
|
onForgotPassword?: () => void;
|
|
304
832
|
onSignupClick?: () => void;
|
|
833
|
+
/**
|
|
834
|
+
* Client identifier recorded on the session when the user picks an account
|
|
835
|
+
* (sent as `client` to `/auth/select-account`), e.g. `"webapp"`.
|
|
836
|
+
*/
|
|
837
|
+
client?: string;
|
|
305
838
|
}
|
|
306
839
|
|
|
307
840
|
export declare interface LoginRequest {
|
|
@@ -325,11 +858,13 @@ export declare function logout(options: LogoutOptions): Promise<void>;
|
|
|
325
858
|
|
|
326
859
|
export declare function logoutApi(projectId: string, token: string, apiBaseUrl?: string): Promise<void>;
|
|
327
860
|
|
|
328
|
-
export declare function LogoutButton({ className, children, projectId, token, apiBaseUrl, onSuccess, onError, }: LogoutButtonProps): JSX_2.Element;
|
|
861
|
+
export declare function LogoutButton({ className, children, mode, projectId, token, apiBaseUrl, onSuccess, onError, }: LogoutButtonProps): JSX_2.Element;
|
|
329
862
|
|
|
330
863
|
declare interface LogoutButtonProps {
|
|
331
864
|
className?: string;
|
|
332
865
|
children?: React.ReactNode;
|
|
866
|
+
/** Colour scheme — "light" (default) or "dark". */
|
|
867
|
+
mode?: AuthMode;
|
|
333
868
|
/**
|
|
334
869
|
* Standalone mode: when `projectId` + `token` are supplied the button calls
|
|
335
870
|
* POST /api/v1/projects/:projectId/auth/logout directly (no SessionProvider
|
|
@@ -346,7 +881,8 @@ declare interface LogoutButtonProps {
|
|
|
346
881
|
|
|
347
882
|
export declare interface LogoutOptions {
|
|
348
883
|
projectId: string;
|
|
349
|
-
token
|
|
884
|
+
/** Omit to use the stored session token. */
|
|
885
|
+
token?: string;
|
|
350
886
|
apiBaseUrl?: string;
|
|
351
887
|
/** Called after the logout request completes — clear local state / redirect here. */
|
|
352
888
|
onSuccess?: () => void;
|
|
@@ -393,6 +929,51 @@ export declare interface MappedSubscription {
|
|
|
393
929
|
packageCounts: Record<string, number>;
|
|
394
930
|
}
|
|
395
931
|
|
|
932
|
+
/**
|
|
933
|
+
* Resolves a pathname to one of the auth screens, or null when it's not ours.
|
|
934
|
+
*
|
|
935
|
+
* Matching is prefix-based for the token-bearing routes, so both
|
|
936
|
+
* `/verify/<token>` and `/reset-password?token=<token>` work, and exact for the
|
|
937
|
+
* rest — `/login` must not swallow the host's `/login-help`.
|
|
938
|
+
*/
|
|
939
|
+
export declare function matchSsoRoute(pathname: string, search?: string, options?: {
|
|
940
|
+
basePath?: string;
|
|
941
|
+
routes?: Partial<SsoRoutePaths>;
|
|
942
|
+
}): SsoRouteMatch | null;
|
|
943
|
+
|
|
944
|
+
export declare interface Member {
|
|
945
|
+
/** Membership id — this is what update/revoke take. */
|
|
946
|
+
id: string;
|
|
947
|
+
/** The person's login profile id (not the membership). */
|
|
948
|
+
user_id?: string;
|
|
949
|
+
name?: string;
|
|
950
|
+
email?: string;
|
|
951
|
+
type?: 'member' | 'user' | string;
|
|
952
|
+
role?: {
|
|
953
|
+
id: string;
|
|
954
|
+
title: string;
|
|
955
|
+
} | null;
|
|
956
|
+
access_expiry?: string | null;
|
|
957
|
+
expired?: boolean;
|
|
958
|
+
status?: MemberStatus | string;
|
|
959
|
+
joined_at?: string;
|
|
960
|
+
revoked_at?: string | null;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
export declare function MembersSection({ projectId, token, apiBaseUrl }: TeamSectionProps): JSX_2.Element;
|
|
964
|
+
|
|
965
|
+
export declare type MemberStatus = 'active' | 'revoked';
|
|
966
|
+
|
|
967
|
+
/**
|
|
968
|
+
* Subscribes to token changes made through this module (login, switch-account,
|
|
969
|
+
* logout, …). Returns an unsubscribe function.
|
|
970
|
+
*
|
|
971
|
+
* This fires for changes made *in this tab*. It deliberately does not listen to
|
|
972
|
+
* the cross-tab `storage` event — syncing sessions between tabs is a host
|
|
973
|
+
* decision, not something a component library should impose.
|
|
974
|
+
*/
|
|
975
|
+
export declare function onStoredTokenChange(listener: Listener): () => void;
|
|
976
|
+
|
|
396
977
|
/**
|
|
397
978
|
* Imperatively open the Account Settings modal from anywhere — e.g. a button
|
|
398
979
|
* or menu item's onClick. Mounts the modal into a detached container appended
|
|
@@ -441,6 +1022,9 @@ export declare interface ProjectCurrency {
|
|
|
441
1022
|
code: string;
|
|
442
1023
|
}
|
|
443
1024
|
|
|
1025
|
+
/** Reads `accessToken` out of any of the envelope shapes the auth endpoints return. */
|
|
1026
|
+
export declare function readAccessTokenFrom(response: unknown): string;
|
|
1027
|
+
|
|
444
1028
|
/**
|
|
445
1029
|
* POST /auth/reset-password — sets a new password using the reset token from the
|
|
446
1030
|
* emailed link. Body: { token, password, captchaToken }.
|
|
@@ -470,20 +1054,80 @@ export declare interface ResetPasswordRequest {
|
|
|
470
1054
|
*/
|
|
471
1055
|
export declare function restoreAccountSettings(options?: OpenAccountSettingsOptions): AccountSettingsHandle | null;
|
|
472
1056
|
|
|
1057
|
+
/** Only works on a *pending* invite — revoking an accepted one is a member operation. */
|
|
1058
|
+
export declare function revokeInvitationApi(projectId: string, inviteId: string, token?: string, apiBaseUrl?: string): Promise<unknown>;
|
|
1059
|
+
|
|
1060
|
+
/**
|
|
1061
|
+
* Revokes this account's access for the member. Their login is untouched — only
|
|
1062
|
+
* this account becomes unreachable and it disappears from their picker, on
|
|
1063
|
+
* their next request rather than when their token expires. Re-inviting the same
|
|
1064
|
+
* person reactivates the existing row.
|
|
1065
|
+
*/
|
|
1066
|
+
export declare function revokeMemberApi(projectId: string, memberId: string, token?: string, apiBaseUrl?: string): Promise<unknown>;
|
|
1067
|
+
|
|
1068
|
+
/** A role as returned by `GET /account/roles`. */
|
|
1069
|
+
export declare interface Role {
|
|
1070
|
+
id: string;
|
|
1071
|
+
title: string;
|
|
1072
|
+
description?: string;
|
|
1073
|
+
/** Permission strings, stored uppercased + deduped + sorted by the server. */
|
|
1074
|
+
permission: string[];
|
|
1075
|
+
/** Platform-provided role: assignable, but not editable or deletable. */
|
|
1076
|
+
is_system?: boolean;
|
|
1077
|
+
/** Live count of members holding this role — use it to warn before deleting. */
|
|
1078
|
+
members?: number;
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
export declare function RolesSection({ projectId, token, apiBaseUrl }: TeamSectionProps): JSX_2.Element;
|
|
1082
|
+
|
|
1083
|
+
/**
|
|
1084
|
+
* POST /auth/select-account — exchanges a login `selectionTicket` for a session
|
|
1085
|
+
* token on the chosen account.
|
|
1086
|
+
*
|
|
1087
|
+
* Unauthenticated: the ticket *is* the credential, and it expires after five
|
|
1088
|
+
* minutes (then the user has to log in again). Pass the caller's own
|
|
1089
|
+
* `account_id` to enter their own account.
|
|
1090
|
+
*/
|
|
1091
|
+
export declare function selectAccountApi(projectId: string, data: SelectAccountRequest, apiBaseUrl?: string): Promise<string>;
|
|
1092
|
+
|
|
1093
|
+
/** Body of `POST /auth/select-account` — unauthenticated; the ticket is the credential. */
|
|
1094
|
+
export declare interface SelectAccountRequest {
|
|
1095
|
+
selection_ticket: string;
|
|
1096
|
+
account_id: string;
|
|
1097
|
+
/** Free-form client identifier recorded on the session, e.g. "webapp". */
|
|
1098
|
+
client?: string;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
473
1101
|
declare interface SessionContextValue {
|
|
474
1102
|
isAuthenticated: boolean;
|
|
475
1103
|
isValidating: boolean;
|
|
476
1104
|
logout: () => Promise<void>;
|
|
1105
|
+
/**
|
|
1106
|
+
* Set when the session was rejected because *this account* is no longer
|
|
1107
|
+
* reachable (member access revoked or expired, account disabled) — the login
|
|
1108
|
+
* itself is still valid. Null in every other state.
|
|
1109
|
+
*/
|
|
1110
|
+
accountAccessLost: {
|
|
1111
|
+
message?: string;
|
|
1112
|
+
code?: string;
|
|
1113
|
+
} | null;
|
|
477
1114
|
}
|
|
478
1115
|
|
|
479
|
-
export declare function SessionProvider({ projectId, token, onSessionInvalid, apiBaseUrl, enforceActiveSubscription, children, }: SessionProviderProps): JSX_2.Element;
|
|
1116
|
+
export declare function SessionProvider({ projectId, token: tokenProp, onSessionInvalid: onSessionInvalidProp, apiBaseUrl, enforceActiveSubscription, onAccountAccessLost, onTokenChange, mode, children, }: SessionProviderProps): JSX_2.Element;
|
|
480
1117
|
|
|
481
1118
|
declare interface SessionProviderProps {
|
|
482
1119
|
projectId: string;
|
|
483
|
-
/**
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
1120
|
+
/**
|
|
1121
|
+
* The session token. **Omit it** to use the token the package stored at
|
|
1122
|
+
* login; pass `null` to state explicitly that there is no session.
|
|
1123
|
+
*/
|
|
1124
|
+
token?: string | null;
|
|
1125
|
+
/**
|
|
1126
|
+
* Called when the session is invalid or the user logged out — navigate to
|
|
1127
|
+
* your login route here. Optional: with no handler the provider sends the
|
|
1128
|
+
* browser to the package's login route (see `SSO_ROUTES`).
|
|
1129
|
+
*/
|
|
1130
|
+
onSessionInvalid?: () => void;
|
|
487
1131
|
apiBaseUrl?: string;
|
|
488
1132
|
/**
|
|
489
1133
|
* When true, after the session validates we fetch the user's subscription; if
|
|
@@ -491,9 +1135,50 @@ declare interface SessionProviderProps {
|
|
|
491
1135
|
* so they can subscribe. The modal stays open but the user can still close it.
|
|
492
1136
|
*/
|
|
493
1137
|
enforceActiveSubscription?: boolean;
|
|
1138
|
+
/**
|
|
1139
|
+
* Called when the session is rejected with `A403` — the member's access to
|
|
1140
|
+
* **this** account was revoked or expired, or the account was disabled. Their
|
|
1141
|
+
* login is intact and their other accounts still work, so this is not a
|
|
1142
|
+
* logout: send them back to the account picker.
|
|
1143
|
+
*
|
|
1144
|
+
* Leave it unset to let the provider render the picker itself (see
|
|
1145
|
+
* {@link onTokenChange}).
|
|
1146
|
+
*/
|
|
1147
|
+
onAccountAccessLost?: (info: {
|
|
1148
|
+
message?: string;
|
|
1149
|
+
code?: string;
|
|
1150
|
+
}) => void;
|
|
1151
|
+
/**
|
|
1152
|
+
* Called with a fresh token when the user switches account from the
|
|
1153
|
+
* built-in picker. Store it in place of the old one — without this the
|
|
1154
|
+
* provider has nowhere to hand the new session, so it falls back to
|
|
1155
|
+
* `onSessionInvalid`.
|
|
1156
|
+
*/
|
|
1157
|
+
onTokenChange?: (token: string) => void;
|
|
1158
|
+
/** Colour scheme for the built-in picker — "light" (default) or "dark". */
|
|
1159
|
+
mode?: AuthMode;
|
|
494
1160
|
children: ReactNode;
|
|
495
1161
|
}
|
|
496
1162
|
|
|
1163
|
+
/** Outcome of {@link validateSession}. */
|
|
1164
|
+
export declare interface SessionValidation {
|
|
1165
|
+
valid: boolean;
|
|
1166
|
+
/**
|
|
1167
|
+
* The session failed *because this account is no longer reachable* (member
|
|
1168
|
+
* access revoked or expired, account disabled). The login itself is still
|
|
1169
|
+
* good, so the user should be sent back to the account picker rather than
|
|
1170
|
+
* logged out. See `isAccountAccessError`.
|
|
1171
|
+
*/
|
|
1172
|
+
accountAccessLost: boolean;
|
|
1173
|
+
/** Backend message, when it sent one. */
|
|
1174
|
+
message?: string;
|
|
1175
|
+
/** Backend error code — `A403` for the revoked/expired case. */
|
|
1176
|
+
code?: string;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
/** Writes the token. A falsy token clears it, so this is safe to call blindly. */
|
|
1180
|
+
export declare function setStoredToken(token: string, options?: TokenAccessOptions): void;
|
|
1181
|
+
|
|
497
1182
|
/** Every section that can appear in the hash. Order is not significant. */
|
|
498
1183
|
export declare const SETTINGS_SECTIONS: AccountSettingsSection[];
|
|
499
1184
|
|
|
@@ -501,6 +1186,21 @@ export declare function Signup(props: SignupProps): JSX_2.Element;
|
|
|
501
1186
|
|
|
502
1187
|
export declare interface SignupProps extends AuthComponentBaseProps {
|
|
503
1188
|
onLoginClick?: () => void;
|
|
1189
|
+
/**
|
|
1190
|
+
* Invitation token from an invite link. Sent as `inviteToken`, which lets the
|
|
1191
|
+
* signup through even when the project has signups disabled and creates the
|
|
1192
|
+
* account already verified and active (no verification email).
|
|
1193
|
+
*
|
|
1194
|
+
* `AcceptInvite` sets this for you; pass it yourself only if you're building
|
|
1195
|
+
* the invite screen by hand.
|
|
1196
|
+
*/
|
|
1197
|
+
inviteToken?: string;
|
|
1198
|
+
/**
|
|
1199
|
+
* The invited address, from `GET /invitations/:token`. Prefills the email
|
|
1200
|
+
* field and makes it read-only — the server takes the address from the
|
|
1201
|
+
* invite record regardless of what's posted.
|
|
1202
|
+
*/
|
|
1203
|
+
inviteEmail?: string;
|
|
504
1204
|
/**
|
|
505
1205
|
* Custom country list for the country dropdown. When provided it replaces the
|
|
506
1206
|
* built-in list; each entry must be `{ code, name }`. Falls back to the
|
|
@@ -515,18 +1215,238 @@ export declare interface SignupRequest {
|
|
|
515
1215
|
email: string;
|
|
516
1216
|
password: string;
|
|
517
1217
|
phoneNumber?: string;
|
|
1218
|
+
/** Country calling code without the `+`, e.g. "91". */
|
|
1219
|
+
dialCode?: string;
|
|
518
1220
|
country?: string;
|
|
519
1221
|
currency?: string;
|
|
520
1222
|
captchaToken?: string;
|
|
1223
|
+
/**
|
|
1224
|
+
* Invitation token, when the user is signing up from an invite link. The
|
|
1225
|
+
* server then takes the email from the invite record and **ignores** the
|
|
1226
|
+
* `email` field here; signup is also allowed even when the project has
|
|
1227
|
+
* signups disabled, and the account is created already verified.
|
|
1228
|
+
*/
|
|
1229
|
+
inviteToken?: string;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Renders a single auth screen, wired up for routing: sibling links navigate,
|
|
1234
|
+
* and a successful sign-in stores the token and leaves for `redirectTo`.
|
|
1235
|
+
*
|
|
1236
|
+
* Built for file-based routing — one tiny file per route:
|
|
1237
|
+
*
|
|
1238
|
+
* ```tsx
|
|
1239
|
+
* // app/login/page.tsx
|
|
1240
|
+
* export default () => <SsoAuthPage screen="login" projectId="…" />;
|
|
1241
|
+
* ```
|
|
1242
|
+
*/
|
|
1243
|
+
export declare function SsoAuthPage({ screen, routeToken, ...props }: SsoAuthPageProps): JSX_2.Element;
|
|
1244
|
+
|
|
1245
|
+
export declare interface SsoAuthPageProps extends SsoScreenProps {
|
|
1246
|
+
/** Which screen to render. */
|
|
1247
|
+
screen: SsoScreen;
|
|
1248
|
+
/**
|
|
1249
|
+
* Token from the URL for the reset / verify / invite screens. Pass it when
|
|
1250
|
+
* your router already parsed it (Next's `params`/`searchParams`); omitted,
|
|
1251
|
+
* it's read from `window.location`.
|
|
1252
|
+
*/
|
|
1253
|
+
routeToken?: string;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* Mounts every auth route without needing a router: it matches
|
|
1258
|
+
* `window.location.pathname` itself and renders the right screen, or
|
|
1259
|
+
* `fallback` when the path isn't one of ours.
|
|
1260
|
+
*
|
|
1261
|
+
* ```tsx
|
|
1262
|
+
* <SsoAuthRoutes projectId="…" redirectTo="/dashboard" fallback={<App />} />
|
|
1263
|
+
* ```
|
|
1264
|
+
*
|
|
1265
|
+
* If you *do* have a router, prefer `ssoRoutes()` so the screens live inside
|
|
1266
|
+
* your layouts and guards.
|
|
1267
|
+
*/
|
|
1268
|
+
export declare function SsoAuthRoutes({ fallback, ...props }: SsoAuthRoutesProps): JSX_2.Element;
|
|
1269
|
+
|
|
1270
|
+
export declare interface SsoAuthRoutesProps extends SsoScreenProps {
|
|
1271
|
+
/** Rendered when the current path isn't one of the auth routes. Default `null`. */
|
|
1272
|
+
fallback?: ReactNode;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/**
|
|
1276
|
+
* Default navigation: a `history.pushState` plus an event so `SsoAuthRoutes`
|
|
1277
|
+
* re-renders.
|
|
1278
|
+
*
|
|
1279
|
+
* This is right for apps with **no** router. If you have one, pass its
|
|
1280
|
+
* navigate through `onNavigate` instead — react-router and friends own their
|
|
1281
|
+
* own history and won't notice a pushState from outside.
|
|
1282
|
+
*/
|
|
1283
|
+
export declare function ssoNavigate(to: string): void;
|
|
1284
|
+
|
|
1285
|
+
export declare interface SsoRouteMatch {
|
|
1286
|
+
/** Which screen the path resolves to. */
|
|
1287
|
+
screen: SsoScreen;
|
|
1288
|
+
/** Token carried by the URL (reset / verify / invite links). */
|
|
1289
|
+
token: string;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
export declare interface SsoRouteObject {
|
|
1293
|
+
/** Path, prefixed with `basePath` when one is given. */
|
|
1294
|
+
path: string;
|
|
1295
|
+
/** The screen, ready to mount. */
|
|
1296
|
+
element: ReactNode;
|
|
1297
|
+
/** Which screen this is, in case you need to wrap or reorder them. */
|
|
1298
|
+
screen: SsoScreen;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
/**
|
|
1302
|
+
* The routes every app embedding this package needs.
|
|
1303
|
+
*
|
|
1304
|
+
* A library can't discover a host app's routes — react-router keeps no readable
|
|
1305
|
+
* registry at runtime and Next's routes are filesystem/build-time — so instead
|
|
1306
|
+
* of guessing, the package publishes the paths it expects and gives you two
|
|
1307
|
+
* ways to satisfy them: spread `ssoRoutes()` into your own router, or mount
|
|
1308
|
+
* `<SsoAuthRoutes />` and let the package match the path itself.
|
|
1309
|
+
*
|
|
1310
|
+
* ⚠️ Whichever you pick, **your server has to serve the app shell for these
|
|
1311
|
+
* paths**. If `/accept-invite` isn't rewritten to `index.html`, the user gets a
|
|
1312
|
+
* 404 before any JavaScript runs, and no client-side package can fix that.
|
|
1313
|
+
*/
|
|
1314
|
+
export declare interface SsoRoutePaths {
|
|
1315
|
+
login: string;
|
|
1316
|
+
signup: string;
|
|
1317
|
+
forgotPassword: string;
|
|
1318
|
+
/** Target of the emailed reset link. Token read from `?token=` or a trailing segment. */
|
|
1319
|
+
resetPassword: string;
|
|
1320
|
+
/** Target of the emailed verification link, e.g. `/verify/<token>`. */
|
|
1321
|
+
verifyEmail: string;
|
|
1322
|
+
/** Target of invite links — see `inviteLinkBase` on the settings modal. */
|
|
1323
|
+
acceptInvite: string;
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* The auth screens as plain route objects, to spread into your own router.
|
|
1328
|
+
* Shaped to drop straight into react-router:
|
|
1329
|
+
*
|
|
1330
|
+
* ```tsx
|
|
1331
|
+
* const navigate = useNavigate();
|
|
1332
|
+
* createBrowserRouter([
|
|
1333
|
+
* ...ssoRoutes({ projectId: '…', onNavigate: navigate }),
|
|
1334
|
+
* ...appRoutes,
|
|
1335
|
+
* ]);
|
|
1336
|
+
* ```
|
|
1337
|
+
*
|
|
1338
|
+
* Pass `onNavigate` so the screens move through *your* router — the default
|
|
1339
|
+
* `pushState` won't re-render it. Token-bearing routes are registered with a
|
|
1340
|
+
* `:token` segment as well as the bare path, since the links arrive both ways.
|
|
1341
|
+
*/
|
|
1342
|
+
export declare function ssoRoutes(options: SsoScreenProps): SsoRouteObject[];
|
|
1343
|
+
|
|
1344
|
+
export declare type SsoScreen = 'login' | 'signup' | 'forgot-password' | 'reset-password' | 'verify-email' | 'accept-invite';
|
|
1345
|
+
|
|
1346
|
+
export declare interface SsoScreenProps {
|
|
1347
|
+
projectId: string;
|
|
1348
|
+
apiBaseUrl?: string;
|
|
1349
|
+
/** Pre-fetched config, to skip the internal auth-config request. */
|
|
1350
|
+
config?: AuthConfig;
|
|
1351
|
+
mode?: AuthMode;
|
|
1352
|
+
classNames?: AuthClassNames;
|
|
1353
|
+
/** Custom country list for the signup screen. */
|
|
1354
|
+
countries?: CountryOption[];
|
|
1355
|
+
/** Client identifier recorded on the session when an account is chosen. */
|
|
1356
|
+
client?: string;
|
|
1357
|
+
/** Persist the issued token. Default `true`. */
|
|
1358
|
+
persistToken?: boolean;
|
|
1359
|
+
tokenStorageKey?: string;
|
|
1360
|
+
/**
|
|
1361
|
+
* Live session token, used by the accept-invite "accept" branch. Defaults to
|
|
1362
|
+
* the stored token, so an already-signed-in user can accept straight away.
|
|
1363
|
+
*/
|
|
1364
|
+
token?: string;
|
|
1365
|
+
/** Where to go after a successful sign-in. Default `"/"`; a safe `?next=` wins. */
|
|
1366
|
+
redirectTo?: string;
|
|
1367
|
+
/**
|
|
1368
|
+
* Replaces the default post-auth behaviour (a full navigation to
|
|
1369
|
+
* `redirectTo`, so the host app boots with the stored token). Use it to hook
|
|
1370
|
+
* into your own router instead.
|
|
1371
|
+
*/
|
|
1372
|
+
onAuthenticated?: (result: unknown) => void;
|
|
1373
|
+
onError?: (error: Error) => void;
|
|
1374
|
+
/** How to move between auth screens. Defaults to {@link ssoNavigate}. */
|
|
1375
|
+
onNavigate?: (to: string) => void;
|
|
1376
|
+
/** Prefix the routes are mounted under, e.g. `"/auth"`. */
|
|
1377
|
+
basePath?: string;
|
|
1378
|
+
/** Path overrides for this mount, on top of `configureSsoRoutes()`. */
|
|
1379
|
+
routes?: Partial<SsoRoutePaths>;
|
|
521
1380
|
}
|
|
522
1381
|
|
|
523
|
-
|
|
1382
|
+
/**
|
|
1383
|
+
* POST /auth/switch-account — swaps a *live* session onto another account the
|
|
1384
|
+
* user belongs to, returning a fresh token. The old token stays valid until it
|
|
1385
|
+
* expires or is logged out, so replace whatever you stored with the new one.
|
|
1386
|
+
*/
|
|
1387
|
+
export declare function switchAccountApi(projectId: string, accountId: string, token: string, apiBaseUrl?: string): Promise<string>;
|
|
1388
|
+
|
|
1389
|
+
export declare interface TeamSectionProps {
|
|
1390
|
+
projectId: string;
|
|
1391
|
+
/** Omit to use the stored session token. */
|
|
1392
|
+
token?: string;
|
|
1393
|
+
apiBaseUrl?: string;
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
declare interface TokenAccessOptions {
|
|
1397
|
+
/** Key override for this call. */
|
|
1398
|
+
key?: string;
|
|
1399
|
+
/** Storage override for this call. */
|
|
1400
|
+
storage?: TokenStorageKind;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
export declare interface TokenStorageConfig {
|
|
1404
|
+
/** Storage key. Default `"ab-sso:accessToken"`. */
|
|
1405
|
+
key: string;
|
|
1406
|
+
/** Where the token is kept. Default `"localStorage"`. */
|
|
1407
|
+
storage: TokenStorageKind;
|
|
1408
|
+
/** Master switch for reading *and* writing. Default `true`. */
|
|
1409
|
+
enabled: boolean;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* Session-token storage.
|
|
1414
|
+
*
|
|
1415
|
+
* The package persists the token itself so hosts don't have to re-implement it
|
|
1416
|
+
* in every app. Everything funnels through here so there is exactly one key,
|
|
1417
|
+
* one place that writes it, and one place that clears it.
|
|
1418
|
+
*
|
|
1419
|
+
* Design notes:
|
|
1420
|
+
*
|
|
1421
|
+
* - **One key, namespaced by default** (`ab-sso:accessToken`). A bare
|
|
1422
|
+
* `accessToken` collides with whatever the host app already keeps under that
|
|
1423
|
+
* very common name; set `key` if you need to match an existing app.
|
|
1424
|
+
* - **Every token source writes here** — login, verify-2fa, select-account,
|
|
1425
|
+
* switch-account, signup, accept-invite and the magic-link path. Missing one
|
|
1426
|
+
* is worse than persisting nothing: `switch-account` in particular issues a
|
|
1427
|
+
* *fresh* token, and a stale stored copy means the user silently keeps acting
|
|
1428
|
+
* as the previous account.
|
|
1429
|
+
* - **Storage can fail.** Private windows, disabled site data and quota errors
|
|
1430
|
+
* all throw on access, so every read/write is guarded and falls back to an
|
|
1431
|
+
* in-memory value that lasts for the page's lifetime.
|
|
1432
|
+
*
|
|
1433
|
+
* A bearer token in `localStorage` is readable by any script on the origin.
|
|
1434
|
+
* That is the same thing hosts were doing by hand, but it is now the default —
|
|
1435
|
+
* use `configureTokenStorage({ storage: 'sessionStorage' })`, or
|
|
1436
|
+
* `{ enabled: false }` to opt out entirely and keep handling it yourself.
|
|
1437
|
+
*/
|
|
1438
|
+
export declare type TokenStorageKind = 'localStorage' | 'sessionStorage' | 'memory' | 'none';
|
|
1439
|
+
|
|
1440
|
+
export declare function TwoFactorSetup({ projectId, token: tokenProp, apiBaseUrl, className, mode, onEnabled, onDisabled, onError, }: TwoFactorSetupProps): JSX_2.Element;
|
|
524
1441
|
|
|
525
1442
|
declare interface TwoFactorSetupProps {
|
|
526
1443
|
projectId: string;
|
|
527
|
-
token
|
|
1444
|
+
/** Session token. Omit to use the one the package stored at login. */
|
|
1445
|
+
token?: string;
|
|
528
1446
|
apiBaseUrl?: string;
|
|
529
1447
|
className?: string;
|
|
1448
|
+
/** Colour scheme — "light" (default) or "dark". */
|
|
1449
|
+
mode?: AuthMode;
|
|
530
1450
|
onEnabled?: () => void;
|
|
531
1451
|
onDisabled?: () => void;
|
|
532
1452
|
onError?: (error: Error) => void;
|
|
@@ -546,6 +1466,35 @@ export declare interface TwoFactorVerifySetupRequest {
|
|
|
546
1466
|
secret: string;
|
|
547
1467
|
}
|
|
548
1468
|
|
|
1469
|
+
export declare interface UnauthorizedInfo {
|
|
1470
|
+
/** The request that came back unauthorized. */
|
|
1471
|
+
url: string;
|
|
1472
|
+
status: number;
|
|
1473
|
+
message?: string;
|
|
1474
|
+
code?: string;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
export declare function updateMemberApi(projectId: string, memberId: string, payload: UpdateMemberPayload, token?: string, apiBaseUrl?: string): Promise<unknown>;
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* Partial. Pass `role: null` to strip permissions and `access_expiry: null` to
|
|
1481
|
+
* remove an expiry — omitting a field leaves it alone, so the two cases are
|
|
1482
|
+
* genuinely different and the body is built from the keys actually present.
|
|
1483
|
+
*/
|
|
1484
|
+
export declare interface UpdateMemberPayload {
|
|
1485
|
+
role?: string | null;
|
|
1486
|
+
access_expiry?: string | null;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
export declare function updateRoleApi(projectId: string, roleId: string, payload: UpdateRolePayload, token?: string, apiBaseUrl?: string): Promise<unknown>;
|
|
1490
|
+
|
|
1491
|
+
/** Partial — omitted fields are left alone; `permission` replaces the array wholesale. */
|
|
1492
|
+
export declare interface UpdateRolePayload {
|
|
1493
|
+
title?: string;
|
|
1494
|
+
description?: string;
|
|
1495
|
+
permission?: string[];
|
|
1496
|
+
}
|
|
1497
|
+
|
|
549
1498
|
export declare function useAuthConfig(): AuthConfigContextValue;
|
|
550
1499
|
|
|
551
1500
|
/** Like {@link useSession} but returns null instead of throwing when there is no provider. */
|
|
@@ -561,6 +1510,13 @@ export declare interface UserWallets {
|
|
|
561
1510
|
|
|
562
1511
|
export declare function useSession(): SessionContextValue;
|
|
563
1512
|
|
|
1513
|
+
/**
|
|
1514
|
+
* GET /me, but reporting *why* a session was rejected instead of just a
|
|
1515
|
+
* boolean — `SessionProvider` needs to tell "logged out" apart from "this one
|
|
1516
|
+
* account went away".
|
|
1517
|
+
*/
|
|
1518
|
+
export declare function validateSession(projectId: string, token: string, apiBaseUrl?: string): Promise<SessionValidation>;
|
|
1519
|
+
|
|
564
1520
|
/**
|
|
565
1521
|
* Email-verification screen for a `/verify/:token` route.
|
|
566
1522
|
*
|