@authowl/core 0.22.1 → 0.24.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 +13 -1
- package/dist/chunk-LBCOMERN.js +1 -0
- package/dist/chunk-YL2FF2VS.js +2 -0
- package/dist/{client-BwZ1bqU4.d.cts → config-ZhNp65dI.d.cts} +496 -254
- package/dist/{client-DXypEWpf.d.ts → config-ZhNp65dI.d.ts} +496 -254
- package/dist/{idempotency-BWMPd3B7.d.ts → idempotency-BbNZaxT8.d.ts} +24 -24
- package/dist/{idempotency-BfRYS8qn.d.cts → idempotency-CG0ZpMnW.d.cts} +24 -24
- package/dist/index.cjs +3 -3
- package/dist/index.d.cts +19 -5
- package/dist/index.d.ts +19 -5
- package/dist/index.js +3 -3
- package/dist/messages.cjs +1 -1
- package/dist/messages.d.cts +8 -0
- package/dist/messages.d.ts +8 -0
- package/dist/messages.js +1 -1
- package/dist/native.cjs +2 -2
- package/dist/native.d.cts +7 -8
- package/dist/native.d.ts +7 -8
- package/dist/native.js +1 -1
- package/dist/privacy.d.cts +2 -2
- package/dist/privacy.d.ts +2 -2
- package/dist/public-config-BfuiC8Ph.d.ts +241 -0
- package/dist/public-config-DdTDwDF7.d.cts +241 -0
- package/dist/server.cjs +2 -2
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-DVVGVPDH.js +0 -1
- package/dist/chunk-IB6TFQFD.js +0 -2
- package/dist/organization-membership-BIify9hZ.d.cts +0 -450
- package/dist/organization-membership-BIify9hZ.d.ts +0 -450
|
@@ -1,286 +1,265 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*
|
|
6
|
-
* The link lands on the TENANT'S OWN page carrying `?authowl_invitation=<id>`,
|
|
7
|
-
* and the only route that can redeem it needs a session the visitor usually does
|
|
8
|
-
* not have yet. So the id has to survive an entire sign-up or sign-in - which
|
|
9
|
-
* means surviving that flow's redirects: the OAuth round trip, the email
|
|
10
|
-
* verification round trip (which can land on a different page entirely), the MFA
|
|
11
|
-
* hold, and whatever navigation the operator's own `redirectTo` performs. A query
|
|
12
|
-
* parameter survives none of those, so it is read once, moved into storage, and
|
|
13
|
-
* taken out of the URL.
|
|
14
|
-
*
|
|
15
|
-
* Best-effort storage, deliberately: a browser that refuses `localStorage`
|
|
16
|
-
* degrades to "this invitation is claimable until you navigate", which is worse
|
|
17
|
-
* than the ideal and much better than a sign-in that throws.
|
|
18
|
-
*/
|
|
19
|
-
declare const INVITATION_QUERY_PARAM = "authowl_invitation";
|
|
20
|
-
/**
|
|
21
|
-
* The only hint value that exists. There is deliberately no `existing_user`:
|
|
22
|
-
* absence has to stay ambiguous across an existing account, an older AuthOwl,
|
|
23
|
-
* and an account created between the invite and the click - so it can never be
|
|
24
|
-
* read as proof that an address IS registered.
|
|
25
|
-
*/
|
|
26
|
-
type InvitationRecipientHint = 'new_user';
|
|
27
|
-
type InvitationClaim = {
|
|
28
|
-
id: string;
|
|
29
|
-
/** Epoch milliseconds, for age display and for expiring a forgotten claim. */
|
|
30
|
-
capturedAt: number;
|
|
1
|
+
type DecodedPublishableKey = {
|
|
2
|
+
prefix: 'pk_live' | 'pk_test';
|
|
3
|
+
env: 'live' | 'test';
|
|
4
|
+
projectId: string;
|
|
31
5
|
};
|
|
32
|
-
|
|
33
|
-
declare const INVITATION_CLAIM_MAX_AGE_MS: number;
|
|
34
|
-
/**
|
|
35
|
-
* Read `?authowl_invitation` once, stash it, and strip it from the URL.
|
|
36
|
-
*
|
|
37
|
-
* Namespaced and only ours - a bare `invitation` belongs to the tenant's app as
|
|
38
|
-
* much as to us, and rewriting their parameters is not ours to do. Returns the
|
|
39
|
-
* captured claim, or the one already stashed when there is no parameter, so a
|
|
40
|
-
* caller can render from a single call.
|
|
41
|
-
*/
|
|
42
|
-
declare function captureInvitationClaim(now?: number): InvitationClaim | null;
|
|
43
|
-
/** The stashed claim, or null when there is none, it is unreadable, or it is stale. */
|
|
44
|
-
declare function readInvitationClaim(now?: number): InvitationClaim | null;
|
|
45
|
-
declare function clearInvitationClaim(): void;
|
|
6
|
+
declare function decodePublishableKey(key: string): DecodedPublishableKey;
|
|
46
7
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
8
|
+
/** How a session BEGINS, as the minting door knows it. */
|
|
9
|
+
type SessionStart = {
|
|
10
|
+
/**
|
|
11
|
+
* Whether the session is meant to outlive the tab.
|
|
12
|
+
*
|
|
13
|
+
* `dont_remember` is a SECOND cookie, and on the browsers this transport
|
|
14
|
+
* exists for it is dropped exactly like the session cookie is - so the engine
|
|
15
|
+
* never sees it and treats every bearer session as persistent. The token is
|
|
16
|
+
* the only copy of that session the SDK controls, so "don't remember me" has
|
|
17
|
+
* to be honoured by WHERE it is kept: `sessionStorage`, which dies with the
|
|
18
|
+
* tab, rather than `localStorage`, which does not.
|
|
19
|
+
*
|
|
20
|
+
* NOTE the behaviour change that buys: `sessionStorage` is per TAB, where
|
|
21
|
+
* `dont_remember` was per browser session. A "don't remember me" bearer
|
|
22
|
+
* session is therefore not shared with other tabs. That is deliberate and it
|
|
23
|
+
* is the safe direction (a session that ends too soon rather than one that
|
|
24
|
+
* outlives the machine's owner), but it is a real difference from the cookie
|
|
25
|
+
* transport and callers should not discover it by accident.
|
|
26
|
+
*/
|
|
27
|
+
remember: boolean;
|
|
28
|
+
};
|
|
29
|
+
/** Secret-free events for framework adapters that project the current session. */
|
|
30
|
+
type SessionLifecycleEvent = Readonly<{
|
|
31
|
+
type: 'beginSession';
|
|
32
|
+
remember: boolean;
|
|
33
|
+
}> | Readonly<{
|
|
34
|
+
type: 'endSession';
|
|
35
|
+
}>;
|
|
53
36
|
/**
|
|
54
|
-
*
|
|
37
|
+
* One session read, from the moment it is dispatched to the moment it answers.
|
|
55
38
|
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* the slug lets the renderer say which provider it does not know.
|
|
39
|
+
* Cut by the store, so the rule about which answers may be acted on lives with
|
|
40
|
+
* the state it protects rather than being restated by every caller. The token
|
|
41
|
+
* the read is about is captured INSIDE it: `hasToken` explains why nothing hands
|
|
42
|
+
* the credential out, and a receipt that leaked it would be the same hole with a
|
|
43
|
+
* shorter lifetime.
|
|
62
44
|
*/
|
|
63
|
-
|
|
64
|
-
provider: string;
|
|
65
|
-
siteKey: string;
|
|
66
|
-
}
|
|
67
|
-
type PublicConfig = {
|
|
68
|
-
/** Stable workspace product container shared by its environments. */
|
|
69
|
-
applicationId: string;
|
|
70
|
-
/** Stable tenant id for the exact environment selected by the publishable key. */
|
|
71
|
-
environmentId: string;
|
|
72
|
-
/** Environment class that determines key prefixes and billing treatment. */
|
|
73
|
-
environmentType: EnvironmentType;
|
|
45
|
+
type SessionRead = {
|
|
74
46
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
47
|
+
* Whether the read this receipt was cut for actually presented a token.
|
|
48
|
+
*
|
|
49
|
+
* The cookie measurement turns on it: a read that carried NO token and still
|
|
50
|
+
* found a session IS the proof that our cross-site cookie survives here.
|
|
78
51
|
*/
|
|
79
|
-
|
|
52
|
+
readonly carriedToken: boolean;
|
|
80
53
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
54
|
+
* The read came back with no session. End the session it was reading - unless
|
|
55
|
+
* the token it presented is no longer the session in play.
|
|
56
|
+
*
|
|
57
|
+
* This is the path-independent catch for every ending that is not sign-out:
|
|
58
|
+
* expiry, a revoke from another device, `account.revokeSession` called with
|
|
59
|
+
* your own id, `account.delete`, an admin ban. None of those are visible in
|
|
60
|
+
* the response to the call that caused them, and a token we PRESENTED and got
|
|
61
|
+
* nothing back for is dead by definition.
|
|
83
62
|
*/
|
|
84
|
-
|
|
85
|
-
mode: 'open' | 'restricted' | 'allowlist' | 'waitlist';
|
|
86
|
-
};
|
|
63
|
+
endIfDead(): void;
|
|
87
64
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
65
|
+
* Record what this read proved about the browser's cookies. `true` means the
|
|
66
|
+
* cookie came back and no token is needed here.
|
|
67
|
+
*
|
|
68
|
+
* On the receipt rather than on the store, because a detached read answers
|
|
69
|
+
* "there is no cookie session" for two completely different reasons, and only
|
|
70
|
+
* one of them is about the browser. A probe dispatched for one session and
|
|
71
|
+
* answered after that session ENDED - a sign-out, or a sign-out and a fresh
|
|
72
|
+
* sign-in, both of which fit inside one probe's round trip - reports the
|
|
73
|
+
* signed-out gap as a broken cookie. Acting on it would record `bearer` on a
|
|
74
|
+
* browser whose cookies are fine and then persist the NEXT session's token to
|
|
75
|
+
* disk under it, which is the exact defect this gate exists to remove, and it
|
|
76
|
+
* would stick until the sign-in after next.
|
|
77
|
+
*
|
|
78
|
+
* Two facts here, with different authorities, and reading them as one is what
|
|
79
|
+
* this comment used to license. RECORDING the verdict needs no token-value
|
|
80
|
+
* comparison of the kind `endIfDead` does: it is a statement about the
|
|
81
|
+
* BROWSER, every tab on this origin is the same browser, and a verdict
|
|
82
|
+
* wrongly skipped only costs another measurement. The WRITE it unlocks is the
|
|
83
|
+
* opposite - the token this receipt closed over is at least a probe round trip
|
|
84
|
+
* old, and across tabs arbitrarily old, so flushing it unguarded puts a
|
|
85
|
+
* superseded credential into the slot every tab shares. See `flushHeldToken`,
|
|
86
|
+
* which is where that write goes and why.
|
|
90
87
|
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
phone: {
|
|
97
|
-
signUp: boolean;
|
|
98
|
-
signIn: boolean;
|
|
99
|
-
};
|
|
100
|
-
password: {
|
|
101
|
-
signUp: boolean;
|
|
102
|
-
add: boolean;
|
|
103
|
-
/** Server-owned password length policy. Optional for rolling compatibility. */
|
|
104
|
-
minLength?: number;
|
|
105
|
-
maxLength?: number;
|
|
106
|
-
};
|
|
107
|
-
passkey: {
|
|
108
|
-
signIn: boolean;
|
|
109
|
-
add: boolean;
|
|
110
|
-
/**
|
|
111
|
-
* The domain this environment binds passkeys to. Absent or null means the
|
|
112
|
-
* server derives it from the auth host, which is what every server before
|
|
113
|
-
* this field did - so treating both the same keeps a new SDK correct
|
|
114
|
-
* against an older server.
|
|
115
|
-
*/
|
|
116
|
-
relyingPartyId?: string | null;
|
|
117
|
-
};
|
|
118
|
-
username: {
|
|
119
|
-
collectOnSignUp: boolean;
|
|
120
|
-
signIn: boolean;
|
|
121
|
-
};
|
|
122
|
-
};
|
|
123
|
-
/** Email ownership ceremony selected by the project. */
|
|
124
|
-
emailVerification?: {
|
|
125
|
-
required: boolean;
|
|
126
|
-
method: 'link' | 'code';
|
|
127
|
-
};
|
|
128
|
-
/** End-user profile fields and self-service permissions. */
|
|
129
|
-
userModel?: {
|
|
130
|
-
requireEmail: boolean;
|
|
131
|
-
firstLastName: boolean;
|
|
132
|
-
emailChange: boolean;
|
|
133
|
-
accountDeletion: boolean;
|
|
134
|
-
};
|
|
88
|
+
recordCookieVerdict(cookiesWork: boolean): void;
|
|
89
|
+
};
|
|
90
|
+
type SessionTokenStore = {
|
|
91
|
+
/** Observe lifecycle changes without receiving the token or proof key. */
|
|
92
|
+
subscribeLifecycle(listener: (event: SessionLifecycleEvent) => void): () => void;
|
|
135
93
|
/**
|
|
136
|
-
*
|
|
137
|
-
*
|
|
94
|
+
* Whether a token is held. Deliberately not a getter for the token itself:
|
|
95
|
+
* `declareOn` is the only thing that puts it on the wire, so nothing else
|
|
96
|
+
* needs the credential in hand.
|
|
138
97
|
*/
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
backupCodes: boolean;
|
|
143
|
-
/** Whether an emailed code may recover a TOTP challenge. Defaults to true for older servers. */
|
|
144
|
-
emailOtpFallback?: boolean;
|
|
145
|
-
/** Whether a successful challenge may remember this browser. Defaults to true for older servers. */
|
|
146
|
-
trustDevice?: boolean;
|
|
147
|
-
};
|
|
148
|
-
branding: {
|
|
149
|
-
appName?: string;
|
|
150
|
-
logoUrl?: string;
|
|
151
|
-
/** Whether the application name is visible beside the logo. */
|
|
152
|
-
showAppName?: boolean;
|
|
153
|
-
/** Alignment of the brand identity within managed component headers. */
|
|
154
|
-
alignment?: 'left' | 'center' | 'right';
|
|
155
|
-
primaryColor?: string;
|
|
156
|
-
theme?: 'light' | 'dark' | 'system';
|
|
157
|
-
};
|
|
158
|
-
/** Canonical method slugs, e.g. "password", "magic_link", "passkey". */
|
|
159
|
-
enabledMethods: string[];
|
|
160
|
-
/** Configured social provider ids, e.g. "google". */
|
|
161
|
-
socialProviders: string[];
|
|
98
|
+
hasToken(): boolean;
|
|
99
|
+
/** Thumbprint paired with the current token, or null for a legacy unbound session. */
|
|
100
|
+
bindingThumbprint(): string | null;
|
|
162
101
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
102
|
+
* Put this request on the transport - EVERY header, or none of them.
|
|
103
|
+
*
|
|
104
|
+
* The declaration, the bearer when one is held, and the challenge when one is
|
|
105
|
+
* held. Returns whether it declared, which is also the only condition under
|
|
106
|
+
* which the response can carry either cargo back.
|
|
107
|
+
*
|
|
108
|
+
* The challenge rides here rather than at whichever call site knows it is
|
|
109
|
+
* doing 2FA, and that is the point of the shape: `dont_remember` outlives the
|
|
110
|
+
* challenge it arrived with - `get-session` reads it for the life of the
|
|
111
|
+
* session to decide expiry refresh - so a store presented only when answering
|
|
112
|
+
* a code leaves a "don't remember me" session quietly resuming refresh. A
|
|
113
|
+
* request that is on this transport at all is on it for both cargoes.
|
|
165
114
|
*/
|
|
166
|
-
|
|
115
|
+
declareOn(headers: Headers): boolean;
|
|
167
116
|
/**
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
117
|
+
* Whether this transport is still in play at all - for EITHER cargo, since one
|
|
118
|
+
* declaration governs both.
|
|
119
|
+
*
|
|
120
|
+
* False only once cookies are PROVEN to work here, after which `declareOn`
|
|
121
|
+
* changes nothing and no response can carry a token or a challenge back - so a
|
|
122
|
+
* caller may skip the decoration entirely rather than copy headers it will not
|
|
123
|
+
* touch. A browser that keeps our cookies keeps the ticket in its jar, where it
|
|
124
|
+
* is HttpOnly and strictly safer than anything a header can carry.
|
|
171
125
|
*/
|
|
172
|
-
|
|
126
|
+
wantsToken(): boolean;
|
|
127
|
+
/** Whether this browser's cookie behaviour is still unmeasured. */
|
|
128
|
+
needsProbe(): boolean;
|
|
173
129
|
/**
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
130
|
+
* Capture a token the server handed back, and put it where it belongs.
|
|
131
|
+
*
|
|
132
|
+
* "Where it belongs" is MEMORY until the cookie has demonstrably failed. The
|
|
133
|
+
* verdict is not knowable at the moment of capture and the token is not needed
|
|
134
|
+
* on most browsers, so the safe order is to hold it, ask, and write only if
|
|
135
|
+
* the answer says this browser has no other way to keep a session. Capturing
|
|
136
|
+
* one is also what ASKS the question - see `measureWith` - because a gate on a
|
|
137
|
+
* measurement nobody runs is just a session that dies at the next reload.
|
|
178
138
|
*/
|
|
179
|
-
|
|
180
|
-
termsUrl?: string;
|
|
181
|
-
privacyUrl?: string;
|
|
182
|
-
version: number;
|
|
183
|
-
required: boolean;
|
|
184
|
-
};
|
|
185
|
-
/** Published, bilingual privacy notices and optional consent purposes. */
|
|
186
|
-
privacy?: {
|
|
187
|
-
notices: Array<{
|
|
188
|
-
noticeId: string;
|
|
189
|
-
noticeVersionId: string;
|
|
190
|
-
code: string;
|
|
191
|
-
version: number;
|
|
192
|
-
title: {
|
|
193
|
-
en: string;
|
|
194
|
-
ar: string;
|
|
195
|
-
};
|
|
196
|
-
body: {
|
|
197
|
-
en: string;
|
|
198
|
-
ar: string;
|
|
199
|
-
};
|
|
200
|
-
digest: {
|
|
201
|
-
en: string;
|
|
202
|
-
ar: string;
|
|
203
|
-
};
|
|
204
|
-
activityCodes: string[];
|
|
205
|
-
purposeCodes: string[];
|
|
206
|
-
effectiveFrom: string;
|
|
207
|
-
}>;
|
|
208
|
-
consentPurposes: Array<{
|
|
209
|
-
purposeId: string;
|
|
210
|
-
purposeVersionId: string;
|
|
211
|
-
code: string;
|
|
212
|
-
version: number;
|
|
213
|
-
title: {
|
|
214
|
-
en: string;
|
|
215
|
-
ar: string;
|
|
216
|
-
};
|
|
217
|
-
description: {
|
|
218
|
-
en: string;
|
|
219
|
-
ar: string;
|
|
220
|
-
};
|
|
221
|
-
digest: {
|
|
222
|
-
en: string;
|
|
223
|
-
ar: string;
|
|
224
|
-
};
|
|
225
|
-
activityCodes: string[];
|
|
226
|
-
dataCategories: string[];
|
|
227
|
-
}>;
|
|
228
|
-
};
|
|
139
|
+
observe(headers: Headers): void;
|
|
229
140
|
/**
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
-
*
|
|
141
|
+
* The server rejected the challenge this store presented: the ticket is spent,
|
|
142
|
+
* expired, or was never there, and the user has to start at the first factor
|
|
143
|
+
* again.
|
|
144
|
+
*
|
|
145
|
+
* Separate from `endSession` because no session ended - the challenge one was
|
|
146
|
+
* pending on did. Keeping the store instead would present a dead ticket on
|
|
147
|
+
* every later request, and keep a `dont_remember` that no longer describes
|
|
148
|
+
* anything, for as long as the tab lives.
|
|
234
149
|
*/
|
|
235
|
-
|
|
236
|
-
/** Whether enrolled MFA is mandatory rather than optional for this project. */
|
|
237
|
-
mfaRequired: boolean;
|
|
238
|
-
/** Whether signed-in users may delete their own account. */
|
|
239
|
-
accountDeletion: boolean;
|
|
240
|
-
/** Whether organization routes and components are available for this project. */
|
|
241
|
-
organizations: boolean;
|
|
150
|
+
dropChallenge(): void;
|
|
242
151
|
/**
|
|
243
|
-
*
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
152
|
+
* Register the one thing that can actually settle the verdict: a session read
|
|
153
|
+
* with the token deliberately detached. The store holds the question and every
|
|
154
|
+
* fact bearing on it, and none of the network.
|
|
155
|
+
*
|
|
156
|
+
* Called the moment a token is captured with the verdict still unmeasured, and
|
|
157
|
+
* at registration if that already happened. Registration is what makes this
|
|
158
|
+
* work at all: a controller registers when a CLIENT is built, so the
|
|
159
|
+
* measurement no longer waits on a host app subscribing to the session store -
|
|
160
|
+
* which is a thing plenty of integrations never do, and every one of them used
|
|
161
|
+
* to keep a durable token for it.
|
|
162
|
+
*
|
|
163
|
+
* At most one measurer. A second registration replaces the first rather than
|
|
164
|
+
* joining it, because StrictMode double-invokes the memo that builds a client
|
|
165
|
+
* and two probes answer one question.
|
|
248
166
|
*/
|
|
249
|
-
|
|
167
|
+
measureWith(measure: () => void): void;
|
|
250
168
|
/**
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
169
|
+
* Cut a receipt for a session read that is about to go out.
|
|
170
|
+
*
|
|
171
|
+
* A session read is answered asynchronously, and the session can be replaced
|
|
172
|
+
* while one is in flight - by a sign-in in THIS tab (`observe` runs inside the
|
|
173
|
+
* fetch decorator, while the store's post-mutation refresh only fires once the
|
|
174
|
+
* action resolves), or by one in ANOTHER tab, which lands in shared storage
|
|
175
|
+
* that this store holds no copy of. A read that started before either and
|
|
176
|
+
* comes back "no session" then describes a session that no longer exists, and
|
|
177
|
+
* acting on it wipes the token that just replaced it: a silent sign-out
|
|
178
|
+
* immediately after a successful sign-in, in whichever tab reloads first.
|
|
179
|
+
*
|
|
180
|
+
* The caller states no rule of its own. It cuts the receipt before dispatch
|
|
181
|
+
* and hands the answer back; deciding whether that answer still describes the
|
|
182
|
+
* session in play needs both the token and the lifecycle, and this is the only
|
|
183
|
+
* thing that has them.
|
|
256
184
|
*/
|
|
257
|
-
|
|
258
|
-
issuer: string;
|
|
259
|
-
jwksUrl: string;
|
|
260
|
-
aud: string;
|
|
261
|
-
} | null;
|
|
185
|
+
beginRead(): SessionRead;
|
|
262
186
|
/**
|
|
263
|
-
*
|
|
187
|
+
* A session BEGINS here, before the request that mints it goes out.
|
|
264
188
|
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
189
|
+
* Called pre-dispatch at every minting door, because both facts it sets have
|
|
190
|
+
* to be true by the time the RESPONSE arrives: the verdict decides whether the
|
|
191
|
+
* request declares the transport at all (and an un-re-armed "cookies work"
|
|
192
|
+
* means it does not, so no token is ever minted), and `remember` decides where
|
|
193
|
+
* the token that comes back is written.
|
|
194
|
+
*
|
|
195
|
+
* It deliberately does NOT drop the token already held. Several minting doors
|
|
196
|
+
* run ON an existing session - the 2FA verifies upgrade a pending one, phone
|
|
197
|
+
* and email OTP verification can run signed in - so dropping the credential
|
|
198
|
+
* before dispatch would send the very request that needs it out anonymous. A
|
|
199
|
+
* failed attempt (a mistyped password at a re-auth prompt, a wrong OTP) would
|
|
200
|
+
* likewise sign the user out locally on exactly the browsers this exists for.
|
|
201
|
+
* The old token is replaced when the new one ARRIVES, which is the only moment
|
|
202
|
+
* the old session is actually over.
|
|
267
203
|
*/
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
204
|
+
beginSession(start: SessionStart): void;
|
|
205
|
+
/** Select the safer cookie-only path before a new session is minted. */
|
|
206
|
+
useCookieTransport(): void;
|
|
207
|
+
/** Drop a previous bearer only after its replacement cookie was delivered. */
|
|
208
|
+
completeCookieTransport(): void;
|
|
209
|
+
/** Select bearer delivery and remember which browser key the new token binds to. */
|
|
210
|
+
useBoundBearerTransport(thumbprint: string, keyIsPersistent: boolean): void;
|
|
211
|
+
/**
|
|
212
|
+
* A session ENDS here, unconditionally: this is sign-out, the one ending a
|
|
213
|
+
* response states plainly, and it means "end whatever is in play".
|
|
214
|
+
*
|
|
215
|
+
* Every other ending - expiry, a revoke from another device, the account
|
|
216
|
+
* deleted - is invisible in the response to the call that caused it and is
|
|
217
|
+
* caught instead by `SessionRead.endIfDead`, which has to establish that the
|
|
218
|
+
* ending is even about the session it is holding.
|
|
219
|
+
*/
|
|
220
|
+
endSession(): void;
|
|
276
221
|
};
|
|
222
|
+
|
|
223
|
+
type TransportErrorKind = 'aborted' | 'timeout' | 'network' | 'response_too_large' | 'invalid_response';
|
|
224
|
+
/**
|
|
225
|
+
* Stable, secret-safe failure from the shared HTTP boundary.
|
|
226
|
+
*
|
|
227
|
+
* Deliberately does not retain the request URL, headers, body, or underlying
|
|
228
|
+
* error. Server clients may carry secret authorization headers and hostile
|
|
229
|
+
* fetch implementations may echo those values in their error messages.
|
|
230
|
+
*/
|
|
231
|
+
declare class TransportError extends Error {
|
|
232
|
+
readonly kind: TransportErrorKind;
|
|
233
|
+
readonly requestId?: string;
|
|
234
|
+
constructor(kind: TransportErrorKind, requestId?: string);
|
|
235
|
+
}
|
|
236
|
+
declare const TRANSPORT_FETCH: unique symbol;
|
|
277
237
|
/**
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
238
|
+
* A `fetch` that has been through the SDK's transport wiring - the ONLY thing
|
|
239
|
+
* this boundary will execute.
|
|
240
|
+
*
|
|
241
|
+
* The brand is a phantom: it exists purely so that `fetchImpl: fetch` and
|
|
242
|
+
* `fetchImpl: config.fetch ?? fetch` stop compiling. That is not pedantry, it is
|
|
243
|
+
* the bug this file has already shipped. The session transport lives in a
|
|
244
|
+
* decorator around `fetch` (see `session-transport.ts`), and the SDK had TWO
|
|
245
|
+
* sibling lines - `http.ts` and `http-client.ts` - each independently writing
|
|
246
|
+
* `config.fetch ?? fetch`. One got the decorator and the other did not, so the
|
|
247
|
+
* JWT issuer and the consent gate stayed broken on exactly the browsers the
|
|
248
|
+
* work existed to fix, with nothing failing anywhere to say so.
|
|
249
|
+
*
|
|
250
|
+
* A brand cannot stop a deliberate cast, and is not meant to. What it stops is
|
|
251
|
+
* the ACCIDENT: reaching this boundary now forces the author to name where the
|
|
252
|
+
* fetch came from, and there are only two answers - `config.fetch`, which
|
|
253
|
+
* carries the session, or `withoutSessionTransport(...)`, which says in one
|
|
254
|
+
* greppable word that this request has no session to carry.
|
|
255
|
+
*
|
|
256
|
+
* The brand is why `requestBoundedJson` can stay what it says it is - network
|
|
257
|
+
* mechanics, stateless, no idea what a session is - while still being the place
|
|
258
|
+
* a missing transport is caught.
|
|
282
259
|
*/
|
|
283
|
-
|
|
260
|
+
type TransportFetch = typeof fetch & {
|
|
261
|
+
readonly [TRANSPORT_FETCH]: true;
|
|
262
|
+
};
|
|
284
263
|
|
|
285
264
|
/** Legal-consent status for the signed-in user (server contract: `GET /consent`). */
|
|
286
265
|
type ConsentStatus = {
|
|
@@ -472,6 +451,136 @@ interface AccountClient {
|
|
|
472
451
|
delete(params?: DeleteAccountOptions, fetchOptions?: ActionFetchOptions): Promise<AuthActionResult<DeleteAccountData>>;
|
|
473
452
|
}
|
|
474
453
|
|
|
454
|
+
/**
|
|
455
|
+
* The organization invitation an emailed link is asking this browser to accept.
|
|
456
|
+
*
|
|
457
|
+
* The link lands on the TENANT'S OWN page carrying `?authowl_invitation=<id>`,
|
|
458
|
+
* and the only route that can redeem it needs a session the visitor usually does
|
|
459
|
+
* not have yet. So the id has to survive an entire sign-up or sign-in - which
|
|
460
|
+
* means surviving that flow's redirects: the OAuth round trip, the email
|
|
461
|
+
* verification round trip (which can land on a different page entirely), the MFA
|
|
462
|
+
* hold, and whatever navigation the operator's own `redirectTo` performs. A query
|
|
463
|
+
* parameter survives none of those, so it is read once, moved into storage, and
|
|
464
|
+
* taken out of the URL.
|
|
465
|
+
*
|
|
466
|
+
* Best-effort storage, deliberately: a browser that refuses `localStorage`
|
|
467
|
+
* degrades to "this invitation is claimable until you navigate", which is worse
|
|
468
|
+
* than the ideal and much better than a sign-in that throws.
|
|
469
|
+
*/
|
|
470
|
+
declare const INVITATION_QUERY_PARAM = "authowl_invitation";
|
|
471
|
+
/**
|
|
472
|
+
* The only hint value that exists. There is deliberately no `existing_user`:
|
|
473
|
+
* absence has to stay ambiguous across an existing account, an older AuthOwl,
|
|
474
|
+
* and an account created between the invite and the click - so it can never be
|
|
475
|
+
* read as proof that an address IS registered.
|
|
476
|
+
*/
|
|
477
|
+
type InvitationRecipientHint = 'new_user';
|
|
478
|
+
type InvitationClaim = {
|
|
479
|
+
id: string;
|
|
480
|
+
/** Epoch milliseconds, for age display and for expiring a forgotten claim. */
|
|
481
|
+
capturedAt: number;
|
|
482
|
+
};
|
|
483
|
+
/** Claims older than this are dropped unread: a month-old link is not a pending intent. */
|
|
484
|
+
declare const INVITATION_CLAIM_MAX_AGE_MS: number;
|
|
485
|
+
/**
|
|
486
|
+
* Read `?authowl_invitation` once, stash it, and strip it from the URL.
|
|
487
|
+
*
|
|
488
|
+
* Namespaced and only ours - a bare `invitation` belongs to the tenant's app as
|
|
489
|
+
* much as to us, and rewriting their parameters is not ours to do. Returns the
|
|
490
|
+
* captured claim, or the one already stashed when there is no parameter, so a
|
|
491
|
+
* caller can render from a single call.
|
|
492
|
+
*/
|
|
493
|
+
declare function captureInvitationClaim(now?: number): InvitationClaim | null;
|
|
494
|
+
/** The stashed claim, or null when there is none, it is unreadable, or it is stale. */
|
|
495
|
+
declare function readInvitationClaim(now?: number): InvitationClaim | null;
|
|
496
|
+
declare function clearInvitationClaim(): void;
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Pure, dependency-free evaluators for an organization membership's advisory
|
|
500
|
+
* permission claim. Shared by the CLIENT `has()` (organization-client.ts, over
|
|
501
|
+
* the browser session) and the SERVER `has()` (server.ts, over a verified JWT),
|
|
502
|
+
* so the two paths can never disagree on what a membership grants.
|
|
503
|
+
*
|
|
504
|
+
* The membership carries the SAME `permissions` array AuthOwl emits into the
|
|
505
|
+
* session and the JWT claim (plan §4/§5): the relabelled `org:sys_*` system ids
|
|
506
|
+
* (plus their legacy bare forms during the dual-emit window) AND the operator's
|
|
507
|
+
* custom `org:<feature>:<action>` ids. Evaluation is a pure array/string check
|
|
508
|
+
* over that local claim - it NEVER calls a statement-only `/organization/has-
|
|
509
|
+
* permission` route, which only knows the 14 static statements and would wrongly
|
|
510
|
+
* report `false` for any custom permission.
|
|
511
|
+
*/
|
|
512
|
+
/** The active-membership shape carried on the session / decoded from a token. */
|
|
513
|
+
interface OrganizationMembership {
|
|
514
|
+
/**
|
|
515
|
+
* The member's PRIMARY role key (built-in `owner`/`admin`/`member` or a
|
|
516
|
+
* project role). One value, for display - use {@link membershipHas} to gate,
|
|
517
|
+
* because a member can hold more than one.
|
|
518
|
+
*/
|
|
519
|
+
role: string;
|
|
520
|
+
/**
|
|
521
|
+
* EVERY role the member holds, sorted. `member.role` is a comma-separated set
|
|
522
|
+
* server-side, so `admin,editor` is an ordinary membership - and gating on
|
|
523
|
+
* `role` alone made the others invisible.
|
|
524
|
+
*
|
|
525
|
+
* Optional because a token or session minted by an older AuthOwl carries only
|
|
526
|
+
* `role`; readers fall back to it rather than reporting a member holds nothing.
|
|
527
|
+
*/
|
|
528
|
+
roles?: string[];
|
|
529
|
+
/**
|
|
530
|
+
* The member's effective permission ids: `org:sys_*` system claims (with
|
|
531
|
+
* their legacy bare forms during dual-emit) plus custom `org:<feature>:<action>`
|
|
532
|
+
* ids. Advisory only - the real boundary is server-side over the verified token.
|
|
533
|
+
*/
|
|
534
|
+
permissions: string[];
|
|
535
|
+
/**
|
|
536
|
+
* Team ids the member holds inside the ACTIVE organization, as emitted by
|
|
537
|
+
* AuthOwl into both the session and the JWT claim. Teams are pure grouping:
|
|
538
|
+
* belonging to one grants nothing on its own, so this is for the application's
|
|
539
|
+
* own gating, never an authority check.
|
|
540
|
+
*
|
|
541
|
+
* Optional because a token minted before teams shipped carries no `teams` claim.
|
|
542
|
+
* `has({ teamId })` then returns false rather than guessing - it can only ever
|
|
543
|
+
* confirm a team the claim actually proves.
|
|
544
|
+
*/
|
|
545
|
+
teams?: string[];
|
|
546
|
+
}
|
|
547
|
+
/** Clerk-style `has()` query: match the role, the permission, the team, or a combination (AND). */
|
|
548
|
+
interface HasParams {
|
|
549
|
+
role?: string;
|
|
550
|
+
permission?: string;
|
|
551
|
+
/** Require membership of this team within the active organization. */
|
|
552
|
+
teamId?: string;
|
|
553
|
+
}
|
|
554
|
+
/** True when the membership's permission claim includes `permission`. Pure. */
|
|
555
|
+
declare function membershipHasPermission(membership: OrganizationMembership | null | undefined, permission: string): boolean;
|
|
556
|
+
/**
|
|
557
|
+
* True when the membership's team claim includes `teamId`. Pure.
|
|
558
|
+
*
|
|
559
|
+
* False when the claim carries no `teams` at all, which is what a token minted
|
|
560
|
+
* before teams shipped looks like - an absent claim is never read as "any team".
|
|
561
|
+
*/
|
|
562
|
+
declare function membershipHasTeam(membership: OrganizationMembership | null | undefined, teamId: string): boolean;
|
|
563
|
+
/**
|
|
564
|
+
* Clerk-style `has()`: true when the membership satisfies EVERY provided
|
|
565
|
+
* criterion - the role matches AND the permission is included AND the team is
|
|
566
|
+
* held. Returns false when there is no membership, or when no criterion at all is
|
|
567
|
+
* given. Pure: no I/O, evaluated entirely against the local claim.
|
|
568
|
+
*/
|
|
569
|
+
/**
|
|
570
|
+
* True when the member holds `role` - as one of their roles, not merely as the
|
|
571
|
+
* primary one. Falls back to the primary when `roles` is absent, which is what a
|
|
572
|
+
* session or token from an older server carries.
|
|
573
|
+
*/
|
|
574
|
+
declare function membershipHasRole(membership: OrganizationMembership | null | undefined, role: string): boolean;
|
|
575
|
+
declare function membershipHas(membership: OrganizationMembership | null | undefined, params: HasParams): boolean;
|
|
576
|
+
/** Bind the pure evaluators to one membership (drives the client / hook `has`). */
|
|
577
|
+
declare function createMembershipHas(membership: OrganizationMembership | null | undefined): {
|
|
578
|
+
has: (params: HasParams) => boolean;
|
|
579
|
+
hasPermission: (params: {
|
|
580
|
+
permission: string;
|
|
581
|
+
}) => boolean;
|
|
582
|
+
};
|
|
583
|
+
|
|
475
584
|
interface Organization {
|
|
476
585
|
id: string;
|
|
477
586
|
name: string;
|
|
@@ -965,7 +1074,7 @@ type AuthErrorContext = Readonly<AuthRequestContext & {
|
|
|
965
1074
|
failure: 'api' | 'aborted' | 'timeout' | 'network' | 'response_too_large' | 'invalid_response';
|
|
966
1075
|
}>;
|
|
967
1076
|
/** Stable AuthOwl policy codes that callers can handle without matching messages. */
|
|
968
|
-
type AuthOwlErrorCode = 'MAU_BUDGET_REACHED' | 'BOT_CHALLENGE_FAILED' | 'VERSION_CONFLICT' | 'SESSION_NOT_FRESH' | 'EMAIL_OTP_RECOVERY_DISABLED' | 'ORGANIZATION_LAST_OWNER' | 'ORGANIZATION_NOT_FOUND' | 'MEMBER_NOT_FOUND' | 'INVITATION_NOT_FOUND' | 'EMAIL_VERIFICATION_REQUIRED_FOR_INVITATION' | 'EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION' | 'YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION';
|
|
1077
|
+
type AuthOwlErrorCode = 'MAU_BUDGET_REACHED' | 'BOT_CHALLENGE_FAILED' | 'VERSION_CONFLICT' | 'SESSION_NOT_FRESH' | 'SECOND_FACTOR_REQUIRED' | 'EMAIL_OTP_RECOVERY_DISABLED' | 'ORGANIZATION_LAST_OWNER' | 'ORGANIZATION_NOT_FOUND' | 'MEMBER_NOT_FOUND' | 'INVITATION_NOT_FOUND' | 'EMAIL_VERIFICATION_REQUIRED_FOR_INVITATION' | 'EMAIL_VERIFICATION_REQUIRED_BEFORE_ACCEPTING_OR_REJECTING_INVITATION' | 'YOU_ARE_NOT_THE_RECIPIENT_OF_THE_INVITATION';
|
|
969
1078
|
/** Framework-neutral reactive session snapshot. */
|
|
970
1079
|
interface SessionState {
|
|
971
1080
|
data: {
|
|
@@ -1591,4 +1700,137 @@ interface AuthOwlClient {
|
|
|
1591
1700
|
*/
|
|
1592
1701
|
declare function createAuthOwlClient(config: ResolvedAuthConfig): AuthOwlClient;
|
|
1593
1702
|
|
|
1594
|
-
|
|
1703
|
+
/**
|
|
1704
|
+
* A framework adapter's supported view of the browser session transport.
|
|
1705
|
+
*
|
|
1706
|
+
* `authenticatedFetch` is the exact fetch owned by the resolved core client,
|
|
1707
|
+
* so out-of-band requests inherit the current cookie/bearer verdict and sender
|
|
1708
|
+
* proof. The lifecycle contains no credential or response data.
|
|
1709
|
+
*/
|
|
1710
|
+
type SessionTransportConnection = Readonly<{
|
|
1711
|
+
authenticatedFetch: TransportFetch;
|
|
1712
|
+
sessionStore: SessionStore;
|
|
1713
|
+
subscribeLifecycle(listener: (event: SessionLifecycleEvent) => void): () => void;
|
|
1714
|
+
}>;
|
|
1715
|
+
type SessionTransportIntegration = Readonly<{
|
|
1716
|
+
connect(connection: SessionTransportConnection): void;
|
|
1717
|
+
/** Project a newly established AuthOwl session before its action resolves. */
|
|
1718
|
+
sessionEstablished(): Promise<void>;
|
|
1719
|
+
/** Remove the framework-owned projection before sign-out resolves. */
|
|
1720
|
+
sessionEnded(): Promise<void>;
|
|
1721
|
+
}>;
|
|
1722
|
+
/** Attach a framework integration to the fetch it asks the core client to use. */
|
|
1723
|
+
declare function withSessionTransportIntegration(fetchImpl: typeof fetch, integration: SessionTransportIntegration): typeof fetch;
|
|
1724
|
+
|
|
1725
|
+
/**
|
|
1726
|
+
* The session, attached to a request and harvested off a response, in ONE place.
|
|
1727
|
+
*
|
|
1728
|
+
* BOTH CARGOES, NO SECOND WIRE. The sign-in challenge - the `two_factor` ticket
|
|
1729
|
+
* and the `dont_remember` flag - travels on this same transport, and it needed
|
|
1730
|
+
* not one line here: `declareOn` and `observe` below each carry both, so a
|
|
1731
|
+
* request that gets the session gets the challenge by construction. That is
|
|
1732
|
+
* deliberate rather than incidental. Wiring the challenge at the call sites that
|
|
1733
|
+
* know about 2FA is precisely the bug class this file exists to make
|
|
1734
|
+
* unrepresentable - a door that gets one cargo and not the other - and the
|
|
1735
|
+
* server's own ingress is built the same way for the same reason
|
|
1736
|
+
* (`bearerTransportHeaders` does both translations in one function, and the
|
|
1737
|
+
* headers it returns are obtainable nowhere else, so no door can opt into the
|
|
1738
|
+
* session half and forget the challenge half). See `session-challenge.ts`.
|
|
1739
|
+
*
|
|
1740
|
+
* WHY THIS IS A `fetch` DECORATOR AND NOT A STEP IN A CLIENT
|
|
1741
|
+
*
|
|
1742
|
+
* Because this SDK used to reach the network through two independent doors and
|
|
1743
|
+
* the first version of this transport wired one and missed the other. See
|
|
1744
|
+
* `TransportFetch` in `transport.ts`, which owns that incident and the guard it
|
|
1745
|
+
* produced.
|
|
1746
|
+
*
|
|
1747
|
+
* The doors have since been collapsed into one (`requestPublishableJson`), but
|
|
1748
|
+
* the session does not ride THAT, because a door is a thing somebody can add.
|
|
1749
|
+
* It rides the `fetch`, which no door can make a request without, and the brand
|
|
1750
|
+
* on `TransportFetch` makes an undecorated one refuse to typecheck at the one
|
|
1751
|
+
* boundary every request passes through. A deliberate opt-out still compiles and
|
|
1752
|
+
* is meant to; what the brand removes is the SILENT version, where a client
|
|
1753
|
+
* somebody added simply never carries the session and nothing anywhere says so.
|
|
1754
|
+
*
|
|
1755
|
+
* `resolveConfig` is the single producer: `config.fetch` is ALWAYS the decorated
|
|
1756
|
+
* fetch, wrapped around the host's if one was supplied. There is no undecorated
|
|
1757
|
+
* fetch left on a resolved config to reach for by mistake.
|
|
1758
|
+
*/
|
|
1759
|
+
|
|
1760
|
+
/**
|
|
1761
|
+
* Everything about this project's session that is NOT the ordinary fetch.
|
|
1762
|
+
*
|
|
1763
|
+
* Handed to the session controller directly rather than looked up from a project
|
|
1764
|
+
* id. The controller used to take that id and use it as BOTH the token store's
|
|
1765
|
+
* key and a BroadcastChannel name, which is only correct while the two strings
|
|
1766
|
+
* are the same string: pass a channel name and the controller silently measures
|
|
1767
|
+
* a private, permanently empty store. Two facts, two parameters.
|
|
1768
|
+
*/
|
|
1769
|
+
type SessionBinding = {
|
|
1770
|
+
/**
|
|
1771
|
+
* The same transport with the session deliberately DETACHED - no token, no
|
|
1772
|
+
* challenge, no declaration, cookies only.
|
|
1773
|
+
*
|
|
1774
|
+
* Exactly one caller: the probe that measures whether this browser keeps our
|
|
1775
|
+
* cross-site cookie. Every other request attaches the token when we hold one,
|
|
1776
|
+
* which would make a session read succeed whether or not the cookie survived,
|
|
1777
|
+
* and the SDK would never learn the difference.
|
|
1778
|
+
*/
|
|
1779
|
+
readonly probe: TransportFetch;
|
|
1780
|
+
/** The store the fetch above attaches from, and the lifecycle the doors drive. */
|
|
1781
|
+
readonly tokens: SessionTokenStore;
|
|
1782
|
+
/** Framework adapter registered on the caller-supplied fetch, when present. */
|
|
1783
|
+
readonly integration?: SessionTransportIntegration | null;
|
|
1784
|
+
/** Decide cookie-only or sender-bound bearer transport before a session mint. */
|
|
1785
|
+
prepareSession(start: SessionStart): Promise<void>;
|
|
1786
|
+
};
|
|
1787
|
+
|
|
1788
|
+
type AuthConfig = {
|
|
1789
|
+
publishableKey: string;
|
|
1790
|
+
apiUrl: string;
|
|
1791
|
+
/** Optional fetch override (e.g. for testing). */
|
|
1792
|
+
fetch?: typeof fetch;
|
|
1793
|
+
};
|
|
1794
|
+
type ResolvedAuthConfig = Omit<AuthConfig, 'fetch'> & {
|
|
1795
|
+
decoded: DecodedPublishableKey;
|
|
1796
|
+
/** Fully-resolved base URL pointing at the per-project auth endpoint. */
|
|
1797
|
+
projectBaseURL: string;
|
|
1798
|
+
/**
|
|
1799
|
+
* THE fetch for this project - the caller's, wrapped in the session transport.
|
|
1800
|
+
*
|
|
1801
|
+
* Required rather than optional, and branded, so that `config.fetch ?? fetch`
|
|
1802
|
+
* cannot be written at all. See `TransportFetch` in `transport.ts` for what
|
|
1803
|
+
* the brand buys and which bug it closes.
|
|
1804
|
+
*/
|
|
1805
|
+
fetch: TransportFetch;
|
|
1806
|
+
/**
|
|
1807
|
+
* The session itself, resolved here because this function is the only producer
|
|
1808
|
+
* of the fetch that carries it. Nothing else has to find the store by id, and
|
|
1809
|
+
* no request boundary has to grow a "but not this one" flag to reach the
|
|
1810
|
+
* detached fetch.
|
|
1811
|
+
*
|
|
1812
|
+
* A BROWSER SIGN-IN FLOW MUST GO THROUGH A CONSTRUCTED CLIENT, not through
|
|
1813
|
+
* `config.fetch` directly. Resolving a config builds the token store, but the
|
|
1814
|
+
* thing that SETTLES its cookie verdict is registered by the session
|
|
1815
|
+
* controller a client builds - so a hand-rolled integration that drives sign-in
|
|
1816
|
+
* off this fetch captures a token nothing will ever measure. The token is then
|
|
1817
|
+
* held in memory and never written, and the session dies at the next reload on
|
|
1818
|
+
* exactly the browsers this transport exists for. Every shipped surface
|
|
1819
|
+
* (`createAuthOwlClient`, the native client, the React provider) builds one;
|
|
1820
|
+
* this note is for anyone reaching below them. Moving the measurement onto
|
|
1821
|
+
* this binding is the queued fix that removes the hazard rather than
|
|
1822
|
+
* documenting it.
|
|
1823
|
+
*/
|
|
1824
|
+
session: SessionBinding;
|
|
1825
|
+
};
|
|
1826
|
+
type ResolvedAuthTarget = Readonly<{
|
|
1827
|
+
publishableKey: string;
|
|
1828
|
+
apiUrl: string;
|
|
1829
|
+
decoded: DecodedPublishableKey;
|
|
1830
|
+
projectBaseURL: string;
|
|
1831
|
+
}>;
|
|
1832
|
+
/** Resolve the shared, side-effect-free project endpoint contract. */
|
|
1833
|
+
declare function resolveAuthTarget(input: Pick<AuthConfig, 'publishableKey' | 'apiUrl'>): ResolvedAuthTarget;
|
|
1834
|
+
declare function resolveConfig(input: AuthConfig): ResolvedAuthConfig;
|
|
1835
|
+
|
|
1836
|
+
export { type DeleteAccountData as $, type ActionFetchOptions as A, resolveConfig as B, type CreatePrivacyRightsRequestOptions as C, type DecodedPublishableKey as D, type PhoneOtpChallengeData as E, type AkedlyShieldStartProof as F, type AcceptOrganizationInvitationData as G, type HasParams as H, type AccountClient as I, type AccountSession as J, type AccountStatusData as K, type AddOrganizationTeamMemberOptions as L, type AuthErrorContext as M, type AuthOwlErrorCode as N, type Organization as O, type PasskeySignInOptions as P, type AuthRequestContext as Q, type ResolvedAuthConfig as R, type SocialIdTokenOptions as S, type AuthResponseContext as T, type ChangeEmailOptions as U, type ChangePasswordData as V, type ChangePasswordOptions as W, type ConsentAcceptResult as X, type ConsentStatus as Y, type CreateOrganizationOptions as Z, type CreateOrganizationTeamOptions as _, type AuthActionResult as a, type RequestPasswordResetOptions as a$, type DeleteAccountOptions as a0, type DeleteOrganizationOptions as a1, type DeletePasskeyData as a2, type DeletePasskeyOptions as a3, type DisableTwoFactorOptions as a4, type EmailAuthData as a5, type EmailOtpAuthData as a6, type EmailOtpSignInOptions as a7, type EmailOtpType as a8, type EmailSignInOptions as a9, type OrganizationFilterOperator as aA, type OrganizationInvitation as aB, type OrganizationInvitationActionOptions as aC, type OrganizationInvitationDetails as aD, type OrganizationInvitationStatus as aE, type OrganizationMember as aF, type OrganizationMemberUser as aG, type OrganizationMemberWithUser as aH, type OrganizationMembersData as aI, type OrganizationRoleSummary as aJ, type OrganizationSelector as aK, type OrganizationTeam as aL, type OrganizationTeamMember as aM, type OrganizationUserInvitation as aN, type PasswordResetData as aO, type PhoneAuthUser as aP, type PhoneOtpStartData as aQ, type PhoneOtpStartOptions as aR, type PhoneOtpVerifyData as aS, type PhoneOtpVerifyOptions as aT, type RejectOrganizationInvitationData as aU, type RemoveOrganizationMemberData as aV, type RemoveOrganizationMemberOptions as aW, type RemoveOrganizationTeamData as aX, type RemoveOrganizationTeamMemberData as aY, type RemoveOrganizationTeamMemberOptions as aZ, type RemoveOrganizationTeamOptions as a_, type EmailSignUpData as aa, type EmailSignUpOptions as ab, type EnableTwoFactorOptions as ac, type GenerateBackupCodesOptions as ad, type GetOrganizationInvitationOptions as ae, type GetOrganizationOptions as af, type GetToken as ag, type GetTokenOptions as ah, INVITATION_CLAIM_MAX_AGE_MS as ai, INVITATION_QUERY_PARAM as aj, type InvitationClaim as ak, type InvitationRecipientHint as al, type InviteOrganizationMemberOptions as am, type JsonObject as an, type JsonPrimitive as ao, type JsonValue as ap, type LeaveOrganizationOptions as aq, type LinkSocialData as ar, type LinkSocialOptions as as, type ListOrganizationInvitationsOptions as at, type ListOrganizationMembersOptions as au, type ListOrganizationRolesOptions as av, type ListOrganizationTeamMembersOptions as aw, type ListOrganizationTeamsOptions as ax, type MagicLinkData as ay, type MagicLinkSignInOptions as az, type AuthOwlClient as b, type ResetPasswordOptions as b0, type ResolvedAuthTarget as b1, type RevokeSessionOptions as b2, type SendOtpData as b3, type SendTwoFactorOtpData as b4, type SendTwoFactorOtpOptions as b5, type SendVerificationOtpOptions as b6, type SessionLifecycleEvent as b7, type SessionTransportConnection as b8, type SessionTransportIntegration as b9, type VerifyTwoFactorOtpOptions as bA, type WaitlistJoinData as bB, type WaitlistJoinOptions as bC, acceptConsent as bD, captureInvitationClaim as bE, clearInvitationClaim as bF, createAuthOwlClient as bG, createTokenClient as bH, getConsentStatus as bI, membershipHas as bJ, membershipHasPermission as bK, membershipHasRole as bL, membershipHasTeam as bM, readInvitationClaim as bN, resolveAuthTarget as bO, withSessionTransportIntegration as bP, type SetActiveTeamOptions as ba, type SignOutData as bb, type SocialAccount as bc, type SocialSignInOptions as bd, type TokenClient as be, TransportError as bf, type TransportErrorKind as bg, type TwoFactorBackupCodesData as bh, type TwoFactorEnableData as bi, type TwoFactorRedirectData as bj, type TwoFactorStatusData as bk, type TwoFactorVerifyData as bl, type UnlinkSocialOptions as bm, type UpdateOrganizationMemberRoleOptions as bn, type UpdateOrganizationOptions as bo, type UpdateOrganizationTeamOptions as bp, type UpdatePasskeyData as bq, type UpdatePasskeyOptions as br, type UpdateProfileOptions as bs, type UpdateUnsafeMetadataOptions as bt, type UserMetadata as bu, type UsernameSignInOptions as bv, type VerifyBackupCodeOptions as bw, type VerifyEmailOtpData as bx, type VerifyEmailOtpOptions as by, type VerifyTotpOptions as bz, type PasskeyAuthData as c, type AddPasskeyOptions as d, type AuthPasskey as e, type AuthConfig as f, type AuthClientError as g, type AuthSession as h, type AuthUser as i, type OrganizationClient as j, type OrganizationDetails as k, type OrganizationMembership as l, type PrivacyClient as m, type PrivacyConsentPreference as n, type PrivacyConsentState as o, type PrivacyLocale as p, type PrivacyRightState as q, type PrivacyRightType as r, type PrivacyRightsRequest as s, type RecordPrivacyConsentOptions as t, type SessionState as u, type SessionStore as v, type SetActiveOrganizationOptions as w, type SocialAuthData as x, createMembershipHas as y, decodePublishableKey as z };
|