@finueva/auth 0.10.0 → 0.10.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +132 -400
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,19 +1,8 @@
1
- # `@finueva/auth`
1
+ # Finueva Auth SDK for TypeScript
2
2
 
3
- The official TypeScript client for Finueva Auth.
3
+ Use `@finueva/auth` to add Finueva sign-in, organization access, invitations, and Drive credentials to a browser, server, CLI, or native application.
4
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
- - read organizations, create ordinary teams, replace display data, 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
- Sections marked **Unreleased** describe repository source and are not available in the currently published `@finueva/auth@0.9.0` package.
5
+ The current published version is `0.10.0`. The package is ESM-only. Node.js consumers require Node.js 22 or later. Other server runtimes must provide compatible Fetch, Web Crypto, URL, and Web Streams APIs. Its interface is Experimental until version `1.0.0`.
17
6
 
18
7
  ## Install
19
8
 
@@ -21,22 +10,30 @@ Sections marked **Unreleased** describe repository source and are not available
21
10
  pnpm add @finueva/auth
22
11
  ```
23
12
 
24
- Node.js applications require Node.js 22 or later.
13
+ For a browser or server application, ask your Auth operator for:
25
14
 
26
- ## Pick The Right Client
15
+ - the Auth origin for your environment;
16
+ - your registered application origin.
27
17
 
28
- | Runtime | Import | How it authenticates |
29
- | ------------------------- | ---------------------- | ------------------------------------ |
30
- | Browser | `@finueva/auth` | Finueva Auth's HTTP-only cookie |
31
- | Server or SSR handler | `@finueva/auth/server` | Cookies or one incoming bearer token |
32
- | CLI or native application | `@finueva/auth/device` | A device bearer session |
18
+ For a CLI or native application, ask for:
33
19
 
34
- Create one browser client for the application, one server client per request, or one device client per application process.
20
+ - the Auth origin for your environment;
21
+ - a registered device client ID.
35
22
 
36
- 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.
23
+ Do not reuse configuration from another application or environment.
24
+
25
+ ## Choose A Client
26
+
27
+ | Runtime | Import | Session authority | Create it |
28
+ | ------------------------- | ---------------------- | ------------------------------ | ------------------------ |
29
+ | Browser | `@finueva/auth` | Auth HTTP-only cookie | Once for the application |
30
+ | Server or SSR handler | `@finueva/auth/server` | Incoming Auth cookie or bearer | Once per request |
31
+ | CLI or native application | `@finueva/auth/device` | Device bearer session | Once per process |
37
32
 
38
33
  ## Browser Quick Start
39
34
 
35
+ Create the client with the exact Auth and application origins:
36
+
40
37
  ```ts
41
38
  import { AuthError, createAuthClient } from "@finueva/auth";
42
39
 
@@ -44,189 +41,60 @@ const auth = createAuthClient({
44
41
  authOrigin: "https://auth.finueva.com",
45
42
  appOrigin: window.location.origin,
46
43
  });
44
+ ```
45
+
46
+ Read the current session:
47
47
 
48
+ ```ts
48
49
  const session = await auth.getSession();
49
50
 
50
51
  if (session) {
51
52
  console.log(`Signed in as ${session.user.email}`);
52
- } else {
53
- console.log("Not signed in");
54
53
  }
54
+ ```
55
+
56
+ Sign in with email and password:
55
57
 
58
+ ```ts
56
59
  try {
57
- const nextSession = await auth.signIn({
60
+ const session = await auth.signIn({
58
61
  email: "ada@example.com",
59
62
  password: "correct-horse-battery-staple",
60
63
  });
61
64
 
62
- console.log(`Welcome ${nextSession.user.name}`);
65
+ console.log(`Welcome ${session.user.name}`);
63
66
  } catch (error) {
64
67
  if (error instanceof AuthError && error.code === "invalid_credentials") {
65
- console.log("Check the email and password.");
68
+ showInvalidCredentials();
66
69
  } else {
67
70
  throw error;
68
71
  }
69
72
  }
70
73
  ```
71
74
 
72
- Auth keeps the session token in an HTTP-only cookie. The browser client never returns that token.
73
-
74
- ### React To Session Changes
75
-
76
- Use `onAuthStateChange()` to update the interface:
77
-
78
- ```ts
79
- const stop = auth.onAuthStateChange((state) => {
80
- switch (state.status) {
81
- case "loading":
82
- showLoading();
83
- break;
84
- case "cached":
85
- showAccountHint(state.session.user);
86
- break;
87
- case "authenticated":
88
- showAccount(state.session.user);
89
- break;
90
- case "unauthenticated":
91
- showSignIn();
92
- break;
93
- case "unavailable":
94
- showAuthUnavailable();
95
- break;
96
- }
97
- });
98
-
99
- window.addEventListener("pagehide", stop, { once: true });
100
- ```
101
-
102
- `cached` state is only a display hint. Never use cached browser state to authorize an operation. Check access on the server.
103
-
104
- Browser snapshots use local storage by default. Choose another mode when you create the client:
105
-
106
- ```ts
107
- const auth = createAuthClient({
108
- authOrigin: "https://auth.finueva.com",
109
- appOrigin: window.location.origin,
110
- persistence: "session", // "local", "session", or "memory"
111
- timeoutMs: 10_000,
112
- });
113
- ```
114
-
115
- The snapshot contains normalized display data, not cookies or Drive credentials.
116
-
117
- ## Common Browser Tasks
118
-
119
- ### Create An Account
120
-
121
- ```ts
122
- const session = await auth.signUp("ada@example.com", "correct-horse-battery-staple", "Ada Lovelace");
123
- ```
124
-
125
- New passwords must contain 10 to 128 characters.
75
+ The browser owns the session cookie. The SDK does not expose or persist it.
126
76
 
127
- ### Update The Profile
77
+ ## Common Tasks
128
78
 
129
- ```ts
130
- const session = await auth.updateProfile({
131
- name: "Ada Lovelace",
132
- image: "https://images.example.com/ada.png",
133
- });
134
- ```
135
-
136
- Pass `image: null` to remove the image.
137
-
138
- ### Change The Password
139
-
140
- ```ts
141
- await auth.changePassword(currentPassword, newPassword);
142
- ```
79
+ | Task | Browser | Server | Device |
80
+ | ------------------------- | ------------------------------------------- | -------------------------------------- | -------------------------------------- |
81
+ | Create an account | `signUp()` | Not available | `signUpWithEmail()` |
82
+ | Read the current session | `getSession()` | `getSession()` | `getSession()` |
83
+ | Observe session state | `onAuthStateChange()` | Not available | Not available |
84
+ | Update a profile | `updateProfile()` | Not available | `updateProfile()` |
85
+ | Change a password | `changePassword()` | Not available | `changePassword()` |
86
+ | Reset a password | `requestPasswordReset()`, `resetPassword()` | Not available | Not available |
87
+ | Verify an email | `sendVerificationEmail()` | Not available | Not available |
88
+ | List organizations | `listOrganizations()` | `listOrganizations()` | `listOrganizations()` |
89
+ | Create an ordinary team | `createOrganizationTeam()` | `createOrganizationTeam()` | `createOrganizationTeam()` |
90
+ | Invite an ordinary member | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` | `createOrganizationMemberInvitation()` |
91
+ | Accept an invitation | `acceptInvitation()` | `acceptInvitation()` | Not available |
92
+ | Request Drive access | `getDriveCredential()` | `getDriveCredential()` | `getDriveCredential()` |
93
+ | End the current session | `signOut()` | `signOut()` | `revokeCurrentDevice()` |
143
94
 
144
- This revokes the user's other sessions by default. Pass `false` as the third argument only when the product deliberately keeps those sessions.
95
+ ## Create An Organization Invitation
145
96
 
146
- ### Reset A Password
147
-
148
- ```ts
149
- await auth.requestPasswordReset("ada@example.com", `${window.location.origin}/account/reset-password`);
150
- ```
151
-
152
- On the reset page, remove the token from the address bar before loading other resources. Use it once:
153
-
154
- ```ts
155
- const url = new URL(window.location.href);
156
- let token = url.searchParams.get("token");
157
-
158
- url.searchParams.delete("token");
159
- window.history.replaceState(null, "", `${url.pathname}${url.search}${url.hash}`);
160
-
161
- if (!token) throw new Error("Reset token is missing");
162
-
163
- try {
164
- await auth.resetPassword(token, newPassword);
165
- } finally {
166
- token = null;
167
- }
168
- ```
169
-
170
- Serve this page with `Cache-Control: no-store` and `Referrer-Policy: no-referrer`. Do not load analytics or third-party content on it.
171
-
172
- ### Verify An Email
173
-
174
- ```ts
175
- await auth.sendVerificationEmail({
176
- email: "ada@example.com",
177
- callbackURL: `${window.location.origin}/account/email-verified`,
178
- });
179
- ```
180
-
181
- The callback must use the application's exact origin. Omit it to use the default Auth result page.
182
-
183
- ### List Organizations
184
-
185
- ```ts
186
- let page = await auth.listOrganizations({ limit: 50 });
187
-
188
- for (const organization of page.organizations) {
189
- console.log(organization.id, organization.name, organization.slug);
190
- }
191
-
192
- if (page.nextCursor) {
193
- page = await auth.listOrganizations({ limit: 50, cursor: page.nextCursor });
194
- }
195
- ```
196
-
197
- All organization collections use an opaque cursor and a page size from 1 through 100. Use
198
- `getOrganization()`, `listOrganizationMembers()`, `listOrganizationTeams()`, and
199
- `listOrganizationTeamMembers()` with an explicit organization ID. Manual owners and admins can use
200
- `listOrganizationInvitations()` and `getOrganizationInvitation()`. A verified recipient can use
201
- `listReceivedInvitations()` and `getReceivedInvitation()`.
202
-
203
- Pagination is deterministic while a collection does not change. During concurrent mutation, an
204
- insert at or before the cursor can be absent, an insert after it can appear once, and a delete,
205
- update, status change, or expiry can change later pages. Restart pagination when you need a current
206
- view.
207
-
208
- ### Create A Team
209
-
210
- **Unreleased:** this method will ship in a later package version and is not in `@finueva/auth@0.9.0`.
211
-
212
- ```ts
213
- const idempotencyKey = crypto.randomUUID();
214
- const team = await auth.createOrganizationTeam("org_01J8R7M6N5P4Q3S2T1", "Platform Engineering", idempotencyKey);
215
- ```
216
-
217
- The name and caller-owned idempotency key must each contain from 1 through 255 UTF-8 bytes. Use a
218
- new key for each intended team. If a response is lost, send the same organization ID, name, and key
219
- again. Auth returns the original creation result for a completed same-key request, even if a later
220
- operation changed the team display. A conflicting or unresolved same-key request returns
221
- `state_conflict`. The SDK never retries automatically.
222
-
223
- For an ordinary organization, a current effective owner or administrator can use the cookie or
224
- bearer lane. For a managed organization, a current manual owner or manual administrator must use the
225
- cookie lane. The method creates only an ordinary team.
226
-
227
- ### Create A Member Invitation
228
-
229
- **Unreleased:** this method will ship in a later package version and is not in `@finueva/auth@0.9.0`.
97
+ Use one new idempotency key for each invitation that you intend to create:
230
98
 
231
99
  ```ts
232
100
  const invitation = await auth.createOrganizationMemberInvitation("org_01J8R7M6N5P4Q3S2T1", {
@@ -238,68 +106,22 @@ const invitation = await auth.createOrganizationMemberInvitation("org_01J8R7M6N5
238
106
  console.log(invitation.id, invitation.expiresAt);
239
107
  ```
240
108
 
241
- The SDK trims and lowercases the email address. The email address can contain no more than 320 UTF-8 bytes. `teamIds` is optional and can contain no more than 100 unique ordinary team IDs. The caller-owned idempotency key must contain from 1 through 255 UTF-8 bytes.
242
-
243
- Use a new key for each intended invitation. If a response is lost, send the same organization ID and input again. The method returns only the invitation ID and expiry. It does not report email delivery. The SDK never retries automatically.
244
-
245
- For an ordinary organization, a current effective owner or administrator can use the cookie or bearer lane. Managed organizations are denied.
246
-
247
- ### Accept An Invitation
248
-
249
- ```ts
250
- const result = await auth.acceptInvitation({
251
- invitationId: "invitation_01J8R7M6N5P4Q3S2T1",
252
- });
253
-
254
- console.log(result.organizationId);
255
- ```
256
-
257
- ### Replace Organization Display
258
-
259
- ```ts
260
- const display = await auth.updateOrganizationDisplay("org_01J8R7M6N5P4Q3S2T1", {
261
- name: "Example Group",
262
- slug: "example-group",
263
- logo: null,
264
- });
265
- ```
266
-
267
- Send all three fields. `name` and `slug` can contain no more than 255 UTF-8 bytes. Use `logo: null` to remove the logo. A non-null logo must be an exact canonical HTTPS URL of no more than 2048 UTF-8 bytes. The method supports manual owners and manual administrators of ordinary organizations. Provider-only administrators are denied. It does not update metadata, lifecycle, membership, teams, invitations, or active-organization state.
268
-
269
- Browser and cookie-based server calls require the exact configured application Origin. The SDK reports a denied or missing Origin as `request_origin_denied`. Other organization authority denials use `access_denied`.
270
-
271
- The method does not retry. If a response is lost, submit the same complete desired state again. Concurrent different updates use last-write-wins behavior.
272
-
273
- ### Replace Team Display
274
-
275
- **Unreleased:** this method will ship in a later package version and is not in `@finueva/auth@0.9.0`.
276
-
277
- ```ts
278
- const display = await auth.updateOrganizationTeamDisplay("org_01J8R7M6N5P4Q3S2T1", "team_01J8R7M6N5P4Q3S2T1", {
279
- name: "Platform Engineering",
280
- });
281
- ```
282
-
283
- Send the complete `name`. It can contain no more than 255 UTF-8 bytes. For an ordinary organization, a current effective owner or administrator can use the cookie or bearer lane. For a managed organization, a current manual owner or manual administrator must use the cookie lane. Managed teams are denied.
284
-
285
- The method does not retry. If a response is lost, submit the same complete desired name again. Concurrent different updates use last-write-wins behavior.
286
-
287
- ### Sign Out
288
-
289
- ```ts
290
- await auth.signOut();
291
- ```
109
+ If the response is lost, repeat the same request with the same key. The SDK does not retry automatically. The result confirms invitation creation. It does not confirm email delivery.
292
110
 
293
111
  ## Request A Drive Credential
294
112
 
295
- Ask Auth for a credential for one workspace:
113
+ Request one short-lived credential for one workspace:
296
114
 
297
115
  ```ts
298
116
  const credential = await auth.getDriveCredential({
299
117
  workspace: { type: "personal" },
300
118
  });
301
119
 
302
- const authorization = `${credential.tokenType} ${credential.token}`;
120
+ await fetch("https://drive.finueva.com/api/files", {
121
+ headers: {
122
+ authorization: `${credential.tokenType} ${credential.token}`,
123
+ },
124
+ });
303
125
  ```
304
126
 
305
127
  For an organization workspace:
@@ -313,184 +135,87 @@ const credential = await auth.getDriveCredential({
313
135
  });
314
136
  ```
315
137
 
316
- 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.
138
+ Browser and device clients cache one completed credential in memory. The server client does not cache credentials. The SDK never replays a failed Drive operation.
317
139
 
318
- If Drive rejects a credential, request a replacement:
140
+ ## Server Quick Start
319
141
 
320
- ```ts
321
- const replacement = await auth.getDriveCredential({
322
- workspace: { type: "personal" },
323
- forceRefresh: true,
324
- });
325
- ```
326
-
327
- The SDK does not replay the failed Drive request. Replay it only when the operation is safe to repeat.
328
-
329
- ## Server And SSR
330
-
331
- 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.
142
+ Create one client inside each request handler:
332
143
 
333
144
  ```ts
334
- import { AuthError, createServerAuth } from "@finueva/auth/server";
145
+ import { createServerAuth } from "@finueva/auth/server";
335
146
 
336
- export async function handleAccountRequest(request: Request): Promise<Response> {
147
+ export async function handleRequest(request: Request): Promise<Response> {
337
148
  const auth = createServerAuth({
338
149
  authOrigin: "https://auth.finueva.com",
339
150
  appOrigin: "https://files.finueva.com",
340
151
  request,
341
152
  });
342
153
 
343
- try {
344
- const session = await auth.getSession();
345
-
346
- if (!session) {
347
- return Response.json(
348
- { authenticated: false },
349
- {
350
- status: 401,
351
- headers: { "cache-control": "no-store" },
352
- },
353
- );
354
- }
355
-
356
- return Response.json(
357
- { user: session.user },
358
- {
359
- headers: { "cache-control": "no-store" },
360
- },
361
- );
362
- } catch (error) {
363
- if (error instanceof AuthError) {
364
- return Response.json(
365
- { code: error.code },
366
- {
367
- status: error.status,
368
- headers: { "cache-control": "no-store" },
369
- },
370
- );
371
- }
154
+ const session = await auth.getSession();
372
155
 
373
- throw error;
374
- }
156
+ return Response.json(
157
+ { authenticated: session !== null, user: session?.user ?? null },
158
+ { headers: { "cache-control": "no-store" } },
159
+ );
375
160
  }
376
161
  ```
377
162
 
378
- Some server mutations return cookies. Apply them to the outgoing response:
163
+ If the incoming request has `Authorization`, the server client uses that bearer and forwards no cookies. Otherwise, it forwards only Auth cookies.
164
+
165
+ Some server methods return cookie changes. Apply them before you send the response:
379
166
 
380
167
  ```ts
381
168
  const result = await auth.signOut();
382
- const headers = new Headers({
383
- "cache-control": "no-store",
384
- location: "/signed-out",
385
- });
169
+ const headers = new Headers({ location: "/signed-out" });
386
170
 
387
171
  result.applyCookies(headers);
388
172
 
389
173
  return new Response(null, { status: 303, headers });
390
174
  ```
391
175
 
392
- Call `applyCookies()` after server `signOut()`, `acceptInvitation()`, and `signInWithSocialIdToken()`. If you omit it, the browser does not receive the session change.
393
-
394
- 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.
176
+ Call `applyCookies()` after server `signOut()`, `acceptInvitation()`, and `signInWithSocialIdToken()`.
395
177
 
396
- ## CLI And Native Applications
178
+ ## Device Quick Start
397
179
 
398
- Use the device flow when a CLI or device can open a system browser:
180
+ Use the device flow for a CLI or native application that can open a system browser:
399
181
 
400
182
  ```ts
401
- import { AuthError, DeviceAuthorizationError, createDeviceAuth } from "@finueva/auth/device";
183
+ import { createDeviceAuth } from "@finueva/auth/device";
402
184
 
403
185
  const auth = createDeviceAuth({
404
186
  authOrigin: "https://auth.finueva.com",
405
187
  clientId: "finueva-cli",
406
188
  });
407
189
 
408
- const controller = new AbortController();
409
- process.once("SIGINT", () => controller.abort());
410
-
411
- try {
412
- const authorization = await auth.requestAuthorization({
413
- signal: controller.signal,
414
- });
415
-
416
- console.log(`Open ${authorization.verificationUriComplete}`);
417
- console.log(`Code: ${authorization.userCode}`);
418
-
419
- await auth.pollForToken(authorization, {
420
- signal: controller.signal,
421
- });
422
-
423
- const session = await auth.getSession();
424
- console.log(session ? `Signed in as ${session.user.email}` : "Not signed in");
425
- } catch (error) {
426
- if (error instanceof DeviceAuthorizationError || error instanceof AuthError) {
427
- console.error(error.code);
428
- } else {
429
- throw error;
430
- }
431
- }
432
- ```
433
-
434
- `clientId` is a public registered identifier, not a secret. Never display or log `authorization.deviceCode`.
435
-
436
- Device sessions are memory-only unless you provide `DeviceTokenPersistence`:
190
+ const authorization = await auth.requestAuthorization();
437
191
 
438
- ```ts
439
- import { createDeviceAuth, type DeviceTokenPersistence } from "@finueva/auth/device";
440
-
441
- const persistence: DeviceTokenPersistence = {
442
- read: (signal) => vault.read("finueva-auth", signal),
443
- replace: (token, signal) => vault.replace("finueva-auth", token, signal),
444
- delete: (signal) => vault.delete("finueva-auth", signal),
445
- };
192
+ console.log(`Open ${authorization.verificationUriComplete}`);
193
+ console.log(`Code: ${authorization.userCode}`);
446
194
 
447
- const auth = createDeviceAuth({
448
- authOrigin: "https://auth.finueva.com",
449
- clientId: "finueva-desktop",
450
- fetch: nativeFetch,
451
- persistence,
452
- });
195
+ await auth.pollForToken(authorization);
453
196
  ```
454
197
 
455
- 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.
198
+ `clientId` is public. `deviceCode` is not. Never log or display the device code.
456
199
 
457
- Direct first-party native email sign-in is available to registered applications:
200
+ Device tokens are memory-only by default. For persistence, supply a `DeviceTokenPersistence` adapter backed by an operating-system credential vault. Do not use local storage, a WebView cookie jar, or a plaintext file.
458
201
 
459
- ```ts
460
- const session = await auth.signInWithEmail("ada@example.com", "correct-horse-battery-staple");
461
- ```
462
-
463
- End the current device session with `revokeCurrentDevice()`. End every browser and device session for the account with `revokeAllSessions()`.
464
-
465
- ## Social ID Tokens
202
+ ## Handle Errors
466
203
 
467
- Browser, server, and device clients accept Google and Apple ID tokens from a first-party provider integration:
204
+ Constructor and selected account-input errors can throw `TypeError`. Validated SDK request failures throw `AuthError`. Device authorization can throw `DeviceAuthorizationError`:
468
205
 
469
206
  ```ts
470
- const session = await auth.signInWithSocialIdToken({
471
- provider: "google",
472
- idToken,
473
- nonce,
474
- signUp: false,
475
- });
476
- ```
207
+ import { AuthError } from "@finueva/auth";
477
208
 
478
- 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.
479
-
480
- The generic Google and Apple redirect flow is not exposed by this package.
481
-
482
- ## Errors
483
-
484
- Invalid local arguments throw `TypeError`. Auth failures throw `AuthError`:
485
-
486
- ```ts
487
209
  try {
488
- await auth.getSession();
210
+ await auth.signIn({
211
+ email: "ada@example.com",
212
+ password: "correct-horse-battery-staple",
213
+ });
489
214
  } catch (error) {
490
215
  if (!(error instanceof AuthError)) throw error;
491
216
 
492
- if (error.code === "auth_request_rate_limited") {
493
- scheduleRetry(error.retryAfterSeconds ?? 60);
217
+ if (error.code === "invalid_credentials") {
218
+ showInvalidCredentials();
494
219
  } else if (error.code === "identity_authority_unavailable") {
495
220
  showAuthUnavailable();
496
221
  } else {
@@ -499,48 +224,55 @@ try {
499
224
  }
500
225
  ```
501
226
 
502
- Use `error.code` for application behavior. Do not parse `error.message`.
503
-
504
- | Code | What the application should do |
505
- | -------------------------------- | --------------------------------------------------------------------- |
506
- | `invalid_credentials` | Ask the user to check the credential. |
507
- | `invalid_auth_input` | Correct the account input. |
508
- | `account_conflict` | Stop the account action. Do not expose account-existence details. |
509
- | `authentication_required` | Start sign-in. |
510
- | `email_verification_required` | Ask the user to verify the current email. |
511
- | `invitation_unavailable` | Ask for a new invitation. Do not retry the old one. |
512
- | `invalid_workspace_selection` | Send one exact personal or organization workspace. |
513
- | `state_conflict` | Wait for the active authority operation, then read the session again. |
514
- | `access_denied` | Stop the operation. |
515
- | `credential_rate_limited` | Wait for `retryAfterSeconds` when present. |
516
- | `auth_request_rate_limited` | Wait for `retryAfterSeconds` when present. |
517
- | `identity_authority_unavailable` | Keep state unverified and try again later. |
518
-
519
- 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`.
520
-
521
- The SDK does not automatically retry requests.
227
+ Use `error.code` for application behavior. Do not parse `error.message`. Except for RFC 8628 device polling, the SDK does not automatically repeat requests.
522
228
 
523
- ## Security Notes
229
+ ## Security Checklist
524
230
 
525
231
  - Use HTTPS. Plain HTTP is accepted only on localhost.
526
- - Use exact registered origins. Do not build an Auth origin from the current request host.
232
+ - Register exact application origins.
233
+ - Create server clients per request.
234
+ - Apply returned server cookies before you send the response.
527
235
  - Keep browser session snapshots display-only.
528
- - Create server clients per request. Do not keep one in a module singleton.
529
- - Apply returned cookies before sending a server response.
530
- - Keep device bearer tokens in an operating-system credential vault.
531
- - Keep Drive credentials in memory and send them only to Drive.
236
+ - Keep device sessions in an operating-system credential vault.
237
+ - Keep Drive credentials in memory.
238
+ - Protect server-side mutations against CSRF.
532
239
  - Do not log cookies, bearer tokens, ID tokens, reset tokens, device codes, or complete Auth responses.
533
- - Protect application mutations against CSRF before calling the server client.
534
240
 
535
241
  ## Current Limits
536
242
 
537
- - The public API is experimental and can change before `1.0.0`.
538
- - Requests time out after 10 seconds by default. `timeoutMs` accepts 1 to 60,000 milliseconds.
539
- - Auth JSON responses are limited to 64 KiB.
243
+ - The package is Experimental until `1.0.0`.
244
+ - Requests time out after 10 seconds by default.
245
+ - `timeoutMs` accepts 1 through 60,000 milliseconds.
246
+ - Most responses are limited to 64 KiB. Organization read responses can use up to 256 KiB.
540
247
  - Browser snapshots and bearer authorization values are limited to 16 KiB.
541
- - Organization lists fail closed at 100 records.
248
+ - Organization collections use page sizes from 1 through 100.
542
249
  - Organization Drive context supports at most 50 current teams.
543
- - Ordinary-organization display replacement is the only organization mutation in `@finueva/auth@0.9.0`. Unreleased source also exposes ordinary-team creation and display replacement. Account deletion and other organization management are not exposed.
544
- - Cross-root SSO and custom refresh tokens are not implemented.
545
- - Hosted mobile transport and operating-system vault qualification are incomplete.
546
- - Production support gates are not complete.
250
+ - Invitation creation supports only the `member` role in ordinary organizations.
251
+ - Invitation resend and cancellation are not public SDK methods.
252
+ - Cross-root SSO, custom refresh tokens, and account deletion are not implemented.
253
+ - Production support gates are incomplete. Ask the Auth operator for the current environment status before adoption.
254
+
255
+ ## Package Exports
256
+
257
+ ```ts
258
+ import { AuthError, createAuthClient } from "@finueva/auth";
259
+ import { createServerAuth } from "@finueva/auth/server";
260
+ import { DeviceAuthorizationError, createDeviceAuth } from "@finueva/auth/device";
261
+ ```
262
+
263
+ TypeScript declarations ship with the package. The public declarations do not expose Better Auth types.
264
+
265
+ ## Documentation
266
+
267
+ Long-form task guides are maintained with the Auth source:
268
+
269
+ - [Get started](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/index.md)
270
+ - [Browser applications](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/browser.md)
271
+ - [Server and SSR applications](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/server.md)
272
+ - [CLI and native applications](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/device.md)
273
+ - [Social ID-token sign-in](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/social-sign-in.md)
274
+ - [Organizations and invitations](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/organizations.md)
275
+ - [Drive credentials](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/drive-credentials.md)
276
+ - [Errors and recovery](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/errors-and-recovery.md)
277
+
278
+ These links track current source and can describe a newer SDK version. Check the version at the start of the guide. Repository access is required until these guides have a public, versioned documentation deployment.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finueva/auth",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "Public browser and server SDK for Finueva Auth",
5
5
  "type": "module",
6
6
  "sideEffects": false,