@finueva/auth 0.9.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.
- package/README.md +137 -334
- package/dist/device.d.ts +6 -2
- package/dist/device.js +189 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +148 -0
- package/dist/server.d.ts +6 -2
- package/dist/server.js +154 -0
- package/dist/{types-B67VIvTQ.d.ts → types-Byw9JQwL.d.ts} +28 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Finueva Auth SDK for TypeScript
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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`.
|
|
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`.
|
|
15
6
|
|
|
16
7
|
## Install
|
|
17
8
|
|
|
@@ -19,22 +10,30 @@ The package is ESM-only and has no runtime dependencies. Its API is experimental
|
|
|
19
10
|
pnpm add @finueva/auth
|
|
20
11
|
```
|
|
21
12
|
|
|
22
|
-
|
|
13
|
+
For a browser or server application, ask your Auth operator for:
|
|
14
|
+
|
|
15
|
+
- the Auth origin for your environment;
|
|
16
|
+
- your registered application origin.
|
|
23
17
|
|
|
24
|
-
|
|
18
|
+
For a CLI or native application, ask for:
|
|
25
19
|
|
|
26
|
-
|
|
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 |
|
|
20
|
+
- the Auth origin for your environment;
|
|
21
|
+
- a registered device client ID.
|
|
31
22
|
|
|
32
|
-
|
|
23
|
+
Do not reuse configuration from another application or environment.
|
|
33
24
|
|
|
34
|
-
|
|
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 |
|
|
35
32
|
|
|
36
33
|
## Browser Quick Start
|
|
37
34
|
|
|
35
|
+
Create the client with the exact Auth and application origins:
|
|
36
|
+
|
|
38
37
|
```ts
|
|
39
38
|
import { AuthError, createAuthClient } from "@finueva/auth";
|
|
40
39
|
|
|
@@ -42,193 +41,87 @@ const auth = createAuthClient({
|
|
|
42
41
|
authOrigin: "https://auth.finueva.com",
|
|
43
42
|
appOrigin: window.location.origin,
|
|
44
43
|
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Read the current session:
|
|
45
47
|
|
|
48
|
+
```ts
|
|
46
49
|
const session = await auth.getSession();
|
|
47
50
|
|
|
48
51
|
if (session) {
|
|
49
52
|
console.log(`Signed in as ${session.user.email}`);
|
|
50
|
-
} else {
|
|
51
|
-
console.log("Not signed in");
|
|
52
53
|
}
|
|
54
|
+
```
|
|
53
55
|
|
|
56
|
+
Sign in with email and password:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
54
59
|
try {
|
|
55
|
-
const
|
|
60
|
+
const session = await auth.signIn({
|
|
56
61
|
email: "ada@example.com",
|
|
57
62
|
password: "correct-horse-battery-staple",
|
|
58
63
|
});
|
|
59
64
|
|
|
60
|
-
console.log(`Welcome ${
|
|
65
|
+
console.log(`Welcome ${session.user.name}`);
|
|
61
66
|
} catch (error) {
|
|
62
67
|
if (error instanceof AuthError && error.code === "invalid_credentials") {
|
|
63
|
-
|
|
68
|
+
showInvalidCredentials();
|
|
64
69
|
} else {
|
|
65
70
|
throw error;
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
```
|
|
69
74
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
### React To Session Changes
|
|
73
|
-
|
|
74
|
-
Use `onAuthStateChange()` to update the interface:
|
|
75
|
-
|
|
76
|
-
```ts
|
|
77
|
-
const stop = auth.onAuthStateChange((state) => {
|
|
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
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
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,
|
|
110
|
-
});
|
|
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.
|
|
75
|
+
The browser owns the session cookie. The SDK does not expose or persist it.
|
|
124
76
|
|
|
125
|
-
|
|
77
|
+
## Common Tasks
|
|
126
78
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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.
|
|
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
|
-
|
|
95
|
+
## Create An Organization Invitation
|
|
145
96
|
|
|
146
|
-
|
|
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:
|
|
97
|
+
Use one new idempotency key for each invitation that you intend to create:
|
|
151
98
|
|
|
152
99
|
```ts
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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`,
|
|
100
|
+
const invitation = await auth.createOrganizationMemberInvitation("org_01J8R7M6N5P4Q3S2T1", {
|
|
101
|
+
email: "member@example.com",
|
|
102
|
+
teamIds: ["team_01J8R7M6N5P4Q3S2T1"],
|
|
103
|
+
idempotencyKey: crypto.randomUUID(),
|
|
176
104
|
});
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
The callback must use the application's exact origin. Omit it to use the default Auth result page.
|
|
180
|
-
|
|
181
|
-
### List Organizations
|
|
182
|
-
|
|
183
|
-
```ts
|
|
184
|
-
let page = await auth.listOrganizations({ limit: 50 });
|
|
185
105
|
|
|
186
|
-
|
|
187
|
-
console.log(organization.id, organization.name, organization.slug);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
if (page.nextCursor) {
|
|
191
|
-
page = await auth.listOrganizations({ limit: 50, cursor: page.nextCursor });
|
|
192
|
-
}
|
|
106
|
+
console.log(invitation.id, invitation.expiresAt);
|
|
193
107
|
```
|
|
194
108
|
|
|
195
|
-
|
|
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()`.
|
|
200
|
-
|
|
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.
|
|
205
|
-
|
|
206
|
-
### Accept An Invitation
|
|
207
|
-
|
|
208
|
-
```ts
|
|
209
|
-
const result = await auth.acceptInvitation({
|
|
210
|
-
invitationId: "invitation_01J8R7M6N5P4Q3S2T1",
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
console.log(result.organizationId);
|
|
214
|
-
```
|
|
215
|
-
|
|
216
|
-
### Sign Out
|
|
217
|
-
|
|
218
|
-
```ts
|
|
219
|
-
await auth.signOut();
|
|
220
|
-
```
|
|
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.
|
|
221
110
|
|
|
222
111
|
## Request A Drive Credential
|
|
223
112
|
|
|
224
|
-
|
|
113
|
+
Request one short-lived credential for one workspace:
|
|
225
114
|
|
|
226
115
|
```ts
|
|
227
116
|
const credential = await auth.getDriveCredential({
|
|
228
117
|
workspace: { type: "personal" },
|
|
229
118
|
});
|
|
230
119
|
|
|
231
|
-
|
|
120
|
+
await fetch("https://drive.finueva.com/api/files", {
|
|
121
|
+
headers: {
|
|
122
|
+
authorization: `${credential.tokenType} ${credential.token}`,
|
|
123
|
+
},
|
|
124
|
+
});
|
|
232
125
|
```
|
|
233
126
|
|
|
234
127
|
For an organization workspace:
|
|
@@ -242,184 +135,87 @@ const credential = await auth.getDriveCredential({
|
|
|
242
135
|
});
|
|
243
136
|
```
|
|
244
137
|
|
|
245
|
-
|
|
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,
|
|
253
|
-
});
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
The SDK does not replay the failed Drive request. Replay it only when the operation is safe to repeat.
|
|
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.
|
|
257
139
|
|
|
258
|
-
## Server
|
|
140
|
+
## Server Quick Start
|
|
259
141
|
|
|
260
|
-
Create
|
|
142
|
+
Create one client inside each request handler:
|
|
261
143
|
|
|
262
144
|
```ts
|
|
263
|
-
import {
|
|
145
|
+
import { createServerAuth } from "@finueva/auth/server";
|
|
264
146
|
|
|
265
|
-
export async function
|
|
147
|
+
export async function handleRequest(request: Request): Promise<Response> {
|
|
266
148
|
const auth = createServerAuth({
|
|
267
149
|
authOrigin: "https://auth.finueva.com",
|
|
268
150
|
appOrigin: "https://files.finueva.com",
|
|
269
151
|
request,
|
|
270
152
|
});
|
|
271
153
|
|
|
272
|
-
|
|
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
|
-
}
|
|
154
|
+
const session = await auth.getSession();
|
|
301
155
|
|
|
302
|
-
|
|
303
|
-
|
|
156
|
+
return Response.json(
|
|
157
|
+
{ authenticated: session !== null, user: session?.user ?? null },
|
|
158
|
+
{ headers: { "cache-control": "no-store" } },
|
|
159
|
+
);
|
|
304
160
|
}
|
|
305
161
|
```
|
|
306
162
|
|
|
307
|
-
|
|
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:
|
|
308
166
|
|
|
309
167
|
```ts
|
|
310
168
|
const result = await auth.signOut();
|
|
311
|
-
const headers = new Headers({
|
|
312
|
-
"cache-control": "no-store",
|
|
313
|
-
location: "/signed-out",
|
|
314
|
-
});
|
|
169
|
+
const headers = new Headers({ location: "/signed-out" });
|
|
315
170
|
|
|
316
171
|
result.applyCookies(headers);
|
|
317
172
|
|
|
318
173
|
return new Response(null, { status: 303, headers });
|
|
319
174
|
```
|
|
320
175
|
|
|
321
|
-
Call `applyCookies()` after server `signOut()`, `acceptInvitation()`, and `signInWithSocialIdToken()`.
|
|
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.
|
|
176
|
+
Call `applyCookies()` after server `signOut()`, `acceptInvitation()`, and `signInWithSocialIdToken()`.
|
|
324
177
|
|
|
325
|
-
##
|
|
178
|
+
## Device Quick Start
|
|
326
179
|
|
|
327
|
-
Use the device flow
|
|
180
|
+
Use the device flow for a CLI or native application that can open a system browser:
|
|
328
181
|
|
|
329
182
|
```ts
|
|
330
|
-
import {
|
|
183
|
+
import { createDeviceAuth } from "@finueva/auth/device";
|
|
331
184
|
|
|
332
185
|
const auth = createDeviceAuth({
|
|
333
186
|
authOrigin: "https://auth.finueva.com",
|
|
334
187
|
clientId: "finueva-cli",
|
|
335
188
|
});
|
|
336
189
|
|
|
337
|
-
const
|
|
338
|
-
process.once("SIGINT", () => controller.abort());
|
|
190
|
+
const authorization = await auth.requestAuthorization();
|
|
339
191
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
signal: controller.signal,
|
|
343
|
-
});
|
|
192
|
+
console.log(`Open ${authorization.verificationUriComplete}`);
|
|
193
|
+
console.log(`Code: ${authorization.userCode}`);
|
|
344
194
|
|
|
345
|
-
|
|
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
|
-
}
|
|
195
|
+
await auth.pollForToken(authorization);
|
|
361
196
|
```
|
|
362
197
|
|
|
363
|
-
`clientId` is
|
|
198
|
+
`clientId` is public. `deviceCode` is not. Never log or display the device code.
|
|
364
199
|
|
|
365
|
-
Device
|
|
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.
|
|
366
201
|
|
|
367
|
-
|
|
368
|
-
import { createDeviceAuth, type DeviceTokenPersistence } from "@finueva/auth/device";
|
|
202
|
+
## Handle Errors
|
|
369
203
|
|
|
370
|
-
|
|
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
|
-
});
|
|
382
|
-
```
|
|
383
|
-
|
|
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.
|
|
385
|
-
|
|
386
|
-
Direct first-party native email sign-in is available to registered applications:
|
|
204
|
+
Constructor and selected account-input errors can throw `TypeError`. Validated SDK request failures throw `AuthError`. Device authorization can throw `DeviceAuthorizationError`:
|
|
387
205
|
|
|
388
206
|
```ts
|
|
389
|
-
|
|
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.
|
|
408
|
-
|
|
409
|
-
The generic Google and Apple redirect flow is not exposed by this package.
|
|
410
|
-
|
|
411
|
-
## Errors
|
|
412
|
-
|
|
413
|
-
Invalid local arguments throw `TypeError`. Auth failures throw `AuthError`:
|
|
207
|
+
import { AuthError } from "@finueva/auth";
|
|
414
208
|
|
|
415
|
-
```ts
|
|
416
209
|
try {
|
|
417
|
-
await auth.
|
|
210
|
+
await auth.signIn({
|
|
211
|
+
email: "ada@example.com",
|
|
212
|
+
password: "correct-horse-battery-staple",
|
|
213
|
+
});
|
|
418
214
|
} catch (error) {
|
|
419
215
|
if (!(error instanceof AuthError)) throw error;
|
|
420
216
|
|
|
421
|
-
if (error.code === "
|
|
422
|
-
|
|
217
|
+
if (error.code === "invalid_credentials") {
|
|
218
|
+
showInvalidCredentials();
|
|
423
219
|
} else if (error.code === "identity_authority_unavailable") {
|
|
424
220
|
showAuthUnavailable();
|
|
425
221
|
} else {
|
|
@@ -428,48 +224,55 @@ try {
|
|
|
428
224
|
}
|
|
429
225
|
```
|
|
430
226
|
|
|
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. |
|
|
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.
|
|
447
228
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
The SDK does not automatically retry requests.
|
|
451
|
-
|
|
452
|
-
## Security Notes
|
|
229
|
+
## Security Checklist
|
|
453
230
|
|
|
454
231
|
- Use HTTPS. Plain HTTP is accepted only on localhost.
|
|
455
|
-
-
|
|
232
|
+
- Register exact application origins.
|
|
233
|
+
- Create server clients per request.
|
|
234
|
+
- Apply returned server cookies before you send the response.
|
|
456
235
|
- Keep browser session snapshots display-only.
|
|
457
|
-
-
|
|
458
|
-
-
|
|
459
|
-
-
|
|
460
|
-
- 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.
|
|
461
239
|
- 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.
|
|
463
240
|
|
|
464
241
|
## Current Limits
|
|
465
242
|
|
|
466
|
-
- The
|
|
467
|
-
- Requests time out after 10 seconds by default.
|
|
468
|
-
-
|
|
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.
|
|
469
247
|
- Browser snapshots and bearer authorization values are limited to 16 KiB.
|
|
470
|
-
- Organization
|
|
248
|
+
- Organization collections use page sizes from 1 through 100.
|
|
471
249
|
- Organization Drive context supports at most 50 current teams.
|
|
472
|
-
-
|
|
473
|
-
-
|
|
474
|
-
-
|
|
475
|
-
- Production support gates are
|
|
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/dist/device.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as ReceivedOrganizationInvitationPage, B as AuthErrorCode, C as OrganizationTeam, E as OrganizationTeamPage, F as UpdateOrganizationDisplayInput, I as UpdateOrganizationTeamDisplayInput, L as UpdateProfileInput, N as SocialIdTokenInput, P as SocialIdTokenProvider, S as OrganizationPageInput, T as OrganizationTeamMemberPage, V as AuthErrorStatus, _ as OrganizationInvitation, b as OrganizationMemberPage, d as CreatedOrganizationTeam, f as DriveCredential, g as OrganizationDisplay, k as ReceivedOrganizationInvitation, l as CreateOrganizationMemberInvitationInput, o as AuthSession, p as DriveCredentialInput, r as AuthOrganization, u as CreatedOrganizationMemberInvitation, v as OrganizationInvitationPage, w as OrganizationTeamDisplay, x as OrganizationPage, y as OrganizationMember, z as AuthError } from "./types-Byw9JQwL.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 {
|
|
@@ -45,6 +45,10 @@ interface DeviceAuthClient {
|
|
|
45
45
|
getSession(): Promise<AuthSession | null>;
|
|
46
46
|
listOrganizations(input?: OrganizationPageInput): Promise<OrganizationPage>;
|
|
47
47
|
getOrganization(organizationId: string): Promise<AuthOrganization>;
|
|
48
|
+
createOrganizationTeam(organizationId: string, name: string, idempotencyKey: string): Promise<CreatedOrganizationTeam>;
|
|
49
|
+
createOrganizationMemberInvitation(organizationId: string, input: CreateOrganizationMemberInvitationInput): Promise<CreatedOrganizationMemberInvitation>;
|
|
50
|
+
updateOrganizationDisplay(organizationId: string, input: UpdateOrganizationDisplayInput): Promise<OrganizationDisplay>;
|
|
51
|
+
updateOrganizationTeamDisplay(organizationId: string, teamId: string, input: UpdateOrganizationTeamDisplayInput): Promise<OrganizationTeamDisplay>;
|
|
48
52
|
listOrganizationMembers(organizationId: string, input?: OrganizationPageInput): Promise<OrganizationMemberPage>;
|
|
49
53
|
listOrganizationTeams(organizationId: string, input?: OrganizationPageInput): Promise<OrganizationTeamPage>;
|
|
50
54
|
listOrganizationTeamMembers(organizationId: string, teamId: string, input?: OrganizationPageInput): Promise<OrganizationTeamMemberPage>;
|
|
@@ -64,4 +68,4 @@ interface DeviceAuthClient {
|
|
|
64
68
|
}
|
|
65
69
|
declare function createDeviceAuth(options: DeviceAuthOptions): DeviceAuthClient;
|
|
66
70
|
//#endregion
|
|
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 };
|
|
71
|
+
export { AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, DeviceAuthClient, DeviceAuthOptions, DeviceAuthorization, DeviceAuthorizationError, DeviceAuthorizationErrorCode, DeviceToken, DeviceTokenPersistence, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, PollDeviceOptions, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, createDeviceAuth };
|
package/dist/device.js
CHANGED
|
@@ -614,6 +614,81 @@ function normalizeOrganization(value) {
|
|
|
614
614
|
}
|
|
615
615
|
};
|
|
616
616
|
}
|
|
617
|
+
function normalizeOrganizationDisplayInput(value) {
|
|
618
|
+
if (!isObject(value) || Object.keys(value).sort().join(",") !== "logo,name,slug") throw new AuthError("invalid_request", 400);
|
|
619
|
+
if (!boundedString(value.name, 255) || !boundedString(value.slug, 255)) throw new AuthError("invalid_request", 400);
|
|
620
|
+
let logo;
|
|
621
|
+
try {
|
|
622
|
+
logo = nullableHttpsUrl(value.logo);
|
|
623
|
+
} catch {
|
|
624
|
+
throw new AuthError("invalid_request", 400);
|
|
625
|
+
}
|
|
626
|
+
return {
|
|
627
|
+
name: value.name,
|
|
628
|
+
slug: value.slug,
|
|
629
|
+
logo
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function normalizeOrganizationDisplay(value) {
|
|
633
|
+
try {
|
|
634
|
+
return normalizeOrganizationDisplayInput(value);
|
|
635
|
+
} catch {
|
|
636
|
+
throw authorityUnavailable();
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function normalizeOrganizationTeamDisplayInput(value) {
|
|
640
|
+
if (!isObject(value) || Object.keys(value).join(",") !== "name" || !boundedString(value.name, 255)) throw new AuthError("invalid_request", 400);
|
|
641
|
+
return { name: value.name };
|
|
642
|
+
}
|
|
643
|
+
function normalizeOrganizationTeamDisplay(value) {
|
|
644
|
+
try {
|
|
645
|
+
return normalizeOrganizationTeamDisplayInput(value);
|
|
646
|
+
} catch {
|
|
647
|
+
throw authorityUnavailable();
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
function normalizeOrganizationTeamCreationInput(name, idempotencyKey) {
|
|
651
|
+
if (!boundedString(name, 255) || !boundedString(idempotencyKey, 255)) throw new AuthError("invalid_request", 400);
|
|
652
|
+
return {
|
|
653
|
+
name,
|
|
654
|
+
idempotencyKey
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
function normalizeCreatedOrganizationTeam(value) {
|
|
658
|
+
exact(value, ["id", "name"]);
|
|
659
|
+
if (!isId(value.id) || !boundedString(value.name, 255)) throw authorityUnavailable();
|
|
660
|
+
return {
|
|
661
|
+
id: value.id,
|
|
662
|
+
name: value.name
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function normalizeOrganizationMemberInvitationCreationInput(value) {
|
|
666
|
+
if (!isObject(value)) throw new AuthError("invalid_request", 400);
|
|
667
|
+
const keys = Object.keys(value).sort().join(",");
|
|
668
|
+
if (keys !== "email,idempotencyKey" && keys !== "email,idempotencyKey,teamIds") throw new AuthError("invalid_request", 400);
|
|
669
|
+
let normalizedEmail;
|
|
670
|
+
try {
|
|
671
|
+
normalizedEmail = normalizeEmail(value.email);
|
|
672
|
+
} catch {
|
|
673
|
+
throw new AuthError("invalid_request", 400);
|
|
674
|
+
}
|
|
675
|
+
if (new TextEncoder().encode(normalizedEmail).byteLength > 320 || !boundedString(value.idempotencyKey, 255) || value.teamIds !== void 0 && (!Array.isArray(value.teamIds) || value.teamIds.length > 100)) throw new AuthError("invalid_request", 400);
|
|
676
|
+
const teamIds = value.teamIds?.map((teamId) => normalizeIdInput(teamId));
|
|
677
|
+
if (teamIds && new Set(teamIds).size !== teamIds.length) throw new AuthError("invalid_request", 400);
|
|
678
|
+
return {
|
|
679
|
+
email: normalizedEmail,
|
|
680
|
+
...teamIds === void 0 ? {} : { teamIds },
|
|
681
|
+
idempotencyKey: value.idempotencyKey
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
function normalizeCreatedOrganizationMemberInvitation(value) {
|
|
685
|
+
exact(value, ["expiresAt", "id"]);
|
|
686
|
+
if (!isId(value.id)) throw authorityUnavailable();
|
|
687
|
+
return {
|
|
688
|
+
id: value.id,
|
|
689
|
+
expiresAt: responseDate(value.expiresAt)
|
|
690
|
+
};
|
|
691
|
+
}
|
|
617
692
|
function normalizeOrganizations(value) {
|
|
618
693
|
exact(value, ["nextCursor", "organizations"]);
|
|
619
694
|
if (!Array.isArray(value.organizations) || value.organizations.length > 100) throw authorityUnavailable();
|
|
@@ -810,6 +885,14 @@ function fromOrganizationReadAuthorityError(value, status) {
|
|
|
810
885
|
if (status === 403) return new AuthError("access_denied", 403);
|
|
811
886
|
return authorityUnavailable();
|
|
812
887
|
}
|
|
888
|
+
function fromOrganizationMutationAuthorityError(value, status, retryAfter) {
|
|
889
|
+
if (status === 400 || status === 413 || status === 415) return new AuthError("invalid_request", 400);
|
|
890
|
+
if (status === 409) return new AuthError("state_conflict", 409);
|
|
891
|
+
if (status === 429) return new AuthError("auth_request_rate_limited", 429, parseRetryAfter(retryAfter));
|
|
892
|
+
const code = isObject(value) && typeof value.code === "string" ? value.code : void 0;
|
|
893
|
+
if (status === 403 && code === "REQUEST_ORIGIN_DENIED") return new AuthError("request_origin_denied", 403);
|
|
894
|
+
return fromOrganizationReadAuthorityError(value, status);
|
|
895
|
+
}
|
|
813
896
|
function mapError(code, status) {
|
|
814
897
|
if (code === "authentication_required") return {
|
|
815
898
|
code,
|
|
@@ -1389,6 +1472,112 @@ function createDeviceAuth(options) {
|
|
|
1389
1472
|
return Promise.reject(error);
|
|
1390
1473
|
}
|
|
1391
1474
|
},
|
|
1475
|
+
createOrganizationTeam(organizationId, name, idempotencyKey) {
|
|
1476
|
+
let id;
|
|
1477
|
+
let body;
|
|
1478
|
+
try {
|
|
1479
|
+
id = normalizeIdInput(organizationId);
|
|
1480
|
+
body = normalizeOrganizationTeamCreationInput(name, idempotencyKey);
|
|
1481
|
+
} catch (error) {
|
|
1482
|
+
return Promise.reject(error);
|
|
1483
|
+
}
|
|
1484
|
+
return serializeAuthority(async () => {
|
|
1485
|
+
const current = await readToken();
|
|
1486
|
+
if (!current) throw new AuthError("authentication_required", 401);
|
|
1487
|
+
return normalizeCreatedOrganizationTeam((await requestJsonResponse(joinURL(authOrigin, "api/v1/auth/organizations", id, "teams"), {
|
|
1488
|
+
fetch: requestFetch,
|
|
1489
|
+
timeoutMs,
|
|
1490
|
+
method: "POST",
|
|
1491
|
+
credentials: "omit",
|
|
1492
|
+
headers: {
|
|
1493
|
+
authorization: `Bearer ${current.accessToken}`,
|
|
1494
|
+
"content-type": "application/json"
|
|
1495
|
+
},
|
|
1496
|
+
body,
|
|
1497
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
1498
|
+
})).value);
|
|
1499
|
+
});
|
|
1500
|
+
},
|
|
1501
|
+
createOrganizationMemberInvitation(organizationId, input) {
|
|
1502
|
+
let id;
|
|
1503
|
+
let body;
|
|
1504
|
+
try {
|
|
1505
|
+
id = normalizeIdInput(organizationId);
|
|
1506
|
+
body = normalizeOrganizationMemberInvitationCreationInput(input);
|
|
1507
|
+
} catch (error) {
|
|
1508
|
+
return Promise.reject(error);
|
|
1509
|
+
}
|
|
1510
|
+
return serializeAuthority(async () => {
|
|
1511
|
+
const current = await readToken();
|
|
1512
|
+
if (!current) throw new AuthError("authentication_required", 401);
|
|
1513
|
+
return normalizeCreatedOrganizationMemberInvitation((await requestJsonResponse(joinURL(authOrigin, "api/v1/auth/organizations", id, "invitations"), {
|
|
1514
|
+
fetch: requestFetch,
|
|
1515
|
+
timeoutMs,
|
|
1516
|
+
method: "POST",
|
|
1517
|
+
credentials: "omit",
|
|
1518
|
+
headers: {
|
|
1519
|
+
authorization: `Bearer ${current.accessToken}`,
|
|
1520
|
+
"content-type": "application/json"
|
|
1521
|
+
},
|
|
1522
|
+
body,
|
|
1523
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
1524
|
+
})).value);
|
|
1525
|
+
});
|
|
1526
|
+
},
|
|
1527
|
+
updateOrganizationDisplay(organizationId, input) {
|
|
1528
|
+
let id;
|
|
1529
|
+
let desired;
|
|
1530
|
+
try {
|
|
1531
|
+
id = normalizeIdInput(organizationId);
|
|
1532
|
+
desired = normalizeOrganizationDisplayInput(input);
|
|
1533
|
+
} catch (error) {
|
|
1534
|
+
return Promise.reject(error);
|
|
1535
|
+
}
|
|
1536
|
+
return serializeAuthority(async () => {
|
|
1537
|
+
const current = await readToken();
|
|
1538
|
+
if (!current) throw new AuthError("authentication_required", 401);
|
|
1539
|
+
return normalizeOrganizationDisplay((await requestJsonResponse(joinURL(authOrigin, "api/v1/auth/organizations", id, "display"), {
|
|
1540
|
+
fetch: requestFetch,
|
|
1541
|
+
timeoutMs,
|
|
1542
|
+
method: "POST",
|
|
1543
|
+
credentials: "omit",
|
|
1544
|
+
headers: {
|
|
1545
|
+
authorization: `Bearer ${current.accessToken}`,
|
|
1546
|
+
"content-type": "application/json"
|
|
1547
|
+
},
|
|
1548
|
+
body: desired,
|
|
1549
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
1550
|
+
})).value);
|
|
1551
|
+
});
|
|
1552
|
+
},
|
|
1553
|
+
updateOrganizationTeamDisplay(organizationId, teamId, input) {
|
|
1554
|
+
let organization;
|
|
1555
|
+
let team;
|
|
1556
|
+
let desired;
|
|
1557
|
+
try {
|
|
1558
|
+
organization = normalizeIdInput(organizationId);
|
|
1559
|
+
team = normalizeIdInput(teamId);
|
|
1560
|
+
desired = normalizeOrganizationTeamDisplayInput(input);
|
|
1561
|
+
} catch (error) {
|
|
1562
|
+
return Promise.reject(error);
|
|
1563
|
+
}
|
|
1564
|
+
return serializeAuthority(async () => {
|
|
1565
|
+
const current = await readToken();
|
|
1566
|
+
if (!current) throw new AuthError("authentication_required", 401);
|
|
1567
|
+
return normalizeOrganizationTeamDisplay((await requestJsonResponse(joinURL(authOrigin, "api/v1/auth/organizations", organization, "teams", team, "display"), {
|
|
1568
|
+
fetch: requestFetch,
|
|
1569
|
+
timeoutMs,
|
|
1570
|
+
method: "POST",
|
|
1571
|
+
credentials: "omit",
|
|
1572
|
+
headers: {
|
|
1573
|
+
authorization: `Bearer ${current.accessToken}`,
|
|
1574
|
+
"content-type": "application/json"
|
|
1575
|
+
},
|
|
1576
|
+
body: desired,
|
|
1577
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
1578
|
+
})).value);
|
|
1579
|
+
});
|
|
1580
|
+
},
|
|
1392
1581
|
listOrganizationMembers(organizationId, input) {
|
|
1393
1582
|
try {
|
|
1394
1583
|
return serializeAuthority(() => organizationRead(`${normalizeIdInput(organizationId)}/members`, input, normalizeOrganizationMembers));
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as ReceivedOrganizationInvitationPage, B as AuthErrorCode, C as OrganizationTeam, D as OrganizationWorkspaceSelection, E as OrganizationTeamPage, F as UpdateOrganizationDisplayInput, I as UpdateOrganizationTeamDisplayInput, L as UpdateProfileInput, M as SignInInput, N as SocialIdTokenInput, O as PersonalWorkspaceSelection, P as SocialIdTokenProvider, R as WorkspaceSelection, S as OrganizationPageInput, T as OrganizationTeamMemberPage, V as AuthErrorStatus, _ as OrganizationInvitation, a as AuthPersistenceDriver, b as OrganizationMemberPage, c as AuthUser, d as CreatedOrganizationTeam, f as DriveCredential, g as OrganizationDisplay, h as InvitationAcceptance, i as AuthPersistence, j as SendVerificationEmailInput, k as ReceivedOrganizationInvitation, l as CreateOrganizationMemberInvitationInput, m as FinuevaAuthClient, n as AuthClientOptions, o as AuthSession, p as DriveCredentialInput, r as AuthOrganization, s as AuthState, t as AcceptInvitationInput, u as CreatedOrganizationMemberInvitation, v as OrganizationInvitationPage, w as OrganizationTeamDisplay, x as OrganizationPage, y as OrganizationMember, z as AuthError } from "./types-Byw9JQwL.js";
|
|
2
2
|
//#region src/client.d.ts
|
|
3
3
|
declare function createAuthClient(options: AuthClientOptions): FinuevaAuthClient;
|
|
4
4
|
//#endregion
|
|
5
|
-
export { type AcceptInvitationInput, type AuthClientOptions, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthPersistence, type AuthPersistenceDriver, type AuthSession, type AuthState, type AuthUser, type DriveCredential, type DriveCredentialInput, type FinuevaAuthClient, type InvitationAcceptance, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type SendVerificationEmailInput, type SignInInput, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateProfileInput, type WorkspaceSelection, createAuthClient };
|
|
5
|
+
export { type AcceptInvitationInput, type AuthClientOptions, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthPersistence, type AuthPersistenceDriver, type AuthSession, type AuthState, type AuthUser, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type FinuevaAuthClient, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type SendVerificationEmailInput, type SignInInput, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type UpdateProfileInput, type WorkspaceSelection, createAuthClient };
|
package/dist/index.js
CHANGED
|
@@ -2333,6 +2333,81 @@ function normalizeOrganization(value) {
|
|
|
2333
2333
|
}
|
|
2334
2334
|
};
|
|
2335
2335
|
}
|
|
2336
|
+
function normalizeOrganizationDisplayInput(value) {
|
|
2337
|
+
if (!isObject(value) || Object.keys(value).sort().join(",") !== "logo,name,slug") throw new AuthError("invalid_request", 400);
|
|
2338
|
+
if (!boundedString(value.name, 255) || !boundedString(value.slug, 255)) throw new AuthError("invalid_request", 400);
|
|
2339
|
+
let logo;
|
|
2340
|
+
try {
|
|
2341
|
+
logo = nullableHttpsUrl(value.logo);
|
|
2342
|
+
} catch {
|
|
2343
|
+
throw new AuthError("invalid_request", 400);
|
|
2344
|
+
}
|
|
2345
|
+
return {
|
|
2346
|
+
name: value.name,
|
|
2347
|
+
slug: value.slug,
|
|
2348
|
+
logo
|
|
2349
|
+
};
|
|
2350
|
+
}
|
|
2351
|
+
function normalizeOrganizationDisplay(value) {
|
|
2352
|
+
try {
|
|
2353
|
+
return normalizeOrganizationDisplayInput(value);
|
|
2354
|
+
} catch {
|
|
2355
|
+
throw authorityUnavailable();
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
function normalizeOrganizationTeamDisplayInput(value) {
|
|
2359
|
+
if (!isObject(value) || Object.keys(value).join(",") !== "name" || !boundedString(value.name, 255)) throw new AuthError("invalid_request", 400);
|
|
2360
|
+
return { name: value.name };
|
|
2361
|
+
}
|
|
2362
|
+
function normalizeOrganizationTeamDisplay(value) {
|
|
2363
|
+
try {
|
|
2364
|
+
return normalizeOrganizationTeamDisplayInput(value);
|
|
2365
|
+
} catch {
|
|
2366
|
+
throw authorityUnavailable();
|
|
2367
|
+
}
|
|
2368
|
+
}
|
|
2369
|
+
function normalizeOrganizationTeamCreationInput(name, idempotencyKey) {
|
|
2370
|
+
if (!boundedString(name, 255) || !boundedString(idempotencyKey, 255)) throw new AuthError("invalid_request", 400);
|
|
2371
|
+
return {
|
|
2372
|
+
name,
|
|
2373
|
+
idempotencyKey
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
function normalizeCreatedOrganizationTeam(value) {
|
|
2377
|
+
exact(value, ["id", "name"]);
|
|
2378
|
+
if (!isId(value.id) || !boundedString(value.name, 255)) throw authorityUnavailable();
|
|
2379
|
+
return {
|
|
2380
|
+
id: value.id,
|
|
2381
|
+
name: value.name
|
|
2382
|
+
};
|
|
2383
|
+
}
|
|
2384
|
+
function normalizeOrganizationMemberInvitationCreationInput(value) {
|
|
2385
|
+
if (!isObject(value)) throw new AuthError("invalid_request", 400);
|
|
2386
|
+
const keys = Object.keys(value).sort().join(",");
|
|
2387
|
+
if (keys !== "email,idempotencyKey" && keys !== "email,idempotencyKey,teamIds") throw new AuthError("invalid_request", 400);
|
|
2388
|
+
let normalizedEmail;
|
|
2389
|
+
try {
|
|
2390
|
+
normalizedEmail = normalizeEmail(value.email);
|
|
2391
|
+
} catch {
|
|
2392
|
+
throw new AuthError("invalid_request", 400);
|
|
2393
|
+
}
|
|
2394
|
+
if (new TextEncoder().encode(normalizedEmail).byteLength > 320 || !boundedString(value.idempotencyKey, 255) || value.teamIds !== void 0 && (!Array.isArray(value.teamIds) || value.teamIds.length > 100)) throw new AuthError("invalid_request", 400);
|
|
2395
|
+
const teamIds = value.teamIds?.map((teamId) => normalizeIdInput(teamId));
|
|
2396
|
+
if (teamIds && new Set(teamIds).size !== teamIds.length) throw new AuthError("invalid_request", 400);
|
|
2397
|
+
return {
|
|
2398
|
+
email: normalizedEmail,
|
|
2399
|
+
...teamIds === void 0 ? {} : { teamIds },
|
|
2400
|
+
idempotencyKey: value.idempotencyKey
|
|
2401
|
+
};
|
|
2402
|
+
}
|
|
2403
|
+
function normalizeCreatedOrganizationMemberInvitation(value) {
|
|
2404
|
+
exact(value, ["expiresAt", "id"]);
|
|
2405
|
+
if (!isId(value.id)) throw authorityUnavailable();
|
|
2406
|
+
return {
|
|
2407
|
+
id: value.id,
|
|
2408
|
+
expiresAt: responseDate(value.expiresAt)
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2336
2411
|
function normalizeOrganizations(value) {
|
|
2337
2412
|
exact(value, ["nextCursor", "organizations"]);
|
|
2338
2413
|
if (!Array.isArray(value.organizations) || value.organizations.length > 100) throw authorityUnavailable();
|
|
@@ -2556,6 +2631,14 @@ function fromOrganizationReadAuthorityError(value, status) {
|
|
|
2556
2631
|
if (status === 403) return new AuthError("access_denied", 403);
|
|
2557
2632
|
return authorityUnavailable();
|
|
2558
2633
|
}
|
|
2634
|
+
function fromOrganizationMutationAuthorityError(value, status, retryAfter) {
|
|
2635
|
+
if (status === 400 || status === 413 || status === 415) return new AuthError("invalid_request", 400);
|
|
2636
|
+
if (status === 409) return new AuthError("state_conflict", 409);
|
|
2637
|
+
if (status === 429) return new AuthError("auth_request_rate_limited", 429, parseRetryAfter(retryAfter));
|
|
2638
|
+
const code = isObject(value) && typeof value.code === "string" ? value.code : void 0;
|
|
2639
|
+
if (status === 403 && code === "REQUEST_ORIGIN_DENIED") return new AuthError("request_origin_denied", 403);
|
|
2640
|
+
return fromOrganizationReadAuthorityError(value, status);
|
|
2641
|
+
}
|
|
2559
2642
|
function fromInvitationAuthorityError(value, status) {
|
|
2560
2643
|
const code = isObject(value) && typeof value.code === "string" ? value.code : void 0;
|
|
2561
2644
|
if (status === 401 && code === "UNAUTHORIZED") return new AuthError("authentication_required", 401);
|
|
@@ -3841,6 +3924,71 @@ function createAuthClient(options) {
|
|
|
3841
3924
|
async getOrganization(organizationId) {
|
|
3842
3925
|
return organizationRead(normalizeIdInput(organizationId), void 0, normalizeOrganization);
|
|
3843
3926
|
},
|
|
3927
|
+
async createOrganizationTeam(organizationId, name, idempotencyKey) {
|
|
3928
|
+
const id = normalizeIdInput(organizationId);
|
|
3929
|
+
const body = normalizeOrganizationTeamCreationInput(name, idempotencyKey);
|
|
3930
|
+
if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
|
|
3931
|
+
const generation = authorityGeneration;
|
|
3932
|
+
const value = await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", id, "teams"), {
|
|
3933
|
+
fetch: fetchImpl,
|
|
3934
|
+
timeoutMs,
|
|
3935
|
+
method: "POST",
|
|
3936
|
+
headers: { "content-type": "application/json" },
|
|
3937
|
+
body,
|
|
3938
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
3939
|
+
});
|
|
3940
|
+
if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
|
|
3941
|
+
return normalizeCreatedOrganizationTeam(value);
|
|
3942
|
+
},
|
|
3943
|
+
async createOrganizationMemberInvitation(organizationId, input) {
|
|
3944
|
+
const id = normalizeIdInput(organizationId);
|
|
3945
|
+
const body = normalizeOrganizationMemberInvitationCreationInput(input);
|
|
3946
|
+
if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
|
|
3947
|
+
const generation = authorityGeneration;
|
|
3948
|
+
const value = await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", id, "invitations"), {
|
|
3949
|
+
fetch: fetchImpl,
|
|
3950
|
+
timeoutMs,
|
|
3951
|
+
method: "POST",
|
|
3952
|
+
headers: { "content-type": "application/json" },
|
|
3953
|
+
body,
|
|
3954
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
3955
|
+
});
|
|
3956
|
+
if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
|
|
3957
|
+
return normalizeCreatedOrganizationMemberInvitation(value);
|
|
3958
|
+
},
|
|
3959
|
+
async updateOrganizationDisplay(organizationId, input) {
|
|
3960
|
+
const id = normalizeIdInput(organizationId);
|
|
3961
|
+
const desired = normalizeOrganizationDisplayInput(input);
|
|
3962
|
+
if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
|
|
3963
|
+
const generation = authorityGeneration;
|
|
3964
|
+
const value = await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", id, "display"), {
|
|
3965
|
+
fetch: fetchImpl,
|
|
3966
|
+
timeoutMs,
|
|
3967
|
+
method: "POST",
|
|
3968
|
+
headers: { "content-type": "application/json" },
|
|
3969
|
+
body: desired,
|
|
3970
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
3971
|
+
});
|
|
3972
|
+
if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
|
|
3973
|
+
return normalizeOrganizationDisplay(value);
|
|
3974
|
+
},
|
|
3975
|
+
async updateOrganizationTeamDisplay(organizationId, teamId, input) {
|
|
3976
|
+
const organization = normalizeIdInput(organizationId);
|
|
3977
|
+
const team = normalizeIdInput(teamId);
|
|
3978
|
+
const desired = normalizeOrganizationTeamDisplayInput(input);
|
|
3979
|
+
if (signOutRequest || activeAccountMutationGeneration !== void 0 || invitationRequest) throw new AuthError("state_conflict", 409);
|
|
3980
|
+
const generation = authorityGeneration;
|
|
3981
|
+
const value = await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", organization, "teams", team, "display"), {
|
|
3982
|
+
fetch: fetchImpl,
|
|
3983
|
+
timeoutMs,
|
|
3984
|
+
method: "POST",
|
|
3985
|
+
headers: { "content-type": "application/json" },
|
|
3986
|
+
body: desired,
|
|
3987
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
3988
|
+
});
|
|
3989
|
+
if (generation !== authorityGeneration) throw new AuthError("state_conflict", 409);
|
|
3990
|
+
return normalizeOrganizationTeamDisplay(value);
|
|
3991
|
+
},
|
|
3844
3992
|
async listOrganizationMembers(organizationId, input) {
|
|
3845
3993
|
return organizationRead(`${normalizeIdInput(organizationId)}/members`, input, normalizeOrganizationMembers);
|
|
3846
3994
|
},
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { A as ReceivedOrganizationInvitationPage, B as AuthErrorCode, C as OrganizationTeam, D as OrganizationWorkspaceSelection, E as OrganizationTeamPage, F as UpdateOrganizationDisplayInput, I as UpdateOrganizationTeamDisplayInput, N as SocialIdTokenInput, O as PersonalWorkspaceSelection, P as SocialIdTokenProvider, R as WorkspaceSelection, S as OrganizationPageInput, T as OrganizationTeamMemberPage, V as AuthErrorStatus, _ as OrganizationInvitation, b as OrganizationMemberPage, c as AuthUser, d as CreatedOrganizationTeam, f as DriveCredential, g as OrganizationDisplay, h as InvitationAcceptance, k as ReceivedOrganizationInvitation, l as CreateOrganizationMemberInvitationInput, o as AuthSession, p as DriveCredentialInput, r as AuthOrganization, t as AcceptInvitationInput, u as CreatedOrganizationMemberInvitation, v as OrganizationInvitationPage, w as OrganizationTeamDisplay, x as OrganizationPage, y as OrganizationMember, z as AuthError } from "./types-Byw9JQwL.js";
|
|
2
2
|
//#region src/server.d.ts
|
|
3
3
|
interface ServerAuthOptions {
|
|
4
4
|
authOrigin: string;
|
|
@@ -22,6 +22,10 @@ interface FinuevaServerAuth {
|
|
|
22
22
|
getSession(): Promise<AuthSession | null>;
|
|
23
23
|
listOrganizations(input?: OrganizationPageInput): Promise<OrganizationPage>;
|
|
24
24
|
getOrganization(organizationId: string): Promise<AuthOrganization>;
|
|
25
|
+
createOrganizationTeam(organizationId: string, name: string, idempotencyKey: string): Promise<CreatedOrganizationTeam>;
|
|
26
|
+
createOrganizationMemberInvitation(organizationId: string, input: CreateOrganizationMemberInvitationInput): Promise<CreatedOrganizationMemberInvitation>;
|
|
27
|
+
updateOrganizationDisplay(organizationId: string, input: UpdateOrganizationDisplayInput): Promise<OrganizationDisplay>;
|
|
28
|
+
updateOrganizationTeamDisplay(organizationId: string, teamId: string, input: UpdateOrganizationTeamDisplayInput): Promise<OrganizationTeamDisplay>;
|
|
25
29
|
listOrganizationMembers(organizationId: string, input?: OrganizationPageInput): Promise<OrganizationMemberPage>;
|
|
26
30
|
listOrganizationTeams(organizationId: string, input?: OrganizationPageInput): Promise<OrganizationTeamPage>;
|
|
27
31
|
listOrganizationTeamMembers(organizationId: string, teamId: string, input?: OrganizationPageInput): Promise<OrganizationTeamMemberPage>;
|
|
@@ -35,4 +39,4 @@ interface FinuevaServerAuth {
|
|
|
35
39
|
}
|
|
36
40
|
declare function createServerAuth(options: ServerAuthOptions): FinuevaServerAuth;
|
|
37
41
|
//#endregion
|
|
38
|
-
export { type AcceptInvitationInput, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthSession, type AuthUser, type DriveCredential, type DriveCredentialInput, FinuevaServerAuth, type InvitationAcceptance, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, ServerAuthOptions, ServerInvitationAcceptance, ServerSignOutResult, ServerSocialIdTokenSession, type SocialIdTokenInput, type SocialIdTokenProvider, type WorkspaceSelection, createServerAuth };
|
|
42
|
+
export { type AcceptInvitationInput, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthSession, type AuthUser, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, FinuevaServerAuth, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, ServerAuthOptions, ServerInvitationAcceptance, ServerSignOutResult, ServerSocialIdTokenSession, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type WorkspaceSelection, createServerAuth };
|
package/dist/server.js
CHANGED
|
@@ -475,6 +475,12 @@ const DRIVE_CREDENTIAL_LIFETIME_SECONDS = 840;
|
|
|
475
475
|
const id = /^[A-Za-z0-9_-]{1,128}$/;
|
|
476
476
|
const email = /^[^\s@]+@[^\s@]+$/;
|
|
477
477
|
const MAX_SOCIAL_ID_TOKEN_BYTES = 16 * 1024;
|
|
478
|
+
function normalizeEmail(value) {
|
|
479
|
+
if (typeof value !== "string") throw new TypeError("email is invalid");
|
|
480
|
+
const normalized = value.trim().toLowerCase();
|
|
481
|
+
if (normalized.length === 0 || normalized.length > 320 || !email.test(normalized)) throw new TypeError("email is invalid");
|
|
482
|
+
return normalized;
|
|
483
|
+
}
|
|
478
484
|
function normalizeSocialIdTokenInput(value) {
|
|
479
485
|
if (!isObject(value)) throw new TypeError("social ID token input is invalid");
|
|
480
486
|
const keys = Object.keys(value);
|
|
@@ -589,6 +595,81 @@ function normalizeOrganization(value) {
|
|
|
589
595
|
}
|
|
590
596
|
};
|
|
591
597
|
}
|
|
598
|
+
function normalizeOrganizationDisplayInput(value) {
|
|
599
|
+
if (!isObject(value) || Object.keys(value).sort().join(",") !== "logo,name,slug") throw new AuthError("invalid_request", 400);
|
|
600
|
+
if (!boundedString(value.name, 255) || !boundedString(value.slug, 255)) throw new AuthError("invalid_request", 400);
|
|
601
|
+
let logo;
|
|
602
|
+
try {
|
|
603
|
+
logo = nullableHttpsUrl(value.logo);
|
|
604
|
+
} catch {
|
|
605
|
+
throw new AuthError("invalid_request", 400);
|
|
606
|
+
}
|
|
607
|
+
return {
|
|
608
|
+
name: value.name,
|
|
609
|
+
slug: value.slug,
|
|
610
|
+
logo
|
|
611
|
+
};
|
|
612
|
+
}
|
|
613
|
+
function normalizeOrganizationDisplay(value) {
|
|
614
|
+
try {
|
|
615
|
+
return normalizeOrganizationDisplayInput(value);
|
|
616
|
+
} catch {
|
|
617
|
+
throw authorityUnavailable();
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
function normalizeOrganizationTeamDisplayInput(value) {
|
|
621
|
+
if (!isObject(value) || Object.keys(value).join(",") !== "name" || !boundedString(value.name, 255)) throw new AuthError("invalid_request", 400);
|
|
622
|
+
return { name: value.name };
|
|
623
|
+
}
|
|
624
|
+
function normalizeOrganizationTeamDisplay(value) {
|
|
625
|
+
try {
|
|
626
|
+
return normalizeOrganizationTeamDisplayInput(value);
|
|
627
|
+
} catch {
|
|
628
|
+
throw authorityUnavailable();
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
function normalizeOrganizationTeamCreationInput(name, idempotencyKey) {
|
|
632
|
+
if (!boundedString(name, 255) || !boundedString(idempotencyKey, 255)) throw new AuthError("invalid_request", 400);
|
|
633
|
+
return {
|
|
634
|
+
name,
|
|
635
|
+
idempotencyKey
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function normalizeCreatedOrganizationTeam(value) {
|
|
639
|
+
exact(value, ["id", "name"]);
|
|
640
|
+
if (!isId(value.id) || !boundedString(value.name, 255)) throw authorityUnavailable();
|
|
641
|
+
return {
|
|
642
|
+
id: value.id,
|
|
643
|
+
name: value.name
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
function normalizeOrganizationMemberInvitationCreationInput(value) {
|
|
647
|
+
if (!isObject(value)) throw new AuthError("invalid_request", 400);
|
|
648
|
+
const keys = Object.keys(value).sort().join(",");
|
|
649
|
+
if (keys !== "email,idempotencyKey" && keys !== "email,idempotencyKey,teamIds") throw new AuthError("invalid_request", 400);
|
|
650
|
+
let normalizedEmail;
|
|
651
|
+
try {
|
|
652
|
+
normalizedEmail = normalizeEmail(value.email);
|
|
653
|
+
} catch {
|
|
654
|
+
throw new AuthError("invalid_request", 400);
|
|
655
|
+
}
|
|
656
|
+
if (new TextEncoder().encode(normalizedEmail).byteLength > 320 || !boundedString(value.idempotencyKey, 255) || value.teamIds !== void 0 && (!Array.isArray(value.teamIds) || value.teamIds.length > 100)) throw new AuthError("invalid_request", 400);
|
|
657
|
+
const teamIds = value.teamIds?.map((teamId) => normalizeIdInput(teamId));
|
|
658
|
+
if (teamIds && new Set(teamIds).size !== teamIds.length) throw new AuthError("invalid_request", 400);
|
|
659
|
+
return {
|
|
660
|
+
email: normalizedEmail,
|
|
661
|
+
...teamIds === void 0 ? {} : { teamIds },
|
|
662
|
+
idempotencyKey: value.idempotencyKey
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function normalizeCreatedOrganizationMemberInvitation(value) {
|
|
666
|
+
exact(value, ["expiresAt", "id"]);
|
|
667
|
+
if (!isId(value.id)) throw authorityUnavailable();
|
|
668
|
+
return {
|
|
669
|
+
id: value.id,
|
|
670
|
+
expiresAt: responseDate(value.expiresAt)
|
|
671
|
+
};
|
|
672
|
+
}
|
|
592
673
|
function normalizeOrganizations(value) {
|
|
593
674
|
exact(value, ["nextCursor", "organizations"]);
|
|
594
675
|
if (!Array.isArray(value.organizations) || value.organizations.length > 100) throw authorityUnavailable();
|
|
@@ -798,6 +879,14 @@ function fromOrganizationReadAuthorityError(value, status) {
|
|
|
798
879
|
if (status === 403) return new AuthError("access_denied", 403);
|
|
799
880
|
return authorityUnavailable();
|
|
800
881
|
}
|
|
882
|
+
function fromOrganizationMutationAuthorityError(value, status, retryAfter) {
|
|
883
|
+
if (status === 400 || status === 413 || status === 415) return new AuthError("invalid_request", 400);
|
|
884
|
+
if (status === 409) return new AuthError("state_conflict", 409);
|
|
885
|
+
if (status === 429) return new AuthError("auth_request_rate_limited", 429, parseRetryAfter(retryAfter));
|
|
886
|
+
const code = isObject(value) && typeof value.code === "string" ? value.code : void 0;
|
|
887
|
+
if (status === 403 && code === "REQUEST_ORIGIN_DENIED") return new AuthError("request_origin_denied", 403);
|
|
888
|
+
return fromOrganizationReadAuthorityError(value, status);
|
|
889
|
+
}
|
|
801
890
|
function fromInvitationAuthorityError(value, status) {
|
|
802
891
|
const code = isObject(value) && typeof value.code === "string" ? value.code : void 0;
|
|
803
892
|
if (status === 401 && code === "UNAUTHORIZED") return new AuthError("authentication_required", 401);
|
|
@@ -1062,6 +1151,71 @@ function createServerAuth(options) {
|
|
|
1062
1151
|
getOrganization(organizationId) {
|
|
1063
1152
|
return organizationRead(normalizeIdInput(organizationId), void 0, normalizeOrganization);
|
|
1064
1153
|
},
|
|
1154
|
+
async createOrganizationTeam(organizationId, name, idempotencyKey) {
|
|
1155
|
+
const id = normalizeIdInput(organizationId);
|
|
1156
|
+
const body = normalizeOrganizationTeamCreationInput(name, idempotencyKey);
|
|
1157
|
+
return normalizeCreatedOrganizationTeam(await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", id, "teams"), {
|
|
1158
|
+
fetch: fetchImpl,
|
|
1159
|
+
timeoutMs,
|
|
1160
|
+
method: "POST",
|
|
1161
|
+
credentials,
|
|
1162
|
+
headers: {
|
|
1163
|
+
...authHeaders(!authorization),
|
|
1164
|
+
"content-type": "application/json"
|
|
1165
|
+
},
|
|
1166
|
+
body,
|
|
1167
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
1168
|
+
}));
|
|
1169
|
+
},
|
|
1170
|
+
async createOrganizationMemberInvitation(organizationId, input) {
|
|
1171
|
+
const id = normalizeIdInput(organizationId);
|
|
1172
|
+
const body = normalizeOrganizationMemberInvitationCreationInput(input);
|
|
1173
|
+
return normalizeCreatedOrganizationMemberInvitation(await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", id, "invitations"), {
|
|
1174
|
+
fetch: fetchImpl,
|
|
1175
|
+
timeoutMs,
|
|
1176
|
+
method: "POST",
|
|
1177
|
+
credentials,
|
|
1178
|
+
headers: {
|
|
1179
|
+
...authHeaders(!authorization),
|
|
1180
|
+
"content-type": "application/json"
|
|
1181
|
+
},
|
|
1182
|
+
body,
|
|
1183
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
1184
|
+
}));
|
|
1185
|
+
},
|
|
1186
|
+
async updateOrganizationDisplay(organizationId, input) {
|
|
1187
|
+
const id = normalizeIdInput(organizationId);
|
|
1188
|
+
const desired = normalizeOrganizationDisplayInput(input);
|
|
1189
|
+
return normalizeOrganizationDisplay(await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", id, "display"), {
|
|
1190
|
+
fetch: fetchImpl,
|
|
1191
|
+
timeoutMs,
|
|
1192
|
+
method: "POST",
|
|
1193
|
+
credentials,
|
|
1194
|
+
headers: {
|
|
1195
|
+
...authHeaders(!authorization),
|
|
1196
|
+
"content-type": "application/json"
|
|
1197
|
+
},
|
|
1198
|
+
body: desired,
|
|
1199
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
1200
|
+
}));
|
|
1201
|
+
},
|
|
1202
|
+
async updateOrganizationTeamDisplay(organizationId, teamId, input) {
|
|
1203
|
+
const organization = normalizeIdInput(organizationId);
|
|
1204
|
+
const team = normalizeIdInput(teamId);
|
|
1205
|
+
const desired = normalizeOrganizationTeamDisplayInput(input);
|
|
1206
|
+
return normalizeOrganizationTeamDisplay(await requestJson(joinURL(authOrigin, "api/v1/auth/organizations", organization, "teams", team, "display"), {
|
|
1207
|
+
fetch: fetchImpl,
|
|
1208
|
+
timeoutMs,
|
|
1209
|
+
method: "POST",
|
|
1210
|
+
credentials,
|
|
1211
|
+
headers: {
|
|
1212
|
+
...authHeaders(!authorization),
|
|
1213
|
+
"content-type": "application/json"
|
|
1214
|
+
},
|
|
1215
|
+
body: desired,
|
|
1216
|
+
mapError: fromOrganizationMutationAuthorityError
|
|
1217
|
+
}));
|
|
1218
|
+
},
|
|
1065
1219
|
listOrganizationMembers(organizationId, input) {
|
|
1066
1220
|
return organizationRead(`${normalizeIdInput(organizationId)}/members`, input, normalizeOrganizationMembers);
|
|
1067
1221
|
},
|
|
@@ -34,6 +34,16 @@ interface AuthOrganization {
|
|
|
34
34
|
readInvitations: boolean;
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
+
interface OrganizationDisplay {
|
|
38
|
+
name: string;
|
|
39
|
+
slug: string;
|
|
40
|
+
logo: string | null;
|
|
41
|
+
}
|
|
42
|
+
type UpdateOrganizationDisplayInput = OrganizationDisplay;
|
|
43
|
+
interface OrganizationTeamDisplay {
|
|
44
|
+
name: string;
|
|
45
|
+
}
|
|
46
|
+
type UpdateOrganizationTeamDisplayInput = OrganizationTeamDisplay;
|
|
37
47
|
interface OrganizationPageInput {
|
|
38
48
|
limit?: number;
|
|
39
49
|
cursor?: string;
|
|
@@ -63,6 +73,19 @@ interface OrganizationTeam {
|
|
|
63
73
|
category: "manual" | "managed";
|
|
64
74
|
createdAt: Date;
|
|
65
75
|
}
|
|
76
|
+
interface CreatedOrganizationTeam {
|
|
77
|
+
id: string;
|
|
78
|
+
name: string;
|
|
79
|
+
}
|
|
80
|
+
interface CreateOrganizationMemberInvitationInput {
|
|
81
|
+
email: string;
|
|
82
|
+
teamIds?: string[];
|
|
83
|
+
idempotencyKey: string;
|
|
84
|
+
}
|
|
85
|
+
interface CreatedOrganizationMemberInvitation {
|
|
86
|
+
id: string;
|
|
87
|
+
expiresAt: Date;
|
|
88
|
+
}
|
|
66
89
|
interface OrganizationTeamPage {
|
|
67
90
|
teams: OrganizationTeam[];
|
|
68
91
|
nextCursor: string | null;
|
|
@@ -184,6 +207,10 @@ interface FinuevaAuthClient {
|
|
|
184
207
|
getSession(): Promise<AuthSession | null>;
|
|
185
208
|
listOrganizations(input?: OrganizationPageInput): Promise<OrganizationPage>;
|
|
186
209
|
getOrganization(organizationId: string): Promise<AuthOrganization>;
|
|
210
|
+
createOrganizationTeam(organizationId: string, name: string, idempotencyKey: string): Promise<CreatedOrganizationTeam>;
|
|
211
|
+
createOrganizationMemberInvitation(organizationId: string, input: CreateOrganizationMemberInvitationInput): Promise<CreatedOrganizationMemberInvitation>;
|
|
212
|
+
updateOrganizationDisplay(organizationId: string, input: UpdateOrganizationDisplayInput): Promise<OrganizationDisplay>;
|
|
213
|
+
updateOrganizationTeamDisplay(organizationId: string, teamId: string, input: UpdateOrganizationTeamDisplayInput): Promise<OrganizationTeamDisplay>;
|
|
187
214
|
listOrganizationMembers(organizationId: string, input?: OrganizationPageInput): Promise<OrganizationMemberPage>;
|
|
188
215
|
listOrganizationTeams(organizationId: string, input?: OrganizationPageInput): Promise<OrganizationTeamPage>;
|
|
189
216
|
listOrganizationTeamMembers(organizationId: string, teamId: string, input?: OrganizationPageInput): Promise<OrganizationTeamMemberPage>;
|
|
@@ -205,4 +232,4 @@ interface FinuevaAuthClient {
|
|
|
205
232
|
onSessionChange(listener: (session: AuthSession | null) => void): () => void;
|
|
206
233
|
}
|
|
207
234
|
//#endregion
|
|
208
|
-
export {
|
|
235
|
+
export { ReceivedOrganizationInvitationPage as A, AuthErrorCode as B, OrganizationTeam as C, OrganizationWorkspaceSelection as D, OrganizationTeamPage as E, UpdateOrganizationDisplayInput as F, UpdateOrganizationTeamDisplayInput as I, UpdateProfileInput as L, SignInInput as M, SocialIdTokenInput as N, PersonalWorkspaceSelection as O, SocialIdTokenProvider as P, WorkspaceSelection as R, OrganizationPageInput as S, OrganizationTeamMemberPage as T, AuthErrorStatus as V, OrganizationInvitation as _, AuthPersistenceDriver as a, OrganizationMemberPage as b, AuthUser as c, CreatedOrganizationTeam as d, DriveCredential as f, OrganizationDisplay as g, InvitationAcceptance as h, AuthPersistence as i, SendVerificationEmailInput as j, ReceivedOrganizationInvitation as k, CreateOrganizationMemberInvitationInput as l, FinuevaAuthClient as m, AuthClientOptions as n, AuthSession as o, DriveCredentialInput as p, AuthOrganization as r, AuthState as s, AcceptInvitationInput as t, CreatedOrganizationMemberInvitation as u, OrganizationInvitationPage as v, OrganizationTeamDisplay as w, OrganizationPage as x, OrganizationMember as y, AuthError as z };
|