@finueva/auth 0.15.0 → 0.16.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 +16 -0
- package/dist/index.js +30 -7
- package/dist/server.d.ts +19 -1
- package/dist/server.js +114 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,22 @@ Do not reuse configuration from another application or environment.
|
|
|
32
32
|
| Server or SSR handler | `@finueva/auth/server` | Incoming Auth cookie or bearer | Once per request |
|
|
33
33
|
| CLI or native application | `@finueva/auth/device` | Device bearer session | Once per process |
|
|
34
34
|
|
|
35
|
+
## Private Identity Resolution Candidate
|
|
36
|
+
|
|
37
|
+
For private server-to-server identity resolution, the next package candidate exports `createIdentityResolverAuth` from `@finueva/auth/server`. This helper is not in published `0.15.0`.
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { createIdentityResolverAuth } from "@finueva/auth/server";
|
|
41
|
+
|
|
42
|
+
const identities = createIdentityResolverAuth({ authOrigin: "https://auth.example.test" });
|
|
43
|
+
|
|
44
|
+
async function resolvePrivateIdentity(identifier: string, clientAssertion: string) {
|
|
45
|
+
return identities.resolveIdentity({ identifier }, clientAssertion);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The approved server signs a fresh assertion with its registered key and explicit `identities:resolve` capability. The method returns only `{ userId }` or null for one exact username or email. Keep the result private. It grants no product authority. See the [server guide](https://github.com/Ifkafin/auth/blob/main/apps/developer/docs/sdk/server.md#resolve-a-private-identity) for the assertion contract and limits.
|
|
50
|
+
|
|
35
51
|
## Browser Quick Start
|
|
36
52
|
|
|
37
53
|
Create the client with the exact Auth and application origins:
|
package/dist/index.js
CHANGED
|
@@ -4472,7 +4472,7 @@ function createAuthClient(options) {
|
|
|
4472
4472
|
baseURL: authOrigin,
|
|
4473
4473
|
basePath: "/api/v1/auth",
|
|
4474
4474
|
fetchOptions: {
|
|
4475
|
-
customFetchImpl:
|
|
4475
|
+
customFetchImpl: fetchBetterAuth,
|
|
4476
4476
|
credentials: "include",
|
|
4477
4477
|
retry: 0,
|
|
4478
4478
|
timeout: timeoutMs
|
|
@@ -4508,6 +4508,15 @@ function createAuthClient(options) {
|
|
|
4508
4508
|
let signalRefreshPending = false;
|
|
4509
4509
|
let signalRefreshScheduled = false;
|
|
4510
4510
|
let signalRefreshRequest;
|
|
4511
|
+
let betterAuthTransportSequence = 0;
|
|
4512
|
+
async function fetchBetterAuth(input, init) {
|
|
4513
|
+
const request = new Request(input, init);
|
|
4514
|
+
const url = new URL(request.url);
|
|
4515
|
+
if (!authStateStarted || request.method !== "GET" || url.origin !== authOrigin || url.pathname !== "/api/v1/auth/get-session" || signOutRequest || activeAccountMutationGeneration !== void 0) return fetchImpl(input, init);
|
|
4516
|
+
betterAuthTransportSequence += 1;
|
|
4517
|
+
const response = await readAuthoritySession(authorityGeneration, request);
|
|
4518
|
+
return new Response(JSON.stringify(response.value), { headers: { "content-type": "application/json" } });
|
|
4519
|
+
}
|
|
4511
4520
|
function advanceAuthorityGeneration() {
|
|
4512
4521
|
recentAuthenticationStartAttempt = void 0;
|
|
4513
4522
|
return ++authorityGeneration;
|
|
@@ -4569,12 +4578,13 @@ function createAuthClient(options) {
|
|
|
4569
4578
|
authoritative: false
|
|
4570
4579
|
});
|
|
4571
4580
|
});
|
|
4572
|
-
getSession().catch(() => void 0);
|
|
4581
|
+
if (typeof window === "undefined") getSession().catch(() => void 0);
|
|
4573
4582
|
}
|
|
4574
4583
|
function subscribeToSessionSignals() {
|
|
4575
4584
|
if (sessionSignalSubscribed) return;
|
|
4576
4585
|
sessionSignalSubscribed = true;
|
|
4577
4586
|
let initialized = false;
|
|
4587
|
+
let transportSequence = betterAuthTransportSequence;
|
|
4578
4588
|
betterAuth.useSession.subscribe((state) => {
|
|
4579
4589
|
const refetching = state.isRefetching === true;
|
|
4580
4590
|
if (!initialized) {
|
|
@@ -4583,6 +4593,7 @@ function createAuthClient(options) {
|
|
|
4583
4593
|
return;
|
|
4584
4594
|
}
|
|
4585
4595
|
const settled = betterAuthRefetching && !refetching;
|
|
4596
|
+
if (refetching && !betterAuthRefetching) transportSequence = betterAuthTransportSequence;
|
|
4586
4597
|
betterAuthRefetching = refetching;
|
|
4587
4598
|
if (refetching && signOutRequest) suppressBetterAuthSettlement = true;
|
|
4588
4599
|
if (!settled) return;
|
|
@@ -4590,6 +4601,7 @@ function createAuthClient(options) {
|
|
|
4590
4601
|
suppressBetterAuthSettlement = false;
|
|
4591
4602
|
return;
|
|
4592
4603
|
}
|
|
4604
|
+
if (transportSequence !== betterAuthTransportSequence) return;
|
|
4593
4605
|
queueSignalRefresh();
|
|
4594
4606
|
});
|
|
4595
4607
|
}
|
|
@@ -4626,24 +4638,35 @@ function createAuthClient(options) {
|
|
|
4626
4638
|
sessionUserId = session?.user.id;
|
|
4627
4639
|
return session;
|
|
4628
4640
|
}
|
|
4629
|
-
async function
|
|
4641
|
+
async function readAuthoritySession(generation, request) {
|
|
4630
4642
|
const readEpoch = ++sessionReadEpoch;
|
|
4631
4643
|
try {
|
|
4632
|
-
const response = await requestJsonResponse(joinURL(authOrigin, "api/v1/auth/get-session"), {
|
|
4644
|
+
const response = await requestJsonResponse(request?.url ?? joinURL(authOrigin, "api/v1/auth/get-session"), {
|
|
4633
4645
|
fetch: fetchImpl,
|
|
4634
4646
|
timeoutMs,
|
|
4635
|
-
method: "GET"
|
|
4647
|
+
method: "GET",
|
|
4648
|
+
...request ? {
|
|
4649
|
+
headers: request.headers,
|
|
4650
|
+
signal: request.signal
|
|
4651
|
+
} : {}
|
|
4636
4652
|
});
|
|
4637
4653
|
if (generation !== authorityGeneration || readEpoch !== sessionReadEpoch) throw new AuthError("state_conflict", 409);
|
|
4638
|
-
return
|
|
4654
|
+
return {
|
|
4655
|
+
...response,
|
|
4656
|
+
session: acceptAuthoritySession(normalizeSession(response.value), generation)
|
|
4657
|
+
};
|
|
4639
4658
|
} catch (error) {
|
|
4640
4659
|
if (generation !== authorityGeneration || readEpoch !== sessionReadEpoch) throw new AuthError("state_conflict", 409);
|
|
4641
4660
|
if (error instanceof AuthError && error.code === "state_conflict") throw error;
|
|
4642
|
-
|
|
4661
|
+
if (error instanceof AuthError && error.status === 401) acceptAuthoritySession(null, generation);
|
|
4662
|
+
else unavailable();
|
|
4643
4663
|
if (error instanceof AuthError) throw error;
|
|
4644
4664
|
throw authorityUnavailable();
|
|
4645
4665
|
}
|
|
4646
4666
|
}
|
|
4667
|
+
async function refreshAuthoritySession(generation) {
|
|
4668
|
+
return (await readAuthoritySession(generation)).session;
|
|
4669
|
+
}
|
|
4647
4670
|
function getSession() {
|
|
4648
4671
|
if (signOutRequest || activeAccountMutationGeneration !== void 0) return Promise.reject(new AuthError("state_conflict", 409));
|
|
4649
4672
|
return refreshAuthoritySession(authorityGeneration);
|
package/dist/server.d.ts
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
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/identity-resolution.d.ts
|
|
3
|
+
interface IdentityResolverAuthOptions {
|
|
4
|
+
authOrigin: string;
|
|
5
|
+
fetch?: typeof globalThis.fetch;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
interface ResolveIdentityInput {
|
|
9
|
+
identifier: string;
|
|
10
|
+
}
|
|
11
|
+
interface ResolvedIdentity {
|
|
12
|
+
userId: string;
|
|
13
|
+
}
|
|
14
|
+
interface FinuevaIdentityResolverAuth {
|
|
15
|
+
resolveIdentity(input: ResolveIdentityInput, clientAssertion: string): Promise<ResolvedIdentity | null>;
|
|
16
|
+
}
|
|
17
|
+
/** Server-only identity resolution. Keep results private; they grant no product authority. */
|
|
18
|
+
declare function createIdentityResolverAuth(options: IdentityResolverAuthOptions): FinuevaIdentityResolverAuth;
|
|
19
|
+
//#endregion
|
|
2
20
|
//#region src/drive-recipients.d.ts
|
|
3
21
|
interface DriveRecipientAuthOptions {
|
|
4
22
|
authOrigin: string;
|
|
@@ -80,4 +98,4 @@ interface FinuevaServerAuth {
|
|
|
80
98
|
}
|
|
81
99
|
declare function createServerAuth(options: ServerAuthOptions): FinuevaServerAuth;
|
|
82
100
|
//#endregion
|
|
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 };
|
|
101
|
+
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, type FinuevaIdentityResolverAuth, FinuevaServerAuth, type IdentityResolverAuthOptions, 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, type ResolveIdentityInput, type ResolvedIdentity, ServerAuthOptions, ServerInvitationAcceptance, ServerSignOutResult, ServerSocialIdTokenSession, type SocialIdTokenInput, type SocialIdTokenProvider, type UpdateOrganizationDisplayInput, type UpdateOrganizationTeamDisplayInput, type WorkspaceSelection, createDriveProvisioningAuth, createDriveRecipientAuth, createIdentityResolverAuth, createServerAuth };
|
package/dist/server.js
CHANGED
|
@@ -307,7 +307,7 @@ function normalizeIdInput(value) {
|
|
|
307
307
|
return value;
|
|
308
308
|
}
|
|
309
309
|
function normalizeOrganization(value) {
|
|
310
|
-
exact$
|
|
310
|
+
exact$2(value, [
|
|
311
311
|
"capabilities",
|
|
312
312
|
"id",
|
|
313
313
|
"lifecycle",
|
|
@@ -317,7 +317,7 @@ function normalizeOrganization(value) {
|
|
|
317
317
|
"slug"
|
|
318
318
|
]);
|
|
319
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();
|
|
320
|
-
exact$
|
|
320
|
+
exact$2(value.capabilities, [
|
|
321
321
|
"readInvitations",
|
|
322
322
|
"readMembers",
|
|
323
323
|
"readTeams"
|
|
@@ -378,7 +378,7 @@ function normalizeOrganizationTeamCreationInput(name, idempotencyKey) {
|
|
|
378
378
|
};
|
|
379
379
|
}
|
|
380
380
|
function normalizeCreatedOrganizationTeam(value) {
|
|
381
|
-
exact$
|
|
381
|
+
exact$2(value, ["id", "name"]);
|
|
382
382
|
if (!isId(value.id) || !boundedString(value.name, 255)) throw authorityUnavailable();
|
|
383
383
|
return {
|
|
384
384
|
id: value.id,
|
|
@@ -405,7 +405,7 @@ function normalizeOrganizationMemberInvitationCreationInput(value) {
|
|
|
405
405
|
};
|
|
406
406
|
}
|
|
407
407
|
function normalizeCreatedOrganizationMemberInvitation(value) {
|
|
408
|
-
exact$
|
|
408
|
+
exact$2(value, ["expiresAt", "id"]);
|
|
409
409
|
if (!isId(value.id)) throw authorityUnavailable();
|
|
410
410
|
return {
|
|
411
411
|
id: value.id,
|
|
@@ -422,12 +422,12 @@ function normalizeOrganizationInvitationAdministrationInput(idempotencyKey) {
|
|
|
422
422
|
return { idempotencyKey };
|
|
423
423
|
}
|
|
424
424
|
function normalizeCanceledOrganizationInvitation(value) {
|
|
425
|
-
exact$
|
|
425
|
+
exact$2(value, ["status"]);
|
|
426
426
|
if (value.status !== "canceled") throw authorityUnavailable();
|
|
427
427
|
return { status: "canceled" };
|
|
428
428
|
}
|
|
429
429
|
function normalizeOrganizations(value) {
|
|
430
|
-
exact$
|
|
430
|
+
exact$2(value, ["nextCursor", "organizations"]);
|
|
431
431
|
if (!Array.isArray(value.organizations) || value.organizations.length > 100) throw authorityUnavailable();
|
|
432
432
|
return {
|
|
433
433
|
organizations: unique(value.organizations.map(normalizeOrganization), (item) => item.id),
|
|
@@ -435,11 +435,11 @@ function normalizeOrganizations(value) {
|
|
|
435
435
|
};
|
|
436
436
|
}
|
|
437
437
|
function normalizeOrganizationMembers(value) {
|
|
438
|
-
exact$
|
|
438
|
+
exact$2(value, ["members", "nextCursor"]);
|
|
439
439
|
if (!Array.isArray(value.members) || value.members.length > 100) throw authorityUnavailable();
|
|
440
440
|
return {
|
|
441
441
|
members: unique(value.members.map((member) => {
|
|
442
|
-
exact$
|
|
442
|
+
exact$2(member, [
|
|
443
443
|
"email",
|
|
444
444
|
"id",
|
|
445
445
|
"image",
|
|
@@ -468,11 +468,11 @@ function normalizeOrganizationMembers(value) {
|
|
|
468
468
|
};
|
|
469
469
|
}
|
|
470
470
|
function normalizeOrganizationTeams(value) {
|
|
471
|
-
exact$
|
|
471
|
+
exact$2(value, ["nextCursor", "teams"]);
|
|
472
472
|
if (!Array.isArray(value.teams) || value.teams.length > 100) throw authorityUnavailable();
|
|
473
473
|
return {
|
|
474
474
|
teams: unique(value.teams.map((team) => {
|
|
475
|
-
exact$
|
|
475
|
+
exact$2(team, [
|
|
476
476
|
"category",
|
|
477
477
|
"createdAt",
|
|
478
478
|
"id",
|
|
@@ -490,7 +490,7 @@ function normalizeOrganizationTeams(value) {
|
|
|
490
490
|
};
|
|
491
491
|
}
|
|
492
492
|
function normalizeOrganizationTeamMembers(value) {
|
|
493
|
-
exact$
|
|
493
|
+
exact$2(value, ["memberIds", "nextCursor"]);
|
|
494
494
|
if (!Array.isArray(value.memberIds) || value.memberIds.length > 100) throw authorityUnavailable();
|
|
495
495
|
return {
|
|
496
496
|
memberIds: unique(value.memberIds.map((item) => normalizeResponseId(item)), (item) => item),
|
|
@@ -498,7 +498,7 @@ function normalizeOrganizationTeamMembers(value) {
|
|
|
498
498
|
};
|
|
499
499
|
}
|
|
500
500
|
function normalizeOrganizationInvitation(value) {
|
|
501
|
-
exact$
|
|
501
|
+
exact$2(value, [
|
|
502
502
|
"createdAt",
|
|
503
503
|
"email",
|
|
504
504
|
"expiresAt",
|
|
@@ -509,7 +509,7 @@ function normalizeOrganizationInvitation(value) {
|
|
|
509
509
|
"teamIds"
|
|
510
510
|
]);
|
|
511
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();
|
|
512
|
-
exact$
|
|
512
|
+
exact$2(value.inviter, ["membershipId", "name"]);
|
|
513
513
|
if (!isId(value.inviter.membershipId) || !boundedString(value.inviter.name, 255)) throw authorityUnavailable();
|
|
514
514
|
return {
|
|
515
515
|
id: value.id,
|
|
@@ -526,7 +526,7 @@ function normalizeOrganizationInvitation(value) {
|
|
|
526
526
|
};
|
|
527
527
|
}
|
|
528
528
|
function normalizeOrganizationInvitations(value) {
|
|
529
|
-
exact$
|
|
529
|
+
exact$2(value, ["invitations", "nextCursor"]);
|
|
530
530
|
if (!Array.isArray(value.invitations) || value.invitations.length > 100) throw authorityUnavailable();
|
|
531
531
|
return {
|
|
532
532
|
invitations: unique(value.invitations.map(normalizeOrganizationInvitation), (item) => item.id),
|
|
@@ -534,7 +534,7 @@ function normalizeOrganizationInvitations(value) {
|
|
|
534
534
|
};
|
|
535
535
|
}
|
|
536
536
|
function normalizeReceivedInvitation(value) {
|
|
537
|
-
exact$
|
|
537
|
+
exact$2(value, [
|
|
538
538
|
"expiresAt",
|
|
539
539
|
"id",
|
|
540
540
|
"inviterName",
|
|
@@ -543,7 +543,7 @@ function normalizeReceivedInvitation(value) {
|
|
|
543
543
|
"teams"
|
|
544
544
|
]);
|
|
545
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();
|
|
546
|
-
exact$
|
|
546
|
+
exact$2(value.organization, [
|
|
547
547
|
"id",
|
|
548
548
|
"logo",
|
|
549
549
|
"name",
|
|
@@ -560,7 +560,7 @@ function normalizeReceivedInvitation(value) {
|
|
|
560
560
|
},
|
|
561
561
|
role: value.role,
|
|
562
562
|
teams: unique(value.teams.map((team) => {
|
|
563
|
-
exact$
|
|
563
|
+
exact$2(team, ["id", "name"]);
|
|
564
564
|
if (!isId(team.id) || !boundedString(team.name, 255)) throw authorityUnavailable();
|
|
565
565
|
return {
|
|
566
566
|
id: team.id,
|
|
@@ -572,7 +572,7 @@ function normalizeReceivedInvitation(value) {
|
|
|
572
572
|
};
|
|
573
573
|
}
|
|
574
574
|
function normalizeReceivedInvitations(value) {
|
|
575
|
-
exact$
|
|
575
|
+
exact$2(value, ["invitations", "nextCursor"]);
|
|
576
576
|
if (!Array.isArray(value.invitations) || value.invitations.length > 100) throw authorityUnavailable();
|
|
577
577
|
return {
|
|
578
578
|
invitations: unique(value.invitations.map(normalizeReceivedInvitation), (item) => item.id),
|
|
@@ -648,7 +648,7 @@ function fromOrganizationInvitationAdministrationError(value, status, retryAfter
|
|
|
648
648
|
return fromOrganizationMutationAuthorityError(value, status, retryAfter);
|
|
649
649
|
}
|
|
650
650
|
function normalizeOrganizationMemberAuthority(value, organizationId, memberId) {
|
|
651
|
-
exact$
|
|
651
|
+
exact$2(value, [
|
|
652
652
|
"organizationId",
|
|
653
653
|
"actor",
|
|
654
654
|
"target"
|
|
@@ -666,7 +666,7 @@ function normalizeOrganizationMemberAuthority(value, organizationId, memberId) {
|
|
|
666
666
|
};
|
|
667
667
|
}
|
|
668
668
|
function normalizeOrganizationAuthorityMemberOutcome(value, includesStatus = false) {
|
|
669
|
-
exact$
|
|
669
|
+
exact$2(value, [
|
|
670
670
|
"memberId",
|
|
671
671
|
"manualRole",
|
|
672
672
|
"role",
|
|
@@ -759,7 +759,7 @@ function toDate(value) {
|
|
|
759
759
|
function isId(value) {
|
|
760
760
|
return typeof value === "string" && id.test(value);
|
|
761
761
|
}
|
|
762
|
-
function exact$
|
|
762
|
+
function exact$2(value, keys) {
|
|
763
763
|
if (!isObject(value) || Object.keys(value).sort().join(",") !== [...keys].sort().join(",")) throw authorityUnavailable();
|
|
764
764
|
}
|
|
765
765
|
function boundedString(value, maximum) {
|
|
@@ -803,6 +803,98 @@ function isObject(value) {
|
|
|
803
803
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
804
804
|
}
|
|
805
805
|
//#endregion
|
|
806
|
+
//#region src/identity-resolution.ts
|
|
807
|
+
const emailPattern$1 = /^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9-]*\.)+[A-Za-z]{2,}$(?![\s\S])/;
|
|
808
|
+
function exact$1(value, keys) {
|
|
809
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
|
|
810
|
+
}
|
|
811
|
+
function createIdentityResolverAuth(options) {
|
|
812
|
+
const url = joinURL(normalizeOrigin(options.authOrigin, "authOrigin"), "api/v1/auth/identities/resolve");
|
|
813
|
+
const fetchImpl = options.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
814
|
+
if (typeof fetchImpl !== "function") throw new TypeError("fetch is required");
|
|
815
|
+
const timeoutMs = normalizeTimeout(options.timeoutMs);
|
|
816
|
+
let inFlight = false;
|
|
817
|
+
return { async resolveIdentity(input, clientAssertion) {
|
|
818
|
+
if (!exact$1(input, ["identifier"]) || typeof input.identifier !== "string" || input.identifier.length > 324) throw new AuthError("invalid_request", 400);
|
|
819
|
+
const identifier = input.identifier.trim().toLowerCase();
|
|
820
|
+
if (!(identifier.length <= 320 && emailPattern$1.test(identifier)) && !/^[a-z0-9_.]{3,30}$(?![\s\S])/.test(identifier)) throw new AuthError("invalid_request", 400);
|
|
821
|
+
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);
|
|
822
|
+
if (inFlight) throw new AuthError("state_conflict", 409);
|
|
823
|
+
inFlight = true;
|
|
824
|
+
const controller = new AbortController();
|
|
825
|
+
let reader;
|
|
826
|
+
let timer;
|
|
827
|
+
let mappedError;
|
|
828
|
+
try {
|
|
829
|
+
const deadline = new Promise((_, reject) => {
|
|
830
|
+
timer = setTimeout(() => {
|
|
831
|
+
reject(authorityUnavailable());
|
|
832
|
+
controller.abort();
|
|
833
|
+
reader?.cancel().catch(() => {});
|
|
834
|
+
}, timeoutMs);
|
|
835
|
+
});
|
|
836
|
+
const exchange = async () => {
|
|
837
|
+
const response = await fetchImpl(url, {
|
|
838
|
+
method: "POST",
|
|
839
|
+
headers: {
|
|
840
|
+
authorization: `Bearer ${clientAssertion}`,
|
|
841
|
+
accept: "application/json",
|
|
842
|
+
"content-type": "application/json"
|
|
843
|
+
},
|
|
844
|
+
body: JSON.stringify({ identifier }),
|
|
845
|
+
credentials: "omit",
|
|
846
|
+
cache: "no-store",
|
|
847
|
+
redirect: "manual",
|
|
848
|
+
signal: controller.signal
|
|
849
|
+
});
|
|
850
|
+
if (controller.signal.aborted) {
|
|
851
|
+
response.body?.cancel().catch(() => {});
|
|
852
|
+
throw authorityUnavailable();
|
|
853
|
+
}
|
|
854
|
+
if (!response.body) throw authorityUnavailable();
|
|
855
|
+
reader = response.body.getReader();
|
|
856
|
+
const length = response.headers.get("content-length");
|
|
857
|
+
if (response.redirected || response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json" || length !== null && (!/^\d+$/.test(length) || Number(length) > 1024)) throw authorityUnavailable();
|
|
858
|
+
const bytes = new Uint8Array(1024);
|
|
859
|
+
let size = 0;
|
|
860
|
+
while (true) {
|
|
861
|
+
const chunk = await reader.read();
|
|
862
|
+
if (controller.signal.aborted) throw authorityUnavailable();
|
|
863
|
+
if (chunk.done) break;
|
|
864
|
+
if (size + chunk.value.byteLength > bytes.length) throw authorityUnavailable();
|
|
865
|
+
bytes.set(chunk.value, size);
|
|
866
|
+
size += chunk.value.byteLength;
|
|
867
|
+
}
|
|
868
|
+
const value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, size)));
|
|
869
|
+
if (response.status !== 200) {
|
|
870
|
+
if (!exact$1(value, ["code", "message"])) throw authorityUnavailable();
|
|
871
|
+
if (response.status === 401 && value.code === "UNAUTHORIZED" && value.message === "The identity resolution assertion is invalid.") mappedError = new AuthError("authentication_required", 401);
|
|
872
|
+
if (response.status === 429 && value.code === "TOO_MANY_REQUESTS" && value.message === "Identity resolution requests are temporarily limited.") {
|
|
873
|
+
const retryAfter = response.headers.get("retry-after");
|
|
874
|
+
if (retryAfter === null || !/^(?:[1-9]|[1-5][0-9]|60)$/.test(retryAfter)) throw authorityUnavailable();
|
|
875
|
+
mappedError = new AuthError("auth_request_rate_limited", 429, Number(retryAfter));
|
|
876
|
+
}
|
|
877
|
+
throw mappedError ?? authorityUnavailable();
|
|
878
|
+
}
|
|
879
|
+
if (!exact$1(value, ["identity"])) throw authorityUnavailable();
|
|
880
|
+
if (value.identity === null) return null;
|
|
881
|
+
if (!exact$1(value.identity, ["userId"]) || typeof value.identity.userId !== "string" || !/^[A-Za-z0-9_-]{1,128}$(?![\s\S])/.test(value.identity.userId)) throw authorityUnavailable();
|
|
882
|
+
return { userId: value.identity.userId };
|
|
883
|
+
};
|
|
884
|
+
return await Promise.race([exchange().finally(() => {
|
|
885
|
+
inFlight = false;
|
|
886
|
+
}), deadline]);
|
|
887
|
+
} catch (error) {
|
|
888
|
+
if (mappedError && mappedError === error) throw mappedError;
|
|
889
|
+
throw authorityUnavailable();
|
|
890
|
+
} finally {
|
|
891
|
+
clearTimeout(timer);
|
|
892
|
+
controller.abort();
|
|
893
|
+
reader?.cancel().catch(() => {});
|
|
894
|
+
}
|
|
895
|
+
} };
|
|
896
|
+
}
|
|
897
|
+
//#endregion
|
|
806
898
|
//#region src/drive-recipients.ts
|
|
807
899
|
const encoder = new TextEncoder();
|
|
808
900
|
const emailPattern = /^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9-]*\.)+[A-Za-z]{2,}$/;
|
|
@@ -1693,4 +1785,4 @@ function authAuthorizationHeader(value) {
|
|
|
1693
1785
|
return value;
|
|
1694
1786
|
}
|
|
1695
1787
|
//#endregion
|
|
1696
|
-
export { AuthError, createDriveProvisioningAuth, createDriveRecipientAuth, createServerAuth };
|
|
1788
|
+
export { AuthError, createDriveProvisioningAuth, createDriveRecipientAuth, createIdentityResolverAuth, createServerAuth };
|