@openlup/platform 0.15.0 → 0.16.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/CHANGELOG.md +40 -0
- package/README.md +350 -3
- package/dist/identity/adminAuthBinding.d.ts +28 -0
- package/dist/identity/adminAuthBinding.js +66 -0
- package/dist/identity/adminAuthorization.d.ts +34 -0
- package/dist/identity/adminAuthorization.js +72 -0
- package/dist/identity/adminContracts.d.ts +70 -0
- package/dist/identity/adminContracts.js +43 -0
- package/dist/identity/adminMagicLinkHandler.d.ts +39 -0
- package/dist/identity/adminMagicLinkHandler.js +94 -0
- package/dist/identity/adminMeHandler.d.ts +15 -0
- package/dist/identity/adminMeHandler.js +49 -0
- package/dist/identity/adminPreflight.d.ts +16 -0
- package/dist/identity/adminPreflight.js +38 -0
- package/dist/identity/adminUserActionPorts.d.ts +31 -0
- package/dist/identity/adminUserActionPorts.js +51 -0
- package/dist/identity/adminUserHandlers.d.ts +38 -0
- package/dist/identity/adminUserHandlers.js +140 -0
- package/dist/identity/authVerifyUrl.d.ts +6 -0
- package/dist/identity/authVerifyUrl.js +54 -0
- package/dist/identity/contracts/contracts.d.ts +8 -0
- package/dist/identity/contracts/contracts.js +2 -0
- package/dist/identity/contracts/emailTrust.d.ts +3 -0
- package/dist/identity/contracts/emailTrust.js +4 -0
- package/dist/identity/contracts/index.d.ts +8 -0
- package/dist/identity/contracts/index.js +8 -0
- package/dist/identity/contracts/oidc.d.ts +3 -0
- package/dist/identity/contracts/oidc.js +9 -0
- package/dist/identity/contracts/operatorAuth.d.ts +35 -0
- package/dist/identity/contracts/operatorAuth.js +53 -0
- package/dist/identity/contracts/operatorInvitations.d.ts +37 -0
- package/dist/identity/contracts/operatorInvitations.js +1 -0
- package/dist/identity/contracts/ports.d.ts +22 -0
- package/dist/identity/contracts/ports.js +1 -0
- package/dist/identity/contracts/privacy.d.ts +62 -0
- package/dist/identity/contracts/privacy.js +99 -0
- package/dist/identity/contracts/providerList.d.ts +3 -0
- package/dist/identity/contracts/providerList.js +11 -0
- package/dist/identity/contracts/types.d.ts +16 -0
- package/dist/identity/contracts/types.js +4 -0
- package/dist/identity/contribution.d.ts +8 -0
- package/dist/identity/contribution.js +22 -0
- package/dist/identity/customerAuth.d.ts +8 -0
- package/dist/identity/customerAuth.js +1 -0
- package/dist/identity/customerAuthorization.d.ts +5 -0
- package/dist/identity/customerAuthorization.js +22 -0
- package/dist/identity/customerContracts.d.ts +42 -0
- package/dist/identity/customerContracts.js +43 -0
- package/dist/identity/customerIdentityBinding.d.ts +44 -0
- package/dist/identity/customerIdentityBinding.js +60 -0
- package/dist/identity/customerIdentityVerifier.d.ts +8 -0
- package/dist/identity/customerIdentityVerifier.js +101 -0
- package/dist/identity/customerMagicLinkHandler.d.ts +38 -0
- package/dist/identity/customerMagicLinkHandler.js +115 -0
- package/dist/identity/customerMeHandler.d.ts +11 -0
- package/dist/identity/customerMeHandler.js +46 -0
- package/dist/identity/customerPorts.d.ts +4 -0
- package/dist/identity/customerPorts.js +1 -0
- package/dist/identity/customerSessionBinding.d.ts +38 -0
- package/dist/identity/customerSessionBinding.js +80 -0
- package/dist/identity/customerSessionIssuer.d.ts +10 -0
- package/dist/identity/customerSessionIssuer.js +57 -0
- package/dist/identity/customerVerifyOtpHandler.d.ts +8 -0
- package/dist/identity/customerVerifyOtpHandler.js +41 -0
- package/dist/identity/directOperatorInvitationStore.d.ts +19 -0
- package/dist/identity/directOperatorInvitationStore.js +193 -0
- package/dist/identity/emailTrustPolicy.d.ts +14 -0
- package/dist/identity/emailTrustPolicy.js +16 -0
- package/dist/identity/identityPersonalDataContributor.d.ts +7 -0
- package/dist/identity/identityPersonalDataContributor.js +46 -0
- package/dist/identity/identityReadiness.d.ts +10 -0
- package/dist/identity/identityReadiness.js +25 -0
- package/dist/identity/index.d.ts +67 -0
- package/dist/identity/index.js +66 -0
- package/dist/identity/inviteAdminUserUseCase.d.ts +89 -0
- package/dist/identity/inviteAdminUserUseCase.js +75 -0
- package/dist/identity/jwtLibrary.d.ts +54 -0
- package/dist/identity/jwtLibrary.js +1 -0
- package/dist/identity/machineActorWriteFence.d.ts +4 -0
- package/dist/identity/machineActorWriteFence.js +13 -0
- package/dist/identity/magicLinkPorts.d.ts +28 -0
- package/dist/identity/magicLinkPorts.js +1 -0
- package/dist/identity/managedAccountLinkStore.d.ts +2 -0
- package/dist/identity/managedAccountLinkStore.js +93 -0
- package/dist/identity/managedAdminAuthVerifier.d.ts +7 -0
- package/dist/identity/managedAdminAuthVerifier.js +27 -0
- package/dist/identity/managedAdminUserGateways.d.ts +123 -0
- package/dist/identity/managedAdminUserGateways.js +127 -0
- package/dist/identity/managedClient.d.ts +90 -0
- package/dist/identity/managedClient.js +10 -0
- package/dist/identity/managedClientAuthLink.d.ts +3 -0
- package/dist/identity/managedClientAuthLink.js +115 -0
- package/dist/identity/managedCustomerMe.d.ts +13 -0
- package/dist/identity/managedCustomerMe.js +30 -0
- package/dist/identity/managedEmailTrustPolicy.d.ts +11 -0
- package/dist/identity/managedEmailTrustPolicy.js +105 -0
- package/dist/identity/managedIdentityVerifier.d.ts +2 -0
- package/dist/identity/managedIdentityVerifier.js +16 -0
- package/dist/identity/managedPrivacy.d.ts +4 -0
- package/dist/identity/managedPrivacy.js +39 -0
- package/dist/identity/messages.d.ts +29 -0
- package/dist/identity/messages.js +39 -0
- package/dist/identity/oidcIdentityVerifier.d.ts +45 -0
- package/dist/identity/oidcIdentityVerifier.js +309 -0
- package/dist/identity/oidcPrincipal.d.ts +2 -0
- package/dist/identity/oidcPrincipal.js +10 -0
- package/dist/identity/operatorAdminAuth.d.ts +6 -0
- package/dist/identity/operatorAdminAuth.js +13 -0
- package/dist/identity/operatorAuthHandlers.d.ts +18 -0
- package/dist/identity/operatorAuthHandlers.js +71 -0
- package/dist/identity/operatorIdentityVerifier.d.ts +8 -0
- package/dist/identity/operatorIdentityVerifier.js +19 -0
- package/dist/identity/operatorInvitationLifecycle.d.ts +7 -0
- package/dist/identity/operatorInvitationLifecycle.js +15 -0
- package/dist/identity/operatorInvitationPorts.d.ts +31 -0
- package/dist/identity/operatorInvitationPorts.js +1 -0
- package/dist/identity/operatorPorts.d.ts +37 -0
- package/dist/identity/operatorPorts.js +1 -0
- package/dist/identity/operatorSession.d.ts +13 -0
- package/dist/identity/operatorSession.js +31 -0
- package/dist/identity/operatorTokenAdminAuth.d.ts +15 -0
- package/dist/identity/operatorTokenAdminAuth.js +50 -0
- package/dist/identity/personalDataRegistry.d.ts +4 -0
- package/dist/identity/personalDataRegistry.js +11 -0
- package/dist/identity/platformAdminAuth.d.ts +15 -0
- package/dist/identity/platformAdminAuth.js +1 -0
- package/dist/identity/ports.d.ts +127 -0
- package/dist/identity/ports.js +38 -0
- package/dist/identity/postgresAccountLinkStore.d.ts +7 -0
- package/dist/identity/postgresAccountLinkStore.js +52 -0
- package/dist/identity/postgresCustomerMe.d.ts +3 -0
- package/dist/identity/postgresCustomerMe.js +21 -0
- package/dist/identity/postgresCustomerRecovery.d.ts +11 -0
- package/dist/identity/postgresCustomerRecovery.js +57 -0
- package/dist/identity/postgresOperatorIdentity.d.ts +5 -0
- package/dist/identity/postgresOperatorIdentity.js +137 -0
- package/dist/identity/postgresPrivacy.d.ts +7 -0
- package/dist/identity/postgresPrivacy.js +126 -0
- package/dist/identity/privacyHandlers.d.ts +10 -0
- package/dist/identity/privacyHandlers.js +35 -0
- package/dist/identity/privacyPorts.d.ts +72 -0
- package/dist/identity/privacyPorts.js +8 -0
- package/dist/identity/privacyUseCases.d.ts +5 -0
- package/dist/identity/privacyUseCases.js +55 -0
- package/dist/identity/reconcileAccountForPrincipal.d.ts +20 -0
- package/dist/identity/reconcileAccountForPrincipal.js +66 -0
- package/dist/identity/reconcileAccountHandler.d.ts +11 -0
- package/dist/identity/reconcileAccountHandler.js +30 -0
- package/dist/identity/removeAdminUserUseCase.d.ts +31 -0
- package/dist/identity/removeAdminUserUseCase.js +60 -0
- package/dist/identity/response.d.ts +3 -0
- package/dist/identity/response.js +2 -0
- package/dist/identity/sql.d.ts +44 -0
- package/dist/identity/sql.js +10 -0
- package/package.json +12 -2
- package/release-gates.json +22 -0
- package/sql/20260810180001_self_host_actor_auth.sql +40 -0
- package/sql/20260810204057_customer_identity_read.sql +31 -0
- package/sql/20260814090000_customer_selfservice_recovery.sql +477 -0
- package/sql/20260901143000_admin_membership_authority_foundation.sql +234 -0
- package/sql/20261018120000_identity_operator_invitations.sql +42 -0
- package/sql/20261018180000_identity_operator_challenges.sql +56 -0
- package/sql/20261018190000_identity_privacy_erasures.sql +15 -0
- package/src/identity/adminAuthBinding.ts +76 -0
- package/src/identity/adminAuthorization.ts +106 -0
- package/src/identity/adminContracts.ts +52 -0
- package/src/identity/adminMagicLinkHandler.ts +132 -0
- package/src/identity/adminMeHandler.ts +64 -0
- package/src/identity/adminPreflight.ts +46 -0
- package/src/identity/adminUserActionPorts.ts +78 -0
- package/src/identity/adminUserHandlers.ts +174 -0
- package/src/identity/authVerifyUrl.ts +56 -0
- package/src/identity/contracts/contracts.ts +15 -0
- package/src/identity/contracts/emailTrust.ts +5 -0
- package/src/identity/contracts/index.ts +8 -0
- package/src/identity/contracts/oidc.ts +10 -0
- package/src/identity/contracts/operatorAuth.ts +61 -0
- package/src/identity/contracts/operatorInvitations.ts +32 -0
- package/src/identity/contracts/ports.ts +25 -0
- package/src/identity/contracts/privacy.ts +86 -0
- package/src/identity/contracts/providerList.ts +13 -0
- package/src/identity/contracts/types.ts +25 -0
- package/src/identity/contribution.ts +29 -0
- package/src/identity/customerAuth.ts +3 -0
- package/src/identity/customerAuthorization.ts +38 -0
- package/src/identity/customerContracts.ts +52 -0
- package/src/identity/customerIdentityBinding.ts +125 -0
- package/src/identity/customerIdentityVerifier.ts +141 -0
- package/src/identity/customerMagicLinkHandler.ts +171 -0
- package/src/identity/customerMeHandler.ts +64 -0
- package/src/identity/customerPorts.ts +2 -0
- package/src/identity/customerSessionBinding.ts +114 -0
- package/src/identity/customerSessionIssuer.ts +71 -0
- package/src/identity/customerVerifyOtpHandler.ts +44 -0
- package/src/identity/directOperatorInvitationStore.ts +178 -0
- package/src/identity/emailTrustPolicy.ts +16 -0
- package/src/identity/identityPersonalDataContributor.ts +48 -0
- package/src/identity/identityReadiness.ts +26 -0
- package/src/identity/index.ts +69 -0
- package/src/identity/inviteAdminUserUseCase.ts +134 -0
- package/src/identity/jwtLibrary.ts +24 -0
- package/src/identity/machineActorWriteFence.ts +19 -0
- package/src/identity/magicLinkPorts.ts +7 -0
- package/src/identity/managedAccountLinkStore.ts +105 -0
- package/src/identity/managedAdminAuthVerifier.ts +40 -0
- package/src/identity/managedAdminUserGateways.ts +164 -0
- package/src/identity/managedClient.ts +58 -0
- package/src/identity/managedClientAuthLink.ts +120 -0
- package/src/identity/managedCustomerMe.ts +52 -0
- package/src/identity/managedEmailTrustPolicy.ts +90 -0
- package/src/identity/managedIdentityVerifier.ts +20 -0
- package/src/identity/managedPrivacy.ts +37 -0
- package/src/identity/messages.ts +41 -0
- package/src/identity/oidcIdentityVerifier.ts +229 -0
- package/src/identity/oidcPrincipal.ts +10 -0
- package/src/identity/operatorAdminAuth.ts +14 -0
- package/src/identity/operatorAuthHandlers.ts +52 -0
- package/src/identity/operatorIdentityVerifier.ts +19 -0
- package/src/identity/operatorInvitationLifecycle.ts +14 -0
- package/src/identity/operatorInvitationPorts.ts +11 -0
- package/src/identity/operatorPorts.ts +11 -0
- package/src/identity/operatorSession.ts +30 -0
- package/src/identity/operatorTokenAdminAuth.ts +79 -0
- package/src/identity/personalDataRegistry.ts +8 -0
- package/src/identity/platformAdminAuth.ts +7 -0
- package/src/identity/ports.ts +158 -0
- package/src/identity/postgresAccountLinkStore.ts +49 -0
- package/src/identity/postgresCustomerMe.ts +33 -0
- package/src/identity/postgresCustomerRecovery.ts +72 -0
- package/src/identity/postgresOperatorIdentity.ts +123 -0
- package/src/identity/postgresPrivacy.ts +117 -0
- package/src/identity/privacyHandlers.ts +33 -0
- package/src/identity/privacyPorts.ts +52 -0
- package/src/identity/privacyUseCases.ts +48 -0
- package/src/identity/reconcileAccountForPrincipal.ts +99 -0
- package/src/identity/reconcileAccountHandler.ts +36 -0
- package/src/identity/removeAdminUserUseCase.ts +85 -0
- package/src/identity/response.ts +3 -0
- package/src/identity/sql.ts +10 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { assertOperatorInvitationTtl } from "./operatorInvitationLifecycle.js";
|
|
2
|
+
import { identityMessages } from "./messages.js";
|
|
3
|
+
export const ADMIN_ROLE_NOTIFICATION_LOCALE = "en";
|
|
4
|
+
export async function runInviteAdminUserUseCase(request, deps) {
|
|
5
|
+
const messages = deps.messages ?? identityMessages.en;
|
|
6
|
+
try {
|
|
7
|
+
if ("invitationStore" in deps)
|
|
8
|
+
assertOperatorInvitationTtl(deps.ttlSeconds);
|
|
9
|
+
if (!request.accessToken) {
|
|
10
|
+
return { status: 401, body: { code: "UNAUTHORIZED", error: messages.unauthorized } };
|
|
11
|
+
}
|
|
12
|
+
const { user, error: authError } = await deps.gateway.getUser(request.accessToken);
|
|
13
|
+
if (authError || !user) {
|
|
14
|
+
return { status: 401, body: { code: "INVALID_TOKEN", error: messages.invalidToken } };
|
|
15
|
+
}
|
|
16
|
+
const callerAdmin = await deps.gateway.findAdminUser(user.id);
|
|
17
|
+
if (!callerAdmin || callerAdmin.role !== "admin" || callerAdmin.membership_state !== "active" || callerAdmin.is_machine_actor !== false) {
|
|
18
|
+
return { status: 403, body: { code: "INVITE_FORBIDDEN", error: messages.inviteForbidden } };
|
|
19
|
+
}
|
|
20
|
+
if (!request.email) {
|
|
21
|
+
return { status: 400, body: { code: "EMAIL_REQUIRED", error: messages.emailRequired } };
|
|
22
|
+
}
|
|
23
|
+
if (!isInviteRole(request.role)) {
|
|
24
|
+
return { status: 400, body: { code: "INVALID_ROLE", error: messages.invalidRole } };
|
|
25
|
+
}
|
|
26
|
+
const role = request.role;
|
|
27
|
+
const email = request.email.trim().toLowerCase();
|
|
28
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))
|
|
29
|
+
return { status: 400, body: { code: "INVALID_EMAIL", error: "Invalid email" } };
|
|
30
|
+
const existingMembership = await deps.gateway.findAdminUserByEmail(email);
|
|
31
|
+
let principal = await deps.gateway.findAuthUserByEmail(email);
|
|
32
|
+
if (existingMembership && existingMembership.id !== principal?.id)
|
|
33
|
+
return { status: 409, body: { code: "IDENTITY_CONFLICT", error: "IDENTITY_CONFLICT" } };
|
|
34
|
+
if (!principal) {
|
|
35
|
+
const invite = await deps.gateway.inviteAuthUser({ email, role, redirectTo: new URL("/admin/auth/callback", deps.siteUrl).toString() });
|
|
36
|
+
if (invite.error || !invite.user) {
|
|
37
|
+
// A provider duplicate race reuses the durable identity. Never delete it.
|
|
38
|
+
principal = await deps.gateway.findAuthUserByEmail(email);
|
|
39
|
+
if (!principal)
|
|
40
|
+
throw new Error("operator_identity_creation_failed");
|
|
41
|
+
}
|
|
42
|
+
else
|
|
43
|
+
principal = invite.user;
|
|
44
|
+
}
|
|
45
|
+
const issued = "invitationStore" in deps
|
|
46
|
+
? await deps.invitationStore.issue({ actorPrincipalId: user.id, targetPrincipalId: principal.id, email, role, ttlSeconds: deps.ttlSeconds })
|
|
47
|
+
: await deps.activateMembership({ principalId: principal.id, email, role });
|
|
48
|
+
if (!("membershipState" in issued))
|
|
49
|
+
return { status: 409, body: { code: issued.code, error: issued.code } };
|
|
50
|
+
let delivery = {};
|
|
51
|
+
if (issued.code !== "ALREADY_INVITED" && issued.code !== "ALREADY_ACTIVE") {
|
|
52
|
+
try {
|
|
53
|
+
const notification = await deps.roleNotificationPort.sendRoleGranted({ email, authUserId: principal.id, role, locale: deps.notificationLocale ?? ADMIN_ROLE_NOTIFICATION_LOCALE, ...(issued.membershipState === "invited" ? { invitationId: issued.invitationId, expiresAt: issued.expiresAt } : {}) });
|
|
54
|
+
if (!notification.ok)
|
|
55
|
+
throw new Error("operator_invitation_notification_failed");
|
|
56
|
+
delivery = { deliveryReference: notification.skipped ? null : notification.providerMessageId, deliveryReferencePresent: !notification.skipped && Boolean(notification.providerMessageId) };
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return { status: 503, body: issued.membershipState === "invited"
|
|
60
|
+
? { code: "INVITATION_NOTIFICATION_FAILED", error: "Invitation persisted; notification unavailable", invitationId: issued.invitationId }
|
|
61
|
+
: { ...issued, code: "INVITATION_NOTIFICATION_FAILED", error: "Membership active; notification unavailable" } };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { status: 200, body: { success: true, ...issued, ...delivery, message: issued.membershipState === "invited" ? `Invitation pending until ${issued.expiresAt}` : "Operator membership active" } };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const failure = err;
|
|
68
|
+
if (failure.identityCode)
|
|
69
|
+
return { status: failure.bffCode === "FORBIDDEN" ? 403 : 409, body: { code: failure.identityCode, error: failure.identityCode } };
|
|
70
|
+
return { status: 503, body: { code: "IDENTITY_ACTION_FAILED", error: String(err) } };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function isInviteRole(value) {
|
|
74
|
+
return value === "admin" || value === "distributor";
|
|
75
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export interface IdentityJwk {
|
|
2
|
+
[key: string]: unknown;
|
|
3
|
+
kty?: string;
|
|
4
|
+
alg?: string;
|
|
5
|
+
crv?: string;
|
|
6
|
+
kid?: string;
|
|
7
|
+
use?: string;
|
|
8
|
+
x?: string;
|
|
9
|
+
y?: string;
|
|
10
|
+
d?: string;
|
|
11
|
+
}
|
|
12
|
+
type Key = CryptoKey | Uint8Array;
|
|
13
|
+
interface Signer {
|
|
14
|
+
setProtectedHeader(header: {
|
|
15
|
+
alg: string;
|
|
16
|
+
kid: string;
|
|
17
|
+
typ: string;
|
|
18
|
+
}): Signer;
|
|
19
|
+
setSubject(value: string): Signer;
|
|
20
|
+
setIssuer(value: string): Signer;
|
|
21
|
+
setAudience(value: string): Signer;
|
|
22
|
+
setIssuedAt(value: number): Signer;
|
|
23
|
+
setExpirationTime(value: number): Signer;
|
|
24
|
+
sign(key: Key): Promise<string>;
|
|
25
|
+
}
|
|
26
|
+
/** The host loads its selected JWT implementation only for the selected adapter. */
|
|
27
|
+
export interface IdentityJwtLibrary {
|
|
28
|
+
importJWK(jwk: IdentityJwk, alg: string): Promise<Key>;
|
|
29
|
+
jwtVerify(token: string, key: (header: {
|
|
30
|
+
alg?: string;
|
|
31
|
+
kid?: string;
|
|
32
|
+
}) => Promise<Key>, options: {
|
|
33
|
+
algorithms: string[];
|
|
34
|
+
audience: string;
|
|
35
|
+
issuer: string;
|
|
36
|
+
currentDate?: Date;
|
|
37
|
+
clockTolerance?: number;
|
|
38
|
+
}): Promise<{
|
|
39
|
+
payload: IdentityJwtClaims;
|
|
40
|
+
}>;
|
|
41
|
+
SignJWT: new (payload: Record<string, unknown>) => Signer;
|
|
42
|
+
}
|
|
43
|
+
export type IdentityJwtLibraryLoader = () => Promise<IdentityJwtLibrary>;
|
|
44
|
+
/** Claims returned only after signature verification by the host library. */
|
|
45
|
+
export interface IdentityJwtClaims {
|
|
46
|
+
[claim: string]: unknown;
|
|
47
|
+
sub?: string;
|
|
48
|
+
iss?: string;
|
|
49
|
+
aud?: string | string[];
|
|
50
|
+
exp?: number;
|
|
51
|
+
iat?: number;
|
|
52
|
+
nbf?: number;
|
|
53
|
+
}
|
|
54
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { HttpResponse } from "../http/index.js";
|
|
2
|
+
import { type IdentityResponseOptions } from "./response.js";
|
|
3
|
+
export declare function refusesMachineActorRequest(route: string, method: string | undefined, isMachineActor: boolean | undefined, writeRoutes?: readonly string[]): boolean;
|
|
4
|
+
export declare function sendMachineActorWriteRefused(res: HttpResponse, options?: IdentityResponseOptions): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createIdentityResponder } from "./response.js";
|
|
2
|
+
export function refusesMachineActorRequest(route, method, isMachineActor, writeRoutes = []) {
|
|
3
|
+
if (isMachineActor === false)
|
|
4
|
+
return false;
|
|
5
|
+
const verb = (method ?? "").toUpperCase();
|
|
6
|
+
return verb !== "GET" && verb !== "HEAD" && !writeRoutes.includes(route);
|
|
7
|
+
}
|
|
8
|
+
export function sendMachineActorWriteRefused(res, options = {}) {
|
|
9
|
+
const { sendBffError } = createIdentityResponder(options);
|
|
10
|
+
sendBffError(res, "FORBIDDEN", "Human operator required", {
|
|
11
|
+
details: { reason: "machine_actor_write_refused" },
|
|
12
|
+
});
|
|
13
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface MagicLinkAuthClient {
|
|
2
|
+
auth: {
|
|
3
|
+
signInWithOtp(input: {
|
|
4
|
+
email: string;
|
|
5
|
+
options: {
|
|
6
|
+
shouldCreateUser: false;
|
|
7
|
+
emailRedirectTo: string;
|
|
8
|
+
};
|
|
9
|
+
}): Promise<{
|
|
10
|
+
error: {
|
|
11
|
+
name?: string;
|
|
12
|
+
message?: string;
|
|
13
|
+
} | null;
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export interface MagicLinkRateLimitClient {
|
|
18
|
+
rpc(fn: string, args: Record<string, unknown>): Promise<{
|
|
19
|
+
data: unknown;
|
|
20
|
+
error: {
|
|
21
|
+
message?: string;
|
|
22
|
+
} | null;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
export interface MagicLinkDecision {
|
|
26
|
+
allowed: boolean;
|
|
27
|
+
reason?: string;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export function createSupabaseAccountLinkStore(rawClient) {
|
|
2
|
+
const client = rawClient;
|
|
3
|
+
return {
|
|
4
|
+
async findAccountByPrincipalId(principalId) {
|
|
5
|
+
const { data, error } = await client
|
|
6
|
+
.from("clients")
|
|
7
|
+
.select("id, email, auth_user_id")
|
|
8
|
+
.eq("auth_user_id", principalId)
|
|
9
|
+
.maybeSingle();
|
|
10
|
+
if (error)
|
|
11
|
+
throw error;
|
|
12
|
+
if (!data)
|
|
13
|
+
return null;
|
|
14
|
+
return { id: data.id, email: data.email ?? null, principalId: data.auth_user_id ?? null };
|
|
15
|
+
},
|
|
16
|
+
async findAccountByEmail(email) {
|
|
17
|
+
const rows = await findManagedContactsByEmail(client, email, false);
|
|
18
|
+
if (rows.length > 1) {
|
|
19
|
+
throw Object.assign(new Error("multiple rows returned"), { code: "PGRST116" });
|
|
20
|
+
}
|
|
21
|
+
const data = rows[0];
|
|
22
|
+
if (!data)
|
|
23
|
+
return null;
|
|
24
|
+
return { id: data.id, email: data.email ?? null, principalId: data.auth_user_id ?? null };
|
|
25
|
+
},
|
|
26
|
+
async isReservedPrincipal(principalId) {
|
|
27
|
+
const { data, error } = await client
|
|
28
|
+
.from("admin_users")
|
|
29
|
+
.select("id, is_machine_actor")
|
|
30
|
+
.eq("id", principalId)
|
|
31
|
+
.maybeSingle();
|
|
32
|
+
if (error)
|
|
33
|
+
throw error;
|
|
34
|
+
if (!data)
|
|
35
|
+
return false;
|
|
36
|
+
return data.is_machine_actor !== false;
|
|
37
|
+
},
|
|
38
|
+
async linkPrincipalToAccount(accountId, principalId) {
|
|
39
|
+
const { data, error } = await client
|
|
40
|
+
.from("clients")
|
|
41
|
+
.update({ auth_user_id: principalId })
|
|
42
|
+
.eq("id", accountId)
|
|
43
|
+
.is("auth_user_id", null)
|
|
44
|
+
.select("auth_user_id")
|
|
45
|
+
.maybeSingle();
|
|
46
|
+
if (error)
|
|
47
|
+
throw error;
|
|
48
|
+
if (data?.auth_user_id === principalId) {
|
|
49
|
+
return { ok: true, alreadyLinked: false };
|
|
50
|
+
}
|
|
51
|
+
const { data: current, error: currentError } = await client
|
|
52
|
+
.from("clients")
|
|
53
|
+
.select("auth_user_id")
|
|
54
|
+
.eq("id", accountId)
|
|
55
|
+
.maybeSingle();
|
|
56
|
+
if (currentError)
|
|
57
|
+
throw currentError;
|
|
58
|
+
if (current?.auth_user_id === principalId) {
|
|
59
|
+
return { ok: true, alreadyLinked: true };
|
|
60
|
+
}
|
|
61
|
+
return { ok: false, existingPrincipalId: current?.auth_user_id ?? null };
|
|
62
|
+
},
|
|
63
|
+
async createAccount(input) {
|
|
64
|
+
const { data, error } = await client
|
|
65
|
+
.from("clients")
|
|
66
|
+
.insert({
|
|
67
|
+
email: input.email,
|
|
68
|
+
auth_user_id: input.principalId,
|
|
69
|
+
acquisition_source: input.acquisitionSource,
|
|
70
|
+
lifecycle_stage: input.lifecycleStage,
|
|
71
|
+
})
|
|
72
|
+
.select("id")
|
|
73
|
+
.single();
|
|
74
|
+
if (error)
|
|
75
|
+
throw error;
|
|
76
|
+
return { id: data.id };
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
async function findManagedContactsByEmail(client, email, oldestFirst) {
|
|
81
|
+
const normalized = email.trim().toLowerCase();
|
|
82
|
+
// Escaping bounds the database prefilter; Unicode regex folding can still
|
|
83
|
+
// widen it, so only exact normalized addresses may cross this boundary.
|
|
84
|
+
let query = client.from("clients")
|
|
85
|
+
.select("id, email, auth_user_id")
|
|
86
|
+
.filter("email", "imatch", `^${normalized.replace(/[\\^$.*+?()[\]{}|]/g, "\\$&")}$`);
|
|
87
|
+
if (oldestFirst)
|
|
88
|
+
query = query.order("created_at", { ascending: true });
|
|
89
|
+
const { data, error } = await query.limit(10);
|
|
90
|
+
if (error)
|
|
91
|
+
throw error;
|
|
92
|
+
return (data ?? []).filter((row) => row.email?.trim().toLowerCase() === normalized);
|
|
93
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { SupabaseCreateClient } from "../data/index.js";
|
|
2
|
+
import type { AdminAuthPort } from "./ports.js";
|
|
3
|
+
export declare function createSupabaseAdminAuth(rawClient: unknown): AdminAuthPort;
|
|
4
|
+
export declare function createSupabaseAdminAuthFromEnv(env: {
|
|
5
|
+
url: string;
|
|
6
|
+
anonKey: string;
|
|
7
|
+
}, accessToken: string | null, createClient: SupabaseCreateClient): AdminAuthPort;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { authorizeAdminWithUser } from "./adminAuthorization.js";
|
|
2
|
+
export function createSupabaseAdminAuth(rawClient) {
|
|
3
|
+
const client = rawClient;
|
|
4
|
+
return {
|
|
5
|
+
async authorize(accessToken, options) {
|
|
6
|
+
const authz = await authorizeAdminWithUser(client, accessToken, options);
|
|
7
|
+
if (authz.ok === false) {
|
|
8
|
+
return { ok: false, code: authz.code, message: authz.message };
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
ok: true,
|
|
12
|
+
principalId: authz.userId,
|
|
13
|
+
role: authz.role,
|
|
14
|
+
isMachineActor: authz.isMachineActor,
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function createSupabaseAdminAuthFromEnv(env, accessToken, createClient) {
|
|
20
|
+
const client = createClient(env.url, env.anonKey, {
|
|
21
|
+
auth: { persistSession: false, autoRefreshToken: false },
|
|
22
|
+
global: {
|
|
23
|
+
headers: accessToken ? { Authorization: `Bearer ${accessToken}` } : {},
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
return createSupabaseAdminAuth(client);
|
|
27
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { AdminUserInviteRole } from "./inviteAdminUserUseCase.js";
|
|
2
|
+
import type { ManagedOperatorMembershipResult, OperatorInvitationConflict } from "./contracts/operatorInvitations.js";
|
|
3
|
+
import type { InviteAdminUserGateway } from "./inviteAdminUserUseCase.js";
|
|
4
|
+
import type { RemoveAdminUserGateway } from "./removeAdminUserUseCase.js";
|
|
5
|
+
interface InviteAdminUserQuery {
|
|
6
|
+
insert(row: {
|
|
7
|
+
id: string;
|
|
8
|
+
email: string;
|
|
9
|
+
role: AdminUserInviteRole;
|
|
10
|
+
membership_state: "active";
|
|
11
|
+
membership_provenance: "invitation";
|
|
12
|
+
membership_accepted_at: string;
|
|
13
|
+
}): PromiseLike<{
|
|
14
|
+
error: {
|
|
15
|
+
code?: string;
|
|
16
|
+
message?: string;
|
|
17
|
+
} | null;
|
|
18
|
+
}>;
|
|
19
|
+
select(columns: string): {
|
|
20
|
+
eq(column: string, value: string): {
|
|
21
|
+
maybeSingle(): PromiseLike<{
|
|
22
|
+
data: {
|
|
23
|
+
id: string;
|
|
24
|
+
role?: string;
|
|
25
|
+
membership_state?: string;
|
|
26
|
+
is_machine_actor?: boolean | null;
|
|
27
|
+
} | null;
|
|
28
|
+
error?: unknown;
|
|
29
|
+
}>;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export interface InviteAdminUserClient {
|
|
34
|
+
auth: {
|
|
35
|
+
getUser(token: string): PromiseLike<{
|
|
36
|
+
data: {
|
|
37
|
+
user: {
|
|
38
|
+
id: string;
|
|
39
|
+
} | null;
|
|
40
|
+
};
|
|
41
|
+
error: unknown;
|
|
42
|
+
}>;
|
|
43
|
+
admin: {
|
|
44
|
+
listUsers(options: {
|
|
45
|
+
page: number;
|
|
46
|
+
perPage: number;
|
|
47
|
+
}): PromiseLike<{
|
|
48
|
+
data: {
|
|
49
|
+
users: Array<{
|
|
50
|
+
id: string;
|
|
51
|
+
email?: string | null;
|
|
52
|
+
}>;
|
|
53
|
+
nextPage?: number | null;
|
|
54
|
+
} | null;
|
|
55
|
+
error: unknown;
|
|
56
|
+
}>;
|
|
57
|
+
inviteUserByEmail(email: string, options: {
|
|
58
|
+
redirectTo: string;
|
|
59
|
+
}): PromiseLike<{
|
|
60
|
+
data: {
|
|
61
|
+
user: {
|
|
62
|
+
id: string;
|
|
63
|
+
} | null;
|
|
64
|
+
};
|
|
65
|
+
error: {
|
|
66
|
+
message: string;
|
|
67
|
+
} | null;
|
|
68
|
+
}>;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
from(table: "admin_users"): InviteAdminUserQuery;
|
|
72
|
+
}
|
|
73
|
+
interface RemoveAdminUserQuery {
|
|
74
|
+
select(columns: string): {
|
|
75
|
+
eq(column: string, value: string): RemoveAdminUserQueryResult;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
interface RemoveAdminUserQueryResult {
|
|
79
|
+
eq(column: string, value: string): RemoveAdminUserQueryResult;
|
|
80
|
+
maybeSingle(): PromiseLike<{
|
|
81
|
+
data: {
|
|
82
|
+
id: string;
|
|
83
|
+
role: string;
|
|
84
|
+
} | null;
|
|
85
|
+
error: {
|
|
86
|
+
message: string;
|
|
87
|
+
} | null;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
90
|
+
export interface RemoveAdminUserClient {
|
|
91
|
+
auth: {
|
|
92
|
+
getUser(token: string): PromiseLike<{
|
|
93
|
+
data: {
|
|
94
|
+
user: {
|
|
95
|
+
id: string;
|
|
96
|
+
} | null;
|
|
97
|
+
};
|
|
98
|
+
error: unknown;
|
|
99
|
+
}>;
|
|
100
|
+
};
|
|
101
|
+
from(table: "admin_users"): RemoveAdminUserQuery;
|
|
102
|
+
rpc(fn: "admin_revoke_admin_user", args: {
|
|
103
|
+
p_target: string;
|
|
104
|
+
p_reason: string | null;
|
|
105
|
+
}): PromiseLike<{
|
|
106
|
+
error: {
|
|
107
|
+
message: string;
|
|
108
|
+
} | null;
|
|
109
|
+
}>;
|
|
110
|
+
}
|
|
111
|
+
export declare function createInviteAdminUserGateway(clientFactory: () => InviteAdminUserClient): InviteAdminUserGateway & {
|
|
112
|
+
activateMembership(input: {
|
|
113
|
+
principalId: string;
|
|
114
|
+
email: string;
|
|
115
|
+
role: AdminUserInviteRole;
|
|
116
|
+
}): Promise<ManagedOperatorMembershipResult | {
|
|
117
|
+
code: OperatorInvitationConflict;
|
|
118
|
+
}>;
|
|
119
|
+
};
|
|
120
|
+
export declare function createRemoveAdminUserGateway(clientFactory: () => RemoveAdminUserClient): RemoveAdminUserGateway;
|
|
121
|
+
/** Managed eligibility depends only on active native membership and known roles. */
|
|
122
|
+
export declare function managedOperatorMagicLinkEligibility(rawClient: unknown, email: string): Promise<"eligible" | "not_allowlisted" | "role_not_allowed">;
|
|
123
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
export function createInviteAdminUserGateway(clientFactory) {
|
|
2
|
+
let client = null;
|
|
3
|
+
const getClient = () => client ??= clientFactory();
|
|
4
|
+
async function membership(principalId) {
|
|
5
|
+
const { data, error } = await getClient().from("admin_users").select("id, role, membership_state, is_machine_actor").eq("id", principalId).maybeSingle();
|
|
6
|
+
if (error)
|
|
7
|
+
throw error;
|
|
8
|
+
return data;
|
|
9
|
+
}
|
|
10
|
+
function existing(row, role) {
|
|
11
|
+
if (row.is_machine_actor !== false)
|
|
12
|
+
return { code: "IDENTITY_CONFLICT" };
|
|
13
|
+
if (row.membership_state === "revoked")
|
|
14
|
+
return { code: "MANAGED_REINVITE_UNSUPPORTED" };
|
|
15
|
+
if (row.membership_state !== "active")
|
|
16
|
+
return { code: "IDENTITY_CONFLICT" };
|
|
17
|
+
if (row.role !== role)
|
|
18
|
+
return { code: "INVITATION_ROLE_CONFLICT" };
|
|
19
|
+
return { principalId: row.id, role, membershipState: "active", code: "ALREADY_ACTIVE" };
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
async activateMembership(input) {
|
|
23
|
+
const before = await membership(input.principalId);
|
|
24
|
+
if (before)
|
|
25
|
+
return existing(before, input.role);
|
|
26
|
+
const { error } = await getClient().from("admin_users").insert({ id: input.principalId, email: input.email, role: input.role,
|
|
27
|
+
membership_state: "active", membership_provenance: "invitation", membership_accepted_at: new Date().toISOString() });
|
|
28
|
+
if (error) {
|
|
29
|
+
if (error.code !== "23505")
|
|
30
|
+
throw error;
|
|
31
|
+
const raced = await membership(input.principalId);
|
|
32
|
+
if (!raced)
|
|
33
|
+
throw error;
|
|
34
|
+
return existing(raced, input.role);
|
|
35
|
+
}
|
|
36
|
+
return { principalId: input.principalId, role: input.role, membershipState: "active", code: "MEMBERSHIP_CREATED" };
|
|
37
|
+
},
|
|
38
|
+
async getUser(accessToken) {
|
|
39
|
+
const { data, error } = await getClient().auth.getUser(accessToken);
|
|
40
|
+
return { user: data?.user ?? null, error };
|
|
41
|
+
},
|
|
42
|
+
async findAdminUser(userId) {
|
|
43
|
+
const { data, error } = await getClient()
|
|
44
|
+
.from("admin_users")
|
|
45
|
+
.select("id, role, membership_state, is_machine_actor")
|
|
46
|
+
.eq("id", userId)
|
|
47
|
+
.maybeSingle();
|
|
48
|
+
if (error)
|
|
49
|
+
throw error;
|
|
50
|
+
return data && typeof data.role === "string" && typeof data.membership_state === "string" ? { id: data.id, role: data.role, membership_state: data.membership_state, is_machine_actor: data.is_machine_actor !== false } : null;
|
|
51
|
+
},
|
|
52
|
+
async findAdminUserByEmail(email) {
|
|
53
|
+
const { data, error } = await getClient()
|
|
54
|
+
.from("admin_users")
|
|
55
|
+
.select("id")
|
|
56
|
+
.eq("email", email)
|
|
57
|
+
.maybeSingle();
|
|
58
|
+
if (error)
|
|
59
|
+
throw error;
|
|
60
|
+
return data ? { id: data.id } : null;
|
|
61
|
+
},
|
|
62
|
+
async findAuthUserByEmail(email) {
|
|
63
|
+
const normalizedEmail = email.trim().toLowerCase();
|
|
64
|
+
const perPage = 1000;
|
|
65
|
+
const maxUsers = 5000;
|
|
66
|
+
let page = 1;
|
|
67
|
+
let scanned = 0;
|
|
68
|
+
while (scanned < maxUsers) {
|
|
69
|
+
const { data, error } = await getClient().auth.admin.listUsers({ page, perPage });
|
|
70
|
+
if (error)
|
|
71
|
+
throw error;
|
|
72
|
+
const users = data?.users ?? [];
|
|
73
|
+
const match = users.find((user) => (user.email ?? "").trim().toLowerCase() === normalizedEmail);
|
|
74
|
+
if (match)
|
|
75
|
+
return { id: match.id, email: match.email };
|
|
76
|
+
scanned += users.length;
|
|
77
|
+
if (!data?.nextPage)
|
|
78
|
+
return null;
|
|
79
|
+
page = data.nextPage;
|
|
80
|
+
}
|
|
81
|
+
throw new Error("auth_user_lookup_cap_exceeded");
|
|
82
|
+
},
|
|
83
|
+
async inviteAuthUser(input) {
|
|
84
|
+
const { data, error } = await getClient().auth.admin.inviteUserByEmail(input.email, {
|
|
85
|
+
redirectTo: input.redirectTo,
|
|
86
|
+
});
|
|
87
|
+
return { user: data.user, error };
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function createRemoveAdminUserGateway(clientFactory) {
|
|
92
|
+
let client = null;
|
|
93
|
+
const getClient = () => client ??= clientFactory();
|
|
94
|
+
return {
|
|
95
|
+
async getUser(accessToken) {
|
|
96
|
+
const { data, error } = await getClient().auth.getUser(accessToken);
|
|
97
|
+
return { user: data?.user ?? null, error };
|
|
98
|
+
},
|
|
99
|
+
async findAdminUser(userId) {
|
|
100
|
+
const { data, error } = await getClient()
|
|
101
|
+
.from("admin_users")
|
|
102
|
+
.select("id, role")
|
|
103
|
+
.eq("id", userId)
|
|
104
|
+
.eq("membership_state", "active")
|
|
105
|
+
.maybeSingle();
|
|
106
|
+
if (error)
|
|
107
|
+
throw error;
|
|
108
|
+
return data;
|
|
109
|
+
},
|
|
110
|
+
async revokeAdminUser(userId) {
|
|
111
|
+
return await getClient().rpc("admin_revoke_admin_user", {
|
|
112
|
+
p_target: userId,
|
|
113
|
+
p_reason: null,
|
|
114
|
+
});
|
|
115
|
+
},
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/** Managed eligibility depends only on active native membership and known roles. */
|
|
119
|
+
export async function managedOperatorMagicLinkEligibility(rawClient, email) {
|
|
120
|
+
const client = rawClient;
|
|
121
|
+
const { data, error } = await client.from("admin_users").select("id, role").eq("email", email.trim().toLowerCase()).eq("membership_state", "active").maybeSingle();
|
|
122
|
+
if (error)
|
|
123
|
+
throw error;
|
|
124
|
+
if (!data)
|
|
125
|
+
return "not_allowlisted";
|
|
126
|
+
return data.role === "admin" || data.role === "distributor" ? "eligible" : "role_not_allowed";
|
|
127
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { SupabaseCreateClient } from "../data/index.js";
|
|
2
|
+
export interface ManagedIdentityUser {
|
|
3
|
+
id: string;
|
|
4
|
+
email?: string | null;
|
|
5
|
+
email_confirmed_at?: string | null;
|
|
6
|
+
user_metadata?: Record<string, unknown> | null;
|
|
7
|
+
}
|
|
8
|
+
export interface ManagedIdentityRow {
|
|
9
|
+
id: string;
|
|
10
|
+
email?: string | null;
|
|
11
|
+
auth_user_id?: string | null;
|
|
12
|
+
role?: string | null;
|
|
13
|
+
is_machine_actor?: boolean | null;
|
|
14
|
+
membership_state?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ManagedIdentityQuery extends PromiseLike<{
|
|
17
|
+
data: ManagedIdentityRow[] | null;
|
|
18
|
+
error: unknown;
|
|
19
|
+
}> {
|
|
20
|
+
select(columns: string): ManagedIdentityQuery;
|
|
21
|
+
eq(column: string, value: string): ManagedIdentityQuery;
|
|
22
|
+
filter(column: string, operator: "imatch", value: string): ManagedIdentityQuery;
|
|
23
|
+
is(column: string, value: null): ManagedIdentityQuery;
|
|
24
|
+
order(column: string, options: {
|
|
25
|
+
ascending: boolean;
|
|
26
|
+
}): ManagedIdentityQuery;
|
|
27
|
+
limit(count: number): ManagedIdentityQuery;
|
|
28
|
+
maybeSingle(): PromiseLike<{
|
|
29
|
+
data: ManagedIdentityRow | null;
|
|
30
|
+
error: unknown;
|
|
31
|
+
}>;
|
|
32
|
+
single(): PromiseLike<{
|
|
33
|
+
data: ManagedIdentityRow | null;
|
|
34
|
+
error: unknown;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
export interface ManagedIdentityTable {
|
|
38
|
+
select(columns: string): ManagedIdentityQuery;
|
|
39
|
+
update(value: Record<string, unknown>): ManagedIdentityQuery;
|
|
40
|
+
insert(value: Record<string, unknown>): ManagedIdentityQuery;
|
|
41
|
+
}
|
|
42
|
+
/** Only the methods consumed by the identity adapters; the host owns the SDK. */
|
|
43
|
+
export interface ManagedIdentityClient {
|
|
44
|
+
from(table: string): ManagedIdentityTable;
|
|
45
|
+
auth: {
|
|
46
|
+
getUser(token: string): PromiseLike<{
|
|
47
|
+
data: {
|
|
48
|
+
user: ManagedIdentityUser | null;
|
|
49
|
+
};
|
|
50
|
+
error: unknown;
|
|
51
|
+
}>;
|
|
52
|
+
admin: {
|
|
53
|
+
listUsers(options: {
|
|
54
|
+
page: number;
|
|
55
|
+
perPage: number;
|
|
56
|
+
}): PromiseLike<{
|
|
57
|
+
data: {
|
|
58
|
+
users: ManagedIdentityUser[];
|
|
59
|
+
nextPage?: number | null;
|
|
60
|
+
};
|
|
61
|
+
error: unknown;
|
|
62
|
+
}>;
|
|
63
|
+
getUserById(id: string): PromiseLike<{
|
|
64
|
+
data: {
|
|
65
|
+
user: ManagedIdentityUser | null;
|
|
66
|
+
};
|
|
67
|
+
error: unknown;
|
|
68
|
+
}>;
|
|
69
|
+
createUser(input: {
|
|
70
|
+
email: string;
|
|
71
|
+
email_confirm: boolean;
|
|
72
|
+
}): PromiseLike<{
|
|
73
|
+
data: {
|
|
74
|
+
user: ManagedIdentityUser | null;
|
|
75
|
+
};
|
|
76
|
+
error: unknown;
|
|
77
|
+
}>;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export declare function createManagedIdentityClients(createClient: SupabaseCreateClient): {
|
|
82
|
+
actor: (env: {
|
|
83
|
+
url: string;
|
|
84
|
+
anonKey: string;
|
|
85
|
+
}, accessToken: string | null) => ManagedIdentityClient;
|
|
86
|
+
service: (env: {
|
|
87
|
+
url: string;
|
|
88
|
+
serviceRoleKey: string;
|
|
89
|
+
}) => ManagedIdentityClient;
|
|
90
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function createManagedIdentityClients(createClient) {
|
|
2
|
+
const actor = (env, accessToken) => createClient(env.url, env.anonKey, {
|
|
3
|
+
auth: { persistSession: false, autoRefreshToken: false },
|
|
4
|
+
global: { headers: accessToken ? { Authorization: `Bearer ${accessToken}` } : {} },
|
|
5
|
+
});
|
|
6
|
+
const service = (env) => createClient(env.url, env.serviceRoleKey, {
|
|
7
|
+
auth: { persistSession: false, autoRefreshToken: false },
|
|
8
|
+
});
|
|
9
|
+
return { actor, service };
|
|
10
|
+
}
|