@finueva/auth 0.14.1 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/device.d.ts +1 -1
- package/dist/device.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -0
- package/dist/server.d.ts +22 -2
- package/dist/server.js +176 -22
- package/dist/{types-Dsu3nEaN.d.ts → types-Dpy17xNC.d.ts} +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -254,6 +254,36 @@ return new Response(null, { status: 303, headers });
|
|
|
254
254
|
|
|
255
255
|
Call `applyCookies()` after server `signOut()`, `acceptInvitation()`, and `signInWithSocialIdToken()`.
|
|
256
256
|
|
|
257
|
+
## Resolve A Drive Recipient
|
|
258
|
+
|
|
259
|
+
Use the separate server client. Before lookup, Drive must check the caller's current permission to manage the item's shares. Recipient resolution does not grant file access.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { createDriveRecipientAuth } from "@finueva/auth/server";
|
|
263
|
+
|
|
264
|
+
const recipients = createDriveRecipientAuth({
|
|
265
|
+
authOrigin: "https://auth.finueva.com",
|
|
266
|
+
timeoutMs: 10_000,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const recipient = await recipients.resolveRecipient(
|
|
270
|
+
{ email: "ada@example.com" },
|
|
271
|
+
clientAssertion, // Generate a new service assertion for this request.
|
|
272
|
+
);
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
The result contains only `{ userId, name, email }`. The SDK trims and lowercases the exact email input. It accepts no additional input fields. Email has a 320-byte UTF-8 limit. The JSON request has a 1024-byte limit. The response has a 4096-byte limit. The canonical ID contains 1 to 128 URL-safe characters. The nonempty name has a 1024-byte UTF-8 limit. A response email must be normalized and match the requested email.
|
|
276
|
+
|
|
277
|
+
The caller generates one Ed25519 assertion with `typ: "finueva-drive-recipient-assertion+jwt"` and the registered JWK-thumbprint `kid`. Set `aud` to the exact Auth endpoint, such as `https://auth.finueva.com/api/v1/auth/drive-share-recipients/resolve`. Set `iss` and `sub` to the same registered service ID. Include the registered `environment`, `ver: 1`, `principal_type: "ecosystem-service"`, a new bounded `jti`, and `exp = iat + 60` in seconds. Auth permits 30 seconds of clock tolerance. A provisioning assertion cannot replace this assertion. The SDK accepts no keys. Auth verifies the assertion; the SDK checks only its compact syntax and the 16 KiB bearer-header limit.
|
|
278
|
+
|
|
279
|
+
This client sends no cookies or Origin. It follows no redirects, retains no recipient cache, and makes no automatic retries. It permits one active request per client and no queue. The default deadline is 10 seconds. `timeoutMs` accepts an integer from 1 to 60000. If a custom transport ignores abort, the client rejects new calls until that transport settles. A custom `fetch` must preserve the request's credential and redirect restrictions.
|
|
280
|
+
|
|
281
|
+
After a lost response or failure, use a new assertion for an explicit retry. Auth can consume an assertion even when the request fails. Missing or ineligible recipients return `AuthError` with `recipient_unavailable` and status 404. Exact invalid-request responses map to `invalid_request` with status 400 or 413. Invalid assertions map to `authentication_required` with status 401. Rate limits map to `auth_request_rate_limited` with status 429 and `retryAfterSeconds` from 1 to 60. Invalid responses, transport failures, and service failures map to `identity_authority_unavailable` with status 503. An overlapping call returns `state_conflict` with status 409. Error messages contain no request values.
|
|
282
|
+
|
|
283
|
+
Malformed assertion syntax fails locally with `invalid_request` and status 400. An exact `405 METHOD_NOT_ALLOWED` response with message `METHOD_NOT_ALLOWED` maps to `invalid_request` with status 405. Other non-contract errors fail closed with status 503. The SDK does not expose raw Auth error messages.
|
|
284
|
+
|
|
285
|
+
The factory and recipient types exist only in `@finueva/auth/server`. The cookie and session client from `createServerAuth()` does not expose recipient resolution.
|
|
286
|
+
|
|
257
287
|
## Device Quick Start
|
|
258
288
|
|
|
259
289
|
Use the device flow for a CLI or native application that can open a system browser:
|
package/dist/device.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, G as SocialIdTokenProvider, J as UpdateProfileInput, K as UpdateOrganizationDisplayInput, O as OrganizationTeamPage, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, W as SocialIdTokenInput, X as AuthError, Z as AuthErrorCode, _ as OrganizationDisplay, b as OrganizationMember, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, m as DriveCredentialInput, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, u as CreateOrganizationMemberInvitationInput, v as OrganizationInvitation, w as OrganizationPageInput, x as OrganizationMemberAuthority, y as OrganizationInvitationPage, z as ReceivedOrganizationInvitationPage } from "./types-
|
|
1
|
+
import { C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, G as SocialIdTokenProvider, J as UpdateProfileInput, K as UpdateOrganizationDisplayInput, O as OrganizationTeamPage, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, W as SocialIdTokenInput, X as AuthError, Z as AuthErrorCode, _ as OrganizationDisplay, b as OrganizationMember, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, m as DriveCredentialInput, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, u as CreateOrganizationMemberInvitationInput, v as OrganizationInvitation, w as OrganizationPageInput, x as OrganizationMemberAuthority, y as OrganizationInvitationPage, z as ReceivedOrganizationInvitationPage } from "./types-Dpy17xNC.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 {
|
package/dist/device.js
CHANGED
|
@@ -447,6 +447,7 @@ const messages = {
|
|
|
447
447
|
invalid_request: "The request is invalid.",
|
|
448
448
|
invalid_invitation: "The invitation is invalid.",
|
|
449
449
|
invitation_unavailable: "The invitation is not available.",
|
|
450
|
+
recipient_unavailable: "The recipient is unavailable.",
|
|
450
451
|
invitation_recipient_mismatch: "The invitation cannot be accepted by this account.",
|
|
451
452
|
invitation_recovery_required: "Invitation administration requires separate recovery.",
|
|
452
453
|
email_verification_required: "Email verification is required.",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as PersonalWorkspaceSelection, B as RecentAuthenticationStatus, C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, F as PlatformRoleChangeRecentAuthenticationInput, G as SocialIdTokenProvider, H as SendVerificationEmailInput, I as PlatformRoleFirstApproval, J as UpdateProfileInput, K as UpdateOrganizationDisplayInput, L as PlatformStatus, M as PlatformRole, N as PlatformRoleApplied, O as OrganizationTeamPage, P as PlatformRoleApproval, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, U as SignInInput, V as ResentOrganizationInvitation, W as SocialIdTokenInput, X as AuthError, Y as WorkspaceSelection, Z as AuthErrorCode, _ as OrganizationDisplay, a as AuthPersistenceDriver, b as OrganizationMember, c as AuthUser, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, g as InvitationAcceptance, h as FinuevaAuthClient, i as AuthPersistence, j as PlatformBootstrapRoleChangeInput, k as OrganizationWorkspaceSelection, l as CanceledOrganizationInvitation, m as DriveCredentialInput, n as AuthClientOptions, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, s as AuthState, t as AcceptInvitationInput, u as CreateOrganizationMemberInvitationInput, v as OrganizationInvitation, w as OrganizationPageInput, x as OrganizationMemberAuthority, y as OrganizationInvitationPage, z as ReceivedOrganizationInvitationPage } from "./types-
|
|
1
|
+
import { A as PersonalWorkspaceSelection, B as RecentAuthenticationStatus, C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, F as PlatformRoleChangeRecentAuthenticationInput, G as SocialIdTokenProvider, H as SendVerificationEmailInput, I as PlatformRoleFirstApproval, J as UpdateProfileInput, K as UpdateOrganizationDisplayInput, L as PlatformStatus, M as PlatformRole, N as PlatformRoleApplied, O as OrganizationTeamPage, P as PlatformRoleApproval, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, U as SignInInput, V as ResentOrganizationInvitation, W as SocialIdTokenInput, X as AuthError, Y as WorkspaceSelection, Z as AuthErrorCode, _ as OrganizationDisplay, a as AuthPersistenceDriver, b as OrganizationMember, c as AuthUser, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, g as InvitationAcceptance, h as FinuevaAuthClient, i as AuthPersistence, j as PlatformBootstrapRoleChangeInput, k as OrganizationWorkspaceSelection, l as CanceledOrganizationInvitation, m as DriveCredentialInput, n as AuthClientOptions, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, s as AuthState, t as AcceptInvitationInput, u as CreateOrganizationMemberInvitationInput, v as OrganizationInvitation, w as OrganizationPageInput, x as OrganizationMemberAuthority, y as OrganizationInvitationPage, z as ReceivedOrganizationInvitationPage } from "./types-Dpy17xNC.js";
|
|
2
2
|
//#region src/client.d.ts
|
|
3
3
|
declare function createAuthClient(options: AuthClientOptions): FinuevaAuthClient;
|
|
4
4
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -2543,6 +2543,7 @@ const messages = {
|
|
|
2543
2543
|
invalid_request: "The request is invalid.",
|
|
2544
2544
|
invalid_invitation: "The invitation is invalid.",
|
|
2545
2545
|
invitation_unavailable: "The invitation is not available.",
|
|
2546
|
+
recipient_unavailable: "The recipient is unavailable.",
|
|
2546
2547
|
invitation_recipient_mismatch: "The invitation cannot be accepted by this account.",
|
|
2547
2548
|
invitation_recovery_required: "Invitation administration requires separate recovery.",
|
|
2548
2549
|
email_verification_required: "Email verification is required.",
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,24 @@
|
|
|
1
|
-
import { A as PersonalWorkspaceSelection, C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, G as SocialIdTokenProvider, K as UpdateOrganizationDisplayInput, O as OrganizationTeamPage, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, V as ResentOrganizationInvitation, W as SocialIdTokenInput, X as AuthError, Y as WorkspaceSelection, Z as AuthErrorCode, _ as OrganizationDisplay, b as OrganizationMember, c as AuthUser, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, g as InvitationAcceptance, k as OrganizationWorkspaceSelection, l as CanceledOrganizationInvitation, m as DriveCredentialInput, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, t as AcceptInvitationInput, u as CreateOrganizationMemberInvitationInput, v as OrganizationInvitation, w as OrganizationPageInput, x as OrganizationMemberAuthority, y as OrganizationInvitationPage, z as ReceivedOrganizationInvitationPage } from "./types-
|
|
1
|
+
import { A as PersonalWorkspaceSelection, C as OrganizationPage, D as OrganizationTeamMemberPage, E as OrganizationTeamDisplay, G as SocialIdTokenProvider, K as UpdateOrganizationDisplayInput, O as OrganizationTeamPage, Q as AuthErrorStatus, R as ReceivedOrganizationInvitation, S as OrganizationMemberPage, T as OrganizationTeam, V as ResentOrganizationInvitation, W as SocialIdTokenInput, X as AuthError, Y as WorkspaceSelection, Z as AuthErrorCode, _ as OrganizationDisplay, b as OrganizationMember, c as AuthUser, d as CreatedOrganizationMemberInvitation, f as CreatedOrganizationTeam, g as InvitationAcceptance, k as OrganizationWorkspaceSelection, l as CanceledOrganizationInvitation, m as DriveCredentialInput, o as AuthSession, p as DriveCredential, q as UpdateOrganizationTeamDisplayInput, r as AuthOrganization, t as AcceptInvitationInput, u as CreateOrganizationMemberInvitationInput, v as OrganizationInvitation, w as OrganizationPageInput, x as OrganizationMemberAuthority, y as OrganizationInvitationPage, z as ReceivedOrganizationInvitationPage } from "./types-Dpy17xNC.js";
|
|
2
|
+
//#region src/drive-recipients.d.ts
|
|
3
|
+
interface DriveRecipientAuthOptions {
|
|
4
|
+
authOrigin: string;
|
|
5
|
+
fetch?: typeof globalThis.fetch;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
interface ResolveDriveRecipientInput {
|
|
9
|
+
email: string;
|
|
10
|
+
}
|
|
11
|
+
interface DriveRecipient {
|
|
12
|
+
userId: string;
|
|
13
|
+
name: string;
|
|
14
|
+
email: string;
|
|
15
|
+
}
|
|
16
|
+
interface FinuevaDriveRecipientAuth {
|
|
17
|
+
resolveRecipient(input: ResolveDriveRecipientInput, clientAssertion: string): Promise<DriveRecipient>;
|
|
18
|
+
}
|
|
19
|
+
/** Service authority only. The caller signs assertions; Auth verifies their claims and signature. */
|
|
20
|
+
declare function createDriveRecipientAuth(options: DriveRecipientAuthOptions): FinuevaDriveRecipientAuth;
|
|
21
|
+
//#endregion
|
|
2
22
|
//#region src/provisioning.d.ts
|
|
3
23
|
interface DriveProvisioningAuthOptions {
|
|
4
24
|
authOrigin: string;
|
|
@@ -60,4 +80,4 @@ interface FinuevaServerAuth {
|
|
|
60
80
|
}
|
|
61
81
|
declare function createServerAuth(options: ServerAuthOptions): FinuevaServerAuth;
|
|
62
82
|
//#endregion
|
|
63
|
-
export { type AcceptInvitationInput, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthSession, type AuthUser, type CanceledOrganizationInvitation, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type DriveProvisioningAuthOptions, type DriveProvisioningCredential, type FinuevaDriveProvisioningAuth, FinuevaServerAuth, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberAuthority, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type ResentOrganizationInvitation, ServerAuthOptions, ServerInvitationAcceptance, ServerSignOutResult, ServerSocialIdTokenSession, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type WorkspaceSelection, createDriveProvisioningAuth, createServerAuth };
|
|
83
|
+
export { type AcceptInvitationInput, AuthError, type AuthErrorCode, type AuthErrorStatus, type AuthOrganization, type AuthSession, type AuthUser, type CanceledOrganizationInvitation, type CreateOrganizationMemberInvitationInput, type CreatedOrganizationMemberInvitation, type CreatedOrganizationTeam, type DriveCredential, type DriveCredentialInput, type DriveProvisioningAuthOptions, type DriveProvisioningCredential, type DriveRecipient, type DriveRecipientAuthOptions, type FinuevaDriveProvisioningAuth, type FinuevaDriveRecipientAuth, FinuevaServerAuth, type InvitationAcceptance, type OrganizationDisplay, type OrganizationInvitation, type OrganizationInvitationPage, type OrganizationMember, type OrganizationMemberAuthority, type OrganizationMemberPage, type OrganizationPage, type OrganizationPageInput, type OrganizationTeam, type OrganizationTeamDisplay, type OrganizationTeamMemberPage, type OrganizationTeamPage, type OrganizationWorkspaceSelection, type PersonalWorkspaceSelection, type ReceivedOrganizationInvitation, type ReceivedOrganizationInvitationPage, type ResentOrganizationInvitation, type ResolveDriveRecipientInput, ServerAuthOptions, ServerInvitationAcceptance, ServerSignOutResult, ServerSocialIdTokenSession, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type WorkspaceSelection, createDriveProvisioningAuth, createDriveRecipientAuth, createServerAuth };
|
package/dist/server.js
CHANGED
|
@@ -184,6 +184,7 @@ const messages = {
|
|
|
184
184
|
invalid_request: "The request is invalid.",
|
|
185
185
|
invalid_invitation: "The invitation is invalid.",
|
|
186
186
|
invitation_unavailable: "The invitation is not available.",
|
|
187
|
+
recipient_unavailable: "The recipient is unavailable.",
|
|
187
188
|
invitation_recipient_mismatch: "The invitation cannot be accepted by this account.",
|
|
188
189
|
invitation_recovery_required: "Invitation administration requires separate recovery.",
|
|
189
190
|
email_verification_required: "Email verification is required.",
|
|
@@ -306,7 +307,7 @@ function normalizeIdInput(value) {
|
|
|
306
307
|
return value;
|
|
307
308
|
}
|
|
308
309
|
function normalizeOrganization(value) {
|
|
309
|
-
exact(value, [
|
|
310
|
+
exact$1(value, [
|
|
310
311
|
"capabilities",
|
|
311
312
|
"id",
|
|
312
313
|
"lifecycle",
|
|
@@ -316,7 +317,7 @@ function normalizeOrganization(value) {
|
|
|
316
317
|
"slug"
|
|
317
318
|
]);
|
|
318
319
|
if (!isId(value.id) || !boundedString(value.name, 255) || !boundedString(value.slug, 255) || value.lifecycle !== "ordinary" && value.lifecycle !== "managed" || !role(value.role) || !isObject(value.capabilities)) throw authorityUnavailable();
|
|
319
|
-
exact(value.capabilities, [
|
|
320
|
+
exact$1(value.capabilities, [
|
|
320
321
|
"readInvitations",
|
|
321
322
|
"readMembers",
|
|
322
323
|
"readTeams"
|
|
@@ -377,7 +378,7 @@ function normalizeOrganizationTeamCreationInput(name, idempotencyKey) {
|
|
|
377
378
|
};
|
|
378
379
|
}
|
|
379
380
|
function normalizeCreatedOrganizationTeam(value) {
|
|
380
|
-
exact(value, ["id", "name"]);
|
|
381
|
+
exact$1(value, ["id", "name"]);
|
|
381
382
|
if (!isId(value.id) || !boundedString(value.name, 255)) throw authorityUnavailable();
|
|
382
383
|
return {
|
|
383
384
|
id: value.id,
|
|
@@ -404,7 +405,7 @@ function normalizeOrganizationMemberInvitationCreationInput(value) {
|
|
|
404
405
|
};
|
|
405
406
|
}
|
|
406
407
|
function normalizeCreatedOrganizationMemberInvitation(value) {
|
|
407
|
-
exact(value, ["expiresAt", "id"]);
|
|
408
|
+
exact$1(value, ["expiresAt", "id"]);
|
|
408
409
|
if (!isId(value.id)) throw authorityUnavailable();
|
|
409
410
|
return {
|
|
410
411
|
id: value.id,
|
|
@@ -421,12 +422,12 @@ function normalizeOrganizationInvitationAdministrationInput(idempotencyKey) {
|
|
|
421
422
|
return { idempotencyKey };
|
|
422
423
|
}
|
|
423
424
|
function normalizeCanceledOrganizationInvitation(value) {
|
|
424
|
-
exact(value, ["status"]);
|
|
425
|
+
exact$1(value, ["status"]);
|
|
425
426
|
if (value.status !== "canceled") throw authorityUnavailable();
|
|
426
427
|
return { status: "canceled" };
|
|
427
428
|
}
|
|
428
429
|
function normalizeOrganizations(value) {
|
|
429
|
-
exact(value, ["nextCursor", "organizations"]);
|
|
430
|
+
exact$1(value, ["nextCursor", "organizations"]);
|
|
430
431
|
if (!Array.isArray(value.organizations) || value.organizations.length > 100) throw authorityUnavailable();
|
|
431
432
|
return {
|
|
432
433
|
organizations: unique(value.organizations.map(normalizeOrganization), (item) => item.id),
|
|
@@ -434,11 +435,11 @@ function normalizeOrganizations(value) {
|
|
|
434
435
|
};
|
|
435
436
|
}
|
|
436
437
|
function normalizeOrganizationMembers(value) {
|
|
437
|
-
exact(value, ["members", "nextCursor"]);
|
|
438
|
+
exact$1(value, ["members", "nextCursor"]);
|
|
438
439
|
if (!Array.isArray(value.members) || value.members.length > 100) throw authorityUnavailable();
|
|
439
440
|
return {
|
|
440
441
|
members: unique(value.members.map((member) => {
|
|
441
|
-
exact(member, [
|
|
442
|
+
exact$1(member, [
|
|
442
443
|
"email",
|
|
443
444
|
"id",
|
|
444
445
|
"image",
|
|
@@ -467,11 +468,11 @@ function normalizeOrganizationMembers(value) {
|
|
|
467
468
|
};
|
|
468
469
|
}
|
|
469
470
|
function normalizeOrganizationTeams(value) {
|
|
470
|
-
exact(value, ["nextCursor", "teams"]);
|
|
471
|
+
exact$1(value, ["nextCursor", "teams"]);
|
|
471
472
|
if (!Array.isArray(value.teams) || value.teams.length > 100) throw authorityUnavailable();
|
|
472
473
|
return {
|
|
473
474
|
teams: unique(value.teams.map((team) => {
|
|
474
|
-
exact(team, [
|
|
475
|
+
exact$1(team, [
|
|
475
476
|
"category",
|
|
476
477
|
"createdAt",
|
|
477
478
|
"id",
|
|
@@ -489,7 +490,7 @@ function normalizeOrganizationTeams(value) {
|
|
|
489
490
|
};
|
|
490
491
|
}
|
|
491
492
|
function normalizeOrganizationTeamMembers(value) {
|
|
492
|
-
exact(value, ["memberIds", "nextCursor"]);
|
|
493
|
+
exact$1(value, ["memberIds", "nextCursor"]);
|
|
493
494
|
if (!Array.isArray(value.memberIds) || value.memberIds.length > 100) throw authorityUnavailable();
|
|
494
495
|
return {
|
|
495
496
|
memberIds: unique(value.memberIds.map((item) => normalizeResponseId(item)), (item) => item),
|
|
@@ -497,7 +498,7 @@ function normalizeOrganizationTeamMembers(value) {
|
|
|
497
498
|
};
|
|
498
499
|
}
|
|
499
500
|
function normalizeOrganizationInvitation(value) {
|
|
500
|
-
exact(value, [
|
|
501
|
+
exact$1(value, [
|
|
501
502
|
"createdAt",
|
|
502
503
|
"email",
|
|
503
504
|
"expiresAt",
|
|
@@ -508,7 +509,7 @@ function normalizeOrganizationInvitation(value) {
|
|
|
508
509
|
"teamIds"
|
|
509
510
|
]);
|
|
510
511
|
if (!isId(value.id) || typeof value.email !== "string" || value.email.length > 320 || !email.test(value.email) || !role(value.role) || value.status !== "pending" || !Array.isArray(value.teamIds) || value.teamIds.length > 100 || !isObject(value.inviter)) throw authorityUnavailable();
|
|
511
|
-
exact(value.inviter, ["membershipId", "name"]);
|
|
512
|
+
exact$1(value.inviter, ["membershipId", "name"]);
|
|
512
513
|
if (!isId(value.inviter.membershipId) || !boundedString(value.inviter.name, 255)) throw authorityUnavailable();
|
|
513
514
|
return {
|
|
514
515
|
id: value.id,
|
|
@@ -525,7 +526,7 @@ function normalizeOrganizationInvitation(value) {
|
|
|
525
526
|
};
|
|
526
527
|
}
|
|
527
528
|
function normalizeOrganizationInvitations(value) {
|
|
528
|
-
exact(value, ["invitations", "nextCursor"]);
|
|
529
|
+
exact$1(value, ["invitations", "nextCursor"]);
|
|
529
530
|
if (!Array.isArray(value.invitations) || value.invitations.length > 100) throw authorityUnavailable();
|
|
530
531
|
return {
|
|
531
532
|
invitations: unique(value.invitations.map(normalizeOrganizationInvitation), (item) => item.id),
|
|
@@ -533,7 +534,7 @@ function normalizeOrganizationInvitations(value) {
|
|
|
533
534
|
};
|
|
534
535
|
}
|
|
535
536
|
function normalizeReceivedInvitation(value) {
|
|
536
|
-
exact(value, [
|
|
537
|
+
exact$1(value, [
|
|
537
538
|
"expiresAt",
|
|
538
539
|
"id",
|
|
539
540
|
"inviterName",
|
|
@@ -542,7 +543,7 @@ function normalizeReceivedInvitation(value) {
|
|
|
542
543
|
"teams"
|
|
543
544
|
]);
|
|
544
545
|
if (!isId(value.id) || !boundedString(value.inviterName, 255) || !role(value.role) || !Array.isArray(value.teams) || value.teams.length > 100 || !isObject(value.organization)) throw authorityUnavailable();
|
|
545
|
-
exact(value.organization, [
|
|
546
|
+
exact$1(value.organization, [
|
|
546
547
|
"id",
|
|
547
548
|
"logo",
|
|
548
549
|
"name",
|
|
@@ -559,7 +560,7 @@ function normalizeReceivedInvitation(value) {
|
|
|
559
560
|
},
|
|
560
561
|
role: value.role,
|
|
561
562
|
teams: unique(value.teams.map((team) => {
|
|
562
|
-
exact(team, ["id", "name"]);
|
|
563
|
+
exact$1(team, ["id", "name"]);
|
|
563
564
|
if (!isId(team.id) || !boundedString(team.name, 255)) throw authorityUnavailable();
|
|
564
565
|
return {
|
|
565
566
|
id: team.id,
|
|
@@ -571,7 +572,7 @@ function normalizeReceivedInvitation(value) {
|
|
|
571
572
|
};
|
|
572
573
|
}
|
|
573
574
|
function normalizeReceivedInvitations(value) {
|
|
574
|
-
exact(value, ["invitations", "nextCursor"]);
|
|
575
|
+
exact$1(value, ["invitations", "nextCursor"]);
|
|
575
576
|
if (!Array.isArray(value.invitations) || value.invitations.length > 100) throw authorityUnavailable();
|
|
576
577
|
return {
|
|
577
578
|
invitations: unique(value.invitations.map(normalizeReceivedInvitation), (item) => item.id),
|
|
@@ -647,7 +648,7 @@ function fromOrganizationInvitationAdministrationError(value, status, retryAfter
|
|
|
647
648
|
return fromOrganizationMutationAuthorityError(value, status, retryAfter);
|
|
648
649
|
}
|
|
649
650
|
function normalizeOrganizationMemberAuthority(value, organizationId, memberId) {
|
|
650
|
-
exact(value, [
|
|
651
|
+
exact$1(value, [
|
|
651
652
|
"organizationId",
|
|
652
653
|
"actor",
|
|
653
654
|
"target"
|
|
@@ -665,7 +666,7 @@ function normalizeOrganizationMemberAuthority(value, organizationId, memberId) {
|
|
|
665
666
|
};
|
|
666
667
|
}
|
|
667
668
|
function normalizeOrganizationAuthorityMemberOutcome(value, includesStatus = false) {
|
|
668
|
-
exact(value, [
|
|
669
|
+
exact$1(value, [
|
|
669
670
|
"memberId",
|
|
670
671
|
"manualRole",
|
|
671
672
|
"role",
|
|
@@ -758,7 +759,7 @@ function toDate(value) {
|
|
|
758
759
|
function isId(value) {
|
|
759
760
|
return typeof value === "string" && id.test(value);
|
|
760
761
|
}
|
|
761
|
-
function exact(value, keys) {
|
|
762
|
+
function exact$1(value, keys) {
|
|
762
763
|
if (!isObject(value) || Object.keys(value).sort().join(",") !== [...keys].sort().join(",")) throw authorityUnavailable();
|
|
763
764
|
}
|
|
764
765
|
function boundedString(value, maximum) {
|
|
@@ -802,6 +803,159 @@ function isObject(value) {
|
|
|
802
803
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
803
804
|
}
|
|
804
805
|
//#endregion
|
|
806
|
+
//#region src/drive-recipients.ts
|
|
807
|
+
const encoder = new TextEncoder();
|
|
808
|
+
const emailPattern = /^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9-]*\.)+[A-Za-z]{2,}$/;
|
|
809
|
+
function validEmail(value) {
|
|
810
|
+
return typeof value === "string" && encoder.encode(value).byteLength <= 320 && emailPattern.test(value);
|
|
811
|
+
}
|
|
812
|
+
function exact(value, keys) {
|
|
813
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
814
|
+
}
|
|
815
|
+
const failures = {
|
|
816
|
+
400: [
|
|
817
|
+
"BAD_REQUEST",
|
|
818
|
+
"The recipient request is invalid.",
|
|
819
|
+
"invalid_request"
|
|
820
|
+
],
|
|
821
|
+
401: [
|
|
822
|
+
"UNAUTHORIZED",
|
|
823
|
+
"The recipient service assertion is invalid.",
|
|
824
|
+
"authentication_required"
|
|
825
|
+
],
|
|
826
|
+
404: [
|
|
827
|
+
"NOT_FOUND",
|
|
828
|
+
"The recipient is unavailable.",
|
|
829
|
+
"recipient_unavailable"
|
|
830
|
+
],
|
|
831
|
+
405: [
|
|
832
|
+
"METHOD_NOT_ALLOWED",
|
|
833
|
+
"METHOD_NOT_ALLOWED",
|
|
834
|
+
"invalid_request"
|
|
835
|
+
],
|
|
836
|
+
413: [
|
|
837
|
+
"REQUEST_TOO_LONG",
|
|
838
|
+
"The recipient request is too large.",
|
|
839
|
+
"invalid_request"
|
|
840
|
+
],
|
|
841
|
+
429: [
|
|
842
|
+
"TOO_MANY_REQUESTS",
|
|
843
|
+
"Recipient requests are temporarily limited.",
|
|
844
|
+
"auth_request_rate_limited"
|
|
845
|
+
],
|
|
846
|
+
503: [
|
|
847
|
+
"SERVICE_UNAVAILABLE",
|
|
848
|
+
"Recipient resolution is temporarily unavailable.",
|
|
849
|
+
"identity_authority_unavailable"
|
|
850
|
+
]
|
|
851
|
+
};
|
|
852
|
+
function createDriveRecipientAuth(options) {
|
|
853
|
+
const url = joinURL(normalizeOrigin(options.authOrigin, "authOrigin"), "api/v1/auth/drive-share-recipients/resolve");
|
|
854
|
+
const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
855
|
+
if (typeof fetchImpl !== "function") throw new TypeError("fetch is required");
|
|
856
|
+
const timeoutMs = normalizeTimeout(options.timeoutMs);
|
|
857
|
+
let inFlight = false;
|
|
858
|
+
return { async resolveRecipient(input, clientAssertion) {
|
|
859
|
+
let body;
|
|
860
|
+
let email;
|
|
861
|
+
try {
|
|
862
|
+
if (!exact(input, ["email"]) || typeof input.email !== "string") throw new Error();
|
|
863
|
+
email = input.email.trim().toLowerCase();
|
|
864
|
+
if (!validEmail(email)) throw new Error();
|
|
865
|
+
body = JSON.stringify({ email });
|
|
866
|
+
} catch {
|
|
867
|
+
throw new AuthError("invalid_request", 400);
|
|
868
|
+
}
|
|
869
|
+
if (encoder.encode(body).byteLength > 1024) throw new AuthError("invalid_request", 413);
|
|
870
|
+
if (typeof clientAssertion !== "string" || clientAssertion.length + 7 > 16 * 1024 || !/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$(?![\s\S])/.test(clientAssertion)) throw new AuthError("invalid_request", 400);
|
|
871
|
+
if (inFlight) throw new AuthError("state_conflict", 409);
|
|
872
|
+
inFlight = true;
|
|
873
|
+
const controller = new AbortController();
|
|
874
|
+
let reader;
|
|
875
|
+
let timer;
|
|
876
|
+
let authorityError;
|
|
877
|
+
try {
|
|
878
|
+
const deadline = new Promise((_, reject) => {
|
|
879
|
+
timer = setTimeout(() => {
|
|
880
|
+
reject(authorityUnavailable());
|
|
881
|
+
controller.abort();
|
|
882
|
+
reader?.cancel().catch(() => {});
|
|
883
|
+
}, timeoutMs);
|
|
884
|
+
});
|
|
885
|
+
const exchange = async () => {
|
|
886
|
+
const response = await fetchImpl(url, {
|
|
887
|
+
method: "POST",
|
|
888
|
+
headers: {
|
|
889
|
+
authorization: `Bearer ${clientAssertion}`,
|
|
890
|
+
accept: "application/json",
|
|
891
|
+
"content-type": "application/json"
|
|
892
|
+
},
|
|
893
|
+
body,
|
|
894
|
+
credentials: "omit",
|
|
895
|
+
cache: "no-store",
|
|
896
|
+
redirect: "manual",
|
|
897
|
+
signal: controller.signal
|
|
898
|
+
});
|
|
899
|
+
if (controller.signal.aborted) {
|
|
900
|
+
response.body?.cancel().catch(() => {});
|
|
901
|
+
throw authorityUnavailable();
|
|
902
|
+
}
|
|
903
|
+
if (!response.body) throw authorityUnavailable();
|
|
904
|
+
reader = response.body.getReader();
|
|
905
|
+
const contentType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
|
|
906
|
+
const length = response.headers.get("content-length");
|
|
907
|
+
if (response.redirected || contentType !== "application/json" || length !== null && (!/^\d+$/.test(length) || Number(length) > 4096)) throw authorityUnavailable();
|
|
908
|
+
const bytes = new Uint8Array(4096);
|
|
909
|
+
let size = 0;
|
|
910
|
+
while (true) {
|
|
911
|
+
const chunk = await reader.read();
|
|
912
|
+
if (controller.signal.aborted) throw authorityUnavailable();
|
|
913
|
+
if (chunk.done) break;
|
|
914
|
+
if (size + chunk.value.byteLength > bytes.byteLength) throw authorityUnavailable();
|
|
915
|
+
bytes.set(chunk.value, size);
|
|
916
|
+
size += chunk.value.byteLength;
|
|
917
|
+
}
|
|
918
|
+
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size)));
|
|
919
|
+
if (response.status !== 200) {
|
|
920
|
+
const status = response.status;
|
|
921
|
+
const failure = failures[status];
|
|
922
|
+
if (!failure || !exact(value, ["code", "message"]) || value.code !== failure[0] || value.message !== failure[1]) throw authorityUnavailable();
|
|
923
|
+
let retryAfterSeconds;
|
|
924
|
+
if (status === 429) {
|
|
925
|
+
const retryAfter = response.headers.get("retry-after");
|
|
926
|
+
if (retryAfter === null || !/^(?:[1-9]|[1-5][0-9]|60)$/.test(retryAfter)) throw authorityUnavailable();
|
|
927
|
+
retryAfterSeconds = Number(retryAfter);
|
|
928
|
+
}
|
|
929
|
+
authorityError = new AuthError(failure[2], status, retryAfterSeconds);
|
|
930
|
+
throw authorityError;
|
|
931
|
+
}
|
|
932
|
+
if (!exact(value, ["recipient"]) || !exact(value.recipient, [
|
|
933
|
+
"userId",
|
|
934
|
+
"name",
|
|
935
|
+
"email"
|
|
936
|
+
])) throw authorityUnavailable();
|
|
937
|
+
const recipient = value.recipient;
|
|
938
|
+
if (typeof recipient.userId !== "string" || !/^[A-Za-z0-9_-]{1,128}$(?![\s\S])/.test(recipient.userId) || typeof recipient.name !== "string" || recipient.name.length === 0 || encoder.encode(recipient.name).byteLength > 1024 || !validEmail(recipient.email) || recipient.email !== recipient.email.trim().toLowerCase() || recipient.email !== email) throw authorityUnavailable();
|
|
939
|
+
return {
|
|
940
|
+
userId: recipient.userId,
|
|
941
|
+
name: recipient.name,
|
|
942
|
+
email: recipient.email
|
|
943
|
+
};
|
|
944
|
+
};
|
|
945
|
+
return await Promise.race([exchange().finally(() => {
|
|
946
|
+
inFlight = false;
|
|
947
|
+
}), deadline]);
|
|
948
|
+
} catch (error) {
|
|
949
|
+
if (authorityError && error === authorityError) throw authorityError;
|
|
950
|
+
throw authorityUnavailable();
|
|
951
|
+
} finally {
|
|
952
|
+
clearTimeout(timer);
|
|
953
|
+
controller.abort();
|
|
954
|
+
reader?.cancel().catch(() => {});
|
|
955
|
+
}
|
|
956
|
+
} };
|
|
957
|
+
}
|
|
958
|
+
//#endregion
|
|
805
959
|
//#region src/provisioning.ts
|
|
806
960
|
const MAX_BYTES = 16 * 1024;
|
|
807
961
|
const compactJwt = /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/;
|
|
@@ -1539,4 +1693,4 @@ function authAuthorizationHeader(value) {
|
|
|
1539
1693
|
return value;
|
|
1540
1694
|
}
|
|
1541
1695
|
//#endregion
|
|
1542
|
-
export { AuthError, createDriveProvisioningAuth, createServerAuth };
|
|
1696
|
+
export { AuthError, createDriveProvisioningAuth, createDriveRecipientAuth, createServerAuth };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region src/errors.d.ts
|
|
2
|
-
type AuthErrorCode = "invalid_credentials" | "invalid_auth_input" | "account_conflict" | "password_reset_invalid" | "authentication_required" | "request_origin_denied" | "invalid_request" | "invalid_invitation" | "invitation_unavailable" | "invitation_recipient_mismatch" | "invitation_recovery_required" | "email_verification_required" | "invalid_workspace_selection" | "organization_context_too_large" | "credential_rate_limited" | "auth_request_rate_limited" | "state_conflict" | "access_denied" | "platform_target_unavailable" | "platform_quorum_unavailable" | "platform_recovery_required" | "identity_authority_unavailable";
|
|
3
|
-
type AuthErrorStatus = 400 | 401 | 403 | 404 | 409 | 413 | 415 | 422 | 429 | 503;
|
|
2
|
+
type AuthErrorCode = "invalid_credentials" | "invalid_auth_input" | "account_conflict" | "password_reset_invalid" | "authentication_required" | "request_origin_denied" | "invalid_request" | "invalid_invitation" | "invitation_unavailable" | "recipient_unavailable" | "invitation_recipient_mismatch" | "invitation_recovery_required" | "email_verification_required" | "invalid_workspace_selection" | "organization_context_too_large" | "credential_rate_limited" | "auth_request_rate_limited" | "state_conflict" | "access_denied" | "platform_target_unavailable" | "platform_quorum_unavailable" | "platform_recovery_required" | "identity_authority_unavailable";
|
|
3
|
+
type AuthErrorStatus = 400 | 401 | 403 | 404 | 405 | 409 | 413 | 415 | 422 | 429 | 503;
|
|
4
4
|
declare class AuthError extends Error {
|
|
5
5
|
readonly code: AuthErrorCode;
|
|
6
6
|
readonly status: AuthErrorStatus;
|