@finueva/auth 0.8.0 → 0.9.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 +408 -90
- package/dist/device.d.ts +11 -2
- package/dist/device.js +320 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +299 -28
- package/dist/server.d.ts +11 -3
- package/dist/server.js +291 -24
- package/dist/types-B67VIvTQ.d.ts +208 -0
- package/package.json +1 -1
- package/dist/types-DDopQNZR.d.ts +0 -123
package/README.md
CHANGED
|
@@ -1,157 +1,475 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@finueva/auth`
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The official TypeScript client for Finueva Auth.
|
|
4
|
+
|
|
5
|
+
Use it to:
|
|
6
|
+
|
|
7
|
+
- sign users in with email and password;
|
|
8
|
+
- read the current session;
|
|
9
|
+
- manage profile and password details;
|
|
10
|
+
- list organizations and accept invitations;
|
|
11
|
+
- request short-lived Drive credentials;
|
|
12
|
+
- authenticate CLIs and native applications.
|
|
13
|
+
|
|
14
|
+
The package is ESM-only and has no runtime dependencies. Its API is experimental while the package is below `1.0.0`.
|
|
15
|
+
|
|
16
|
+
## Install
|
|
4
17
|
|
|
5
18
|
```sh
|
|
6
19
|
pnpm add @finueva/auth
|
|
7
20
|
```
|
|
8
21
|
|
|
9
|
-
|
|
22
|
+
Node.js applications require Node.js 22 or later.
|
|
23
|
+
|
|
24
|
+
## Pick The Right Client
|
|
25
|
+
|
|
26
|
+
| Runtime | Import | How it authenticates |
|
|
27
|
+
| ------------------------- | ---------------------- | ------------------------------------ |
|
|
28
|
+
| Browser | `@finueva/auth` | Finueva Auth's HTTP-only cookie |
|
|
29
|
+
| Server or SSR handler | `@finueva/auth/server` | Cookies or one incoming bearer token |
|
|
30
|
+
| CLI or native application | `@finueva/auth/device` | A device bearer session |
|
|
31
|
+
|
|
32
|
+
Create one browser client for the application, one server client per request, or one device client per application process.
|
|
33
|
+
|
|
34
|
+
Before you start, ask the Auth operator to register your exact application origin. Device applications also need a registered client ID. Do not copy values from another application or environment.
|
|
35
|
+
|
|
36
|
+
## Browser Quick Start
|
|
10
37
|
|
|
11
38
|
```ts
|
|
12
|
-
import { createAuthClient } from "@finueva/auth";
|
|
39
|
+
import { AuthError, createAuthClient } from "@finueva/auth";
|
|
13
40
|
|
|
14
41
|
const auth = createAuthClient({
|
|
15
42
|
authOrigin: "https://auth.finueva.com",
|
|
16
43
|
appOrigin: window.location.origin,
|
|
17
44
|
});
|
|
18
45
|
|
|
19
|
-
|
|
46
|
+
const session = await auth.getSession();
|
|
47
|
+
|
|
48
|
+
if (session) {
|
|
49
|
+
console.log(`Signed in as ${session.user.email}`);
|
|
50
|
+
} else {
|
|
51
|
+
console.log("Not signed in");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const nextSession = await auth.signIn({
|
|
56
|
+
email: "ada@example.com",
|
|
57
|
+
password: "correct-horse-battery-staple",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
console.log(`Welcome ${nextSession.user.name}`);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (error instanceof AuthError && error.code === "invalid_credentials") {
|
|
63
|
+
console.log("Check the email and password.");
|
|
64
|
+
} else {
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Auth keeps the session token in an HTTP-only cookie. The browser client never returns that token.
|
|
20
71
|
|
|
72
|
+
### React To Session Changes
|
|
73
|
+
|
|
74
|
+
Use `onAuthStateChange()` to update the interface:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
21
77
|
const stop = auth.onAuthStateChange((state) => {
|
|
22
|
-
|
|
23
|
-
|
|
78
|
+
switch (state.status) {
|
|
79
|
+
case "loading":
|
|
80
|
+
showLoading();
|
|
81
|
+
break;
|
|
82
|
+
case "cached":
|
|
83
|
+
showAccountHint(state.session.user);
|
|
84
|
+
break;
|
|
85
|
+
case "authenticated":
|
|
86
|
+
showAccount(state.session.user);
|
|
87
|
+
break;
|
|
88
|
+
case "unauthenticated":
|
|
89
|
+
showSignIn();
|
|
90
|
+
break;
|
|
91
|
+
case "unavailable":
|
|
92
|
+
showAuthUnavailable();
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
24
95
|
});
|
|
25
96
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
97
|
+
window.addEventListener("pagehide", stop, { once: true });
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`cached` state is only a display hint. Never use cached browser state to authorize an operation. Check access on the server.
|
|
101
|
+
|
|
102
|
+
Browser snapshots use local storage by default. Choose another mode when you create the client:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const auth = createAuthClient({
|
|
106
|
+
authOrigin: "https://auth.finueva.com",
|
|
107
|
+
appOrigin: window.location.origin,
|
|
108
|
+
persistence: "session", // "local", "session", or "memory"
|
|
109
|
+
timeoutMs: 10_000,
|
|
37
110
|
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The snapshot contains normalized display data, not cookies or Drive credentials.
|
|
114
|
+
|
|
115
|
+
## Common Browser Tasks
|
|
116
|
+
|
|
117
|
+
### Create An Account
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const session = await auth.signUp("ada@example.com", "correct-horse-battery-staple", "Ada Lovelace");
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
New passwords must contain 10 to 128 characters.
|
|
124
|
+
|
|
125
|
+
### Update The Profile
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const session = await auth.updateProfile({
|
|
129
|
+
name: "Ada Lovelace",
|
|
130
|
+
image: "https://images.example.com/ada.png",
|
|
42
131
|
});
|
|
43
|
-
|
|
44
|
-
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Pass `image: null` to remove the image.
|
|
135
|
+
|
|
136
|
+
### Change The Password
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
await auth.changePassword(currentPassword, newPassword);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
This revokes the user's other sessions by default. Pass `false` as the third argument only when the product deliberately keeps those sessions.
|
|
143
|
+
|
|
144
|
+
### Reset A Password
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
await auth.requestPasswordReset("ada@example.com", `${window.location.origin}/account/reset-password`);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
On the reset page, remove the token from the address bar before loading other resources. Use it once:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const url = new URL(window.location.href);
|
|
154
|
+
let token = url.searchParams.get("token");
|
|
155
|
+
|
|
156
|
+
url.searchParams.delete("token");
|
|
157
|
+
window.history.replaceState(null, "", `${url.pathname}${url.search}${url.hash}`);
|
|
158
|
+
|
|
159
|
+
if (!token) throw new Error("Reset token is missing");
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
await auth.resetPassword(token, newPassword);
|
|
163
|
+
} finally {
|
|
164
|
+
token = null;
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Serve this page with `Cache-Control: no-store` and `Referrer-Policy: no-referrer`. Do not load analytics or third-party content on it.
|
|
169
|
+
|
|
170
|
+
### Verify An Email
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
await auth.sendVerificationEmail({
|
|
174
|
+
email: "ada@example.com",
|
|
175
|
+
callbackURL: `${window.location.origin}/account/email-verified`,
|
|
45
176
|
});
|
|
46
|
-
await auth.signOut();
|
|
47
|
-
stop();
|
|
48
177
|
```
|
|
49
178
|
|
|
50
|
-
The
|
|
179
|
+
The callback must use the application's exact origin. Omit it to use the default Auth result page.
|
|
51
180
|
|
|
52
|
-
|
|
181
|
+
### List Organizations
|
|
53
182
|
|
|
54
|
-
|
|
183
|
+
```ts
|
|
184
|
+
let page = await auth.listOrganizations({ limit: 50 });
|
|
55
185
|
|
|
56
|
-
|
|
186
|
+
for (const organization of page.organizations) {
|
|
187
|
+
console.log(organization.id, organization.name, organization.slug);
|
|
188
|
+
}
|
|
57
189
|
|
|
58
|
-
|
|
190
|
+
if (page.nextCursor) {
|
|
191
|
+
page = await auth.listOrganizations({ limit: 50, cursor: page.nextCursor });
|
|
192
|
+
}
|
|
193
|
+
```
|
|
59
194
|
|
|
60
|
-
|
|
195
|
+
All organization collections use an opaque cursor and a page size from 1 through 100. Use
|
|
196
|
+
`getOrganization()`, `listOrganizationMembers()`, `listOrganizationTeams()`, and
|
|
197
|
+
`listOrganizationTeamMembers()` with an explicit organization ID. Manual owners and admins can use
|
|
198
|
+
`listOrganizationInvitations()` and `getOrganizationInvitation()`. A verified recipient can use
|
|
199
|
+
`listReceivedInvitations()` and `getReceivedInvitation()`.
|
|
61
200
|
|
|
62
|
-
|
|
201
|
+
Pagination is deterministic while a collection does not change. During concurrent mutation, an
|
|
202
|
+
insert at or before the cursor can be absent, an insert after it can appear once, and a delete,
|
|
203
|
+
update, status change, or expiry can change later pages. Restart pagination when you need a current
|
|
204
|
+
view.
|
|
63
205
|
|
|
64
|
-
|
|
206
|
+
### Accept An Invitation
|
|
65
207
|
|
|
66
|
-
|
|
208
|
+
```ts
|
|
209
|
+
const result = await auth.acceptInvitation({
|
|
210
|
+
invitationId: "invitation_01J8R7M6N5P4Q3S2T1",
|
|
211
|
+
});
|
|
67
212
|
|
|
68
|
-
|
|
213
|
+
console.log(result.organizationId);
|
|
214
|
+
```
|
|
69
215
|
|
|
70
|
-
|
|
216
|
+
### Sign Out
|
|
71
217
|
|
|
72
218
|
```ts
|
|
73
|
-
|
|
219
|
+
await auth.signOut();
|
|
220
|
+
```
|
|
74
221
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
222
|
+
## Request A Drive Credential
|
|
223
|
+
|
|
224
|
+
Ask Auth for a credential for one workspace:
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
const credential = await auth.getDriveCredential({
|
|
228
|
+
workspace: { type: "personal" },
|
|
79
229
|
});
|
|
80
230
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
231
|
+
const authorization = `${credential.tokenType} ${credential.token}`;
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
For an organization workspace:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
84
237
|
const credential = await auth.getDriveCredential({
|
|
85
|
-
workspace: {
|
|
238
|
+
workspace: {
|
|
239
|
+
type: "organization",
|
|
240
|
+
organizationId: "org_01J8R7M6N5P4Q3S2T1",
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Drive credentials last about 14 minutes. Browser and device clients keep them in memory only and refresh them when two minutes remain. The server client does not cache them.
|
|
246
|
+
|
|
247
|
+
If Drive rejects a credential, request a replacement:
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
const replacement = await auth.getDriveCredential({
|
|
251
|
+
workspace: { type: "personal" },
|
|
252
|
+
forceRefresh: true,
|
|
86
253
|
});
|
|
87
|
-
const signOut = await auth.signOut();
|
|
88
|
-
const responseHeaders = new Headers();
|
|
89
|
-
signOut.applyCookies(responseHeaders);
|
|
90
254
|
```
|
|
91
255
|
|
|
92
|
-
The
|
|
256
|
+
The SDK does not replay the failed Drive request. Replay it only when the operation is safe to repeat.
|
|
93
257
|
|
|
94
|
-
|
|
258
|
+
## Server And SSR
|
|
95
259
|
|
|
96
|
-
|
|
260
|
+
Create a server client inside each request handler. It forwards only Finueva Auth cookies. If the incoming request has a bearer token, that token takes precedence and the client forwards no cookies.
|
|
97
261
|
|
|
98
262
|
```ts
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
263
|
+
import { AuthError, createServerAuth } from "@finueva/auth/server";
|
|
264
|
+
|
|
265
|
+
export async function handleAccountRequest(request: Request): Promise<Response> {
|
|
266
|
+
const auth = createServerAuth({
|
|
267
|
+
authOrigin: "https://auth.finueva.com",
|
|
268
|
+
appOrigin: "https://files.finueva.com",
|
|
269
|
+
request,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const session = await auth.getSession();
|
|
274
|
+
|
|
275
|
+
if (!session) {
|
|
276
|
+
return Response.json(
|
|
277
|
+
{ authenticated: false },
|
|
278
|
+
{
|
|
279
|
+
status: 401,
|
|
280
|
+
headers: { "cache-control": "no-store" },
|
|
281
|
+
},
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return Response.json(
|
|
286
|
+
{ user: session.user },
|
|
287
|
+
{
|
|
288
|
+
headers: { "cache-control": "no-store" },
|
|
289
|
+
},
|
|
290
|
+
);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
if (error instanceof AuthError) {
|
|
293
|
+
return Response.json(
|
|
294
|
+
{ code: error.code },
|
|
295
|
+
{
|
|
296
|
+
status: error.status,
|
|
297
|
+
headers: { "cache-control": "no-store" },
|
|
298
|
+
},
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
throw error;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Some server mutations return cookies. Apply them to the outgoing response:
|
|
308
|
+
|
|
309
|
+
```ts
|
|
310
|
+
const result = await auth.signOut();
|
|
311
|
+
const headers = new Headers({
|
|
312
|
+
"cache-control": "no-store",
|
|
313
|
+
location: "/signed-out",
|
|
105
314
|
});
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
315
|
+
|
|
316
|
+
result.applyCookies(headers);
|
|
317
|
+
|
|
318
|
+
return new Response(null, { status: 303, headers });
|
|
109
319
|
```
|
|
110
320
|
|
|
111
|
-
|
|
321
|
+
Call `applyCookies()` after server `signOut()`, `acceptInvitation()`, and `signInWithSocialIdToken()`. If you omit it, the browser does not receive the session change.
|
|
322
|
+
|
|
323
|
+
Check `Origin`, Fetch Metadata, or a server-generated CSRF token before a server-side mutation. The SDK forwards authority; it does not replace your application's CSRF protection.
|
|
112
324
|
|
|
113
|
-
##
|
|
325
|
+
## CLI And Native Applications
|
|
326
|
+
|
|
327
|
+
Use the device flow when a CLI or device can open a system browser:
|
|
114
328
|
|
|
115
329
|
```ts
|
|
116
|
-
import { createDeviceAuth } from "@finueva/auth/device";
|
|
330
|
+
import { AuthError, DeviceAuthorizationError, createDeviceAuth } from "@finueva/auth/device";
|
|
117
331
|
|
|
118
332
|
const auth = createDeviceAuth({
|
|
119
333
|
authOrigin: "https://auth.finueva.com",
|
|
120
334
|
clientId: "finueva-cli",
|
|
121
335
|
});
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
await auth.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
336
|
+
|
|
337
|
+
const controller = new AbortController();
|
|
338
|
+
process.once("SIGINT", () => controller.abort());
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const authorization = await auth.requestAuthorization({
|
|
342
|
+
signal: controller.signal,
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
console.log(`Open ${authorization.verificationUriComplete}`);
|
|
346
|
+
console.log(`Code: ${authorization.userCode}`);
|
|
347
|
+
|
|
348
|
+
await auth.pollForToken(authorization, {
|
|
349
|
+
signal: controller.signal,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
const session = await auth.getSession();
|
|
353
|
+
console.log(session ? `Signed in as ${session.user.email}` : "Not signed in");
|
|
354
|
+
} catch (error) {
|
|
355
|
+
if (error instanceof DeviceAuthorizationError || error instanceof AuthError) {
|
|
356
|
+
console.error(error.code);
|
|
357
|
+
} else {
|
|
358
|
+
throw error;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
`clientId` is a public registered identifier, not a secret. Never display or log `authorization.deviceCode`.
|
|
364
|
+
|
|
365
|
+
Device sessions are memory-only unless you provide `DeviceTokenPersistence`:
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
import { createDeviceAuth, type DeviceTokenPersistence } from "@finueva/auth/device";
|
|
369
|
+
|
|
370
|
+
const persistence: DeviceTokenPersistence = {
|
|
371
|
+
read: (signal) => vault.read("finueva-auth", signal),
|
|
372
|
+
replace: (token, signal) => vault.replace("finueva-auth", token, signal),
|
|
373
|
+
delete: (signal) => vault.delete("finueva-auth", signal),
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const auth = createDeviceAuth({
|
|
377
|
+
authOrigin: "https://auth.finueva.com",
|
|
378
|
+
clientId: "finueva-desktop",
|
|
379
|
+
fetch: nativeFetch,
|
|
380
|
+
persistence,
|
|
381
|
+
});
|
|
134
382
|
```
|
|
135
383
|
|
|
136
|
-
|
|
384
|
+
Use the operating system's credential vault. `replace()` must replace the complete token atomically. Do not use local storage, a WebView cookie jar, or a plaintext file. A native application must also supply a reviewed native HTTPS transport that omits ambient WebView cookies.
|
|
137
385
|
|
|
138
|
-
|
|
386
|
+
Direct first-party native email sign-in is available to registered applications:
|
|
139
387
|
|
|
140
|
-
|
|
388
|
+
```ts
|
|
389
|
+
const session = await auth.signInWithEmail("ada@example.com", "correct-horse-battery-staple");
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
End the current device session with `revokeCurrentDevice()`. End every browser and device session for the account with `revokeAllSessions()`.
|
|
393
|
+
|
|
394
|
+
## Social ID Tokens
|
|
395
|
+
|
|
396
|
+
Browser, server, and device clients accept Google and Apple ID tokens from a first-party provider integration:
|
|
397
|
+
|
|
398
|
+
```ts
|
|
399
|
+
const session = await auth.signInWithSocialIdToken({
|
|
400
|
+
provider: "google",
|
|
401
|
+
idToken,
|
|
402
|
+
nonce,
|
|
403
|
+
signUp: false,
|
|
404
|
+
});
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Set `signUp: true` only after the user chooses to create an account. The application owns the provider authorization flow, state, nonce, and PKCE transaction. Pass only the ID token to Auth. Do not pass provider access or refresh tokens.
|
|
141
408
|
|
|
142
|
-
|
|
409
|
+
The generic Google and Apple redirect flow is not exposed by this package.
|
|
143
410
|
|
|
144
|
-
|
|
411
|
+
## Errors
|
|
412
|
+
|
|
413
|
+
Invalid local arguments throw `TypeError`. Auth failures throw `AuthError`:
|
|
414
|
+
|
|
415
|
+
```ts
|
|
416
|
+
try {
|
|
417
|
+
await auth.getSession();
|
|
418
|
+
} catch (error) {
|
|
419
|
+
if (!(error instanceof AuthError)) throw error;
|
|
420
|
+
|
|
421
|
+
if (error.code === "auth_request_rate_limited") {
|
|
422
|
+
scheduleRetry(error.retryAfterSeconds ?? 60);
|
|
423
|
+
} else if (error.code === "identity_authority_unavailable") {
|
|
424
|
+
showAuthUnavailable();
|
|
425
|
+
} else {
|
|
426
|
+
showAuthError(error.code);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
```
|
|
145
430
|
|
|
146
|
-
|
|
431
|
+
Use `error.code` for application behavior. Do not parse `error.message`.
|
|
432
|
+
|
|
433
|
+
| Code | What the application should do |
|
|
434
|
+
| -------------------------------- | --------------------------------------------------------------------- |
|
|
435
|
+
| `invalid_credentials` | Ask the user to check the credential. |
|
|
436
|
+
| `invalid_auth_input` | Correct the account input. |
|
|
437
|
+
| `account_conflict` | Stop the account action. Do not expose account-existence details. |
|
|
438
|
+
| `authentication_required` | Start sign-in. |
|
|
439
|
+
| `email_verification_required` | Ask the user to verify the current email. |
|
|
440
|
+
| `invitation_unavailable` | Ask for a new invitation. Do not retry the old one. |
|
|
441
|
+
| `invalid_workspace_selection` | Send one exact personal or organization workspace. |
|
|
442
|
+
| `state_conflict` | Wait for the active authority operation, then read the session again. |
|
|
443
|
+
| `access_denied` | Stop the operation. |
|
|
444
|
+
| `credential_rate_limited` | Wait for `retryAfterSeconds` when present. |
|
|
445
|
+
| `auth_request_rate_limited` | Wait for `retryAfterSeconds` when present. |
|
|
446
|
+
| `identity_authority_unavailable` | Keep state unverified and try again later. |
|
|
447
|
+
|
|
448
|
+
Device authorization can also throw `DeviceAuthorizationError`. Its codes are `device_access_denied`, `device_authorization_cancelled`, `device_code_expired`, `invalid_device_authorization`, and `identity_authority_unavailable`.
|
|
449
|
+
|
|
450
|
+
The SDK does not automatically retry requests.
|
|
451
|
+
|
|
452
|
+
## Security Notes
|
|
453
|
+
|
|
454
|
+
- Use HTTPS. Plain HTTP is accepted only on localhost.
|
|
455
|
+
- Use exact registered origins. Do not build an Auth origin from the current request host.
|
|
456
|
+
- Keep browser session snapshots display-only.
|
|
457
|
+
- Create server clients per request. Do not keep one in a module singleton.
|
|
458
|
+
- Apply returned cookies before sending a server response.
|
|
459
|
+
- Keep device bearer tokens in an operating-system credential vault.
|
|
460
|
+
- Keep Drive credentials in memory and send them only to Drive.
|
|
461
|
+
- Do not log cookies, bearer tokens, ID tokens, reset tokens, device codes, or complete Auth responses.
|
|
462
|
+
- Protect application mutations against CSRF before calling the server client.
|
|
147
463
|
|
|
148
464
|
## Current Limits
|
|
149
465
|
|
|
150
|
-
-
|
|
151
|
-
-
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
466
|
+
- The public API is experimental and can change before `1.0.0`.
|
|
467
|
+
- Requests time out after 10 seconds by default. `timeoutMs` accepts 1 to 60,000 milliseconds.
|
|
468
|
+
- Auth JSON responses are limited to 64 KiB.
|
|
469
|
+
- Browser snapshots and bearer authorization values are limited to 16 KiB.
|
|
470
|
+
- Organization lists fail closed at 100 records.
|
|
471
|
+
- Organization Drive context supports at most 50 current teams.
|
|
472
|
+
- Account deletion and organization management are not exposed.
|
|
473
|
+
- Cross-root SSO and custom refresh tokens are not implemented.
|
|
474
|
+
- Hosted mobile transport and operating-system vault qualification are incomplete.
|
|
475
|
+
- Production support gates are not complete.
|
package/dist/device.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as UpdateProfileInput, M as AuthError, N as AuthErrorCode, O as SocialIdTokenInput, P as AuthErrorStatus, T as ReceivedOrganizationInvitationPage, _ as OrganizationPage, b as OrganizationTeamMemberPage, g as OrganizationMemberPage, h as OrganizationMember, k as SocialIdTokenProvider, l as DriveCredential, m as OrganizationInvitationPage, o as AuthSession, p as OrganizationInvitation, r as AuthOrganization, u as DriveCredentialInput, v as OrganizationPageInput, w as ReceivedOrganizationInvitation, x as OrganizationTeamPage, y as OrganizationTeam } from "./types-B67VIvTQ.js";
|
|
2
2
|
//#region src/device.d.ts
|
|
3
3
|
type DeviceAuthorizationErrorCode = "device_access_denied" | "device_authorization_cancelled" | "device_code_expired" | "invalid_device_authorization" | "identity_authority_unavailable";
|
|
4
4
|
declare class DeviceAuthorizationError extends Error {
|
|
@@ -43,6 +43,15 @@ interface DeviceAuthClient {
|
|
|
43
43
|
}): Promise<DeviceAuthorization>;
|
|
44
44
|
pollForToken(authorization: DeviceAuthorization, options?: PollDeviceOptions): Promise<DeviceToken>;
|
|
45
45
|
getSession(): Promise<AuthSession | null>;
|
|
46
|
+
listOrganizations(input?: OrganizationPageInput): Promise<OrganizationPage>;
|
|
47
|
+
getOrganization(organizationId: string): Promise<AuthOrganization>;
|
|
48
|
+
listOrganizationMembers(organizationId: string, input?: OrganizationPageInput): Promise<OrganizationMemberPage>;
|
|
49
|
+
listOrganizationTeams(organizationId: string, input?: OrganizationPageInput): Promise<OrganizationTeamPage>;
|
|
50
|
+
listOrganizationTeamMembers(organizationId: string, teamId: string, input?: OrganizationPageInput): Promise<OrganizationTeamMemberPage>;
|
|
51
|
+
listOrganizationInvitations(organizationId: string, input?: OrganizationPageInput): Promise<OrganizationInvitationPage>;
|
|
52
|
+
getOrganizationInvitation(organizationId: string, invitationId: string): Promise<OrganizationInvitation>;
|
|
53
|
+
listReceivedInvitations(input?: OrganizationPageInput): Promise<ReceivedOrganizationInvitationPage>;
|
|
54
|
+
getReceivedInvitation(invitationId: string): Promise<ReceivedOrganizationInvitation>;
|
|
46
55
|
getToken(): Promise<DeviceToken | null>;
|
|
47
56
|
signUpWithEmail(email: string, password: string, name: string): Promise<AuthSession>;
|
|
48
57
|
signInWithEmail(email: string, password: string): Promise<AuthSession>;
|
|
@@ -55,4 +64,4 @@ interface DeviceAuthClient {
|
|
|
55
64
|
}
|
|
56
65
|
declare function createDeviceAuth(options: DeviceAuthOptions): DeviceAuthClient;
|
|
57
66
|
//#endregion
|
|
58
|
-
export { AuthError, type AuthErrorCode, type AuthErrorStatus, DeviceAuthClient, DeviceAuthOptions, DeviceAuthorization, DeviceAuthorizationError, DeviceAuthorizationErrorCode, DeviceToken, DeviceTokenPersistence, PollDeviceOptions, type SocialIdTokenInput, type SocialIdTokenProvider, createDeviceAuth };
|
|
67
|
+
export { AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, DeviceAuthClient, DeviceAuthOptions, DeviceAuthorization, DeviceAuthorizationError, DeviceAuthorizationErrorCode, DeviceToken, DeviceTokenPersistence, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamMemberPage, type OrganizationTeamPage, PollDeviceOptions, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type SocialIdTokenInput, type SocialIdTokenProvider, createDeviceAuth };
|