authhero 8.23.1 → 8.25.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.
Files changed (54) hide show
  1. package/dist/assets/u/widget/index.esm.js +1 -1
  2. package/dist/authhero.cjs +139 -142
  3. package/dist/authhero.d.ts +1073 -292
  4. package/dist/authhero.mjs +20321 -15480
  5. package/dist/tsconfig.types.tsbuildinfo +1 -1
  6. package/dist/types/adapters/control-plane-custom-domains.d.ts +35 -0
  7. package/dist/types/authentication-flows/passwordless.d.ts +4 -4
  8. package/dist/types/helpers/control-plane-client.d.ts +46 -0
  9. package/dist/types/helpers/control-plane-sync-events.d.ts +12 -28
  10. package/dist/types/index.d.ts +326 -246
  11. package/dist/types/routes/auth-api/index.d.ts +28 -28
  12. package/dist/types/routes/auth-api/passwordless.d.ts +12 -12
  13. package/dist/types/routes/auth-api/register/index.d.ts +2 -2
  14. package/dist/types/routes/auth-api/revoke.d.ts +2 -2
  15. package/dist/types/routes/auth-api/token.d.ts +12 -12
  16. package/dist/types/routes/management-api/authentication-methods.d.ts +1 -1
  17. package/dist/types/routes/management-api/clients.d.ts +14 -14
  18. package/dist/types/routes/management-api/connections.d.ts +16 -16
  19. package/dist/types/routes/management-api/custom-domains.d.ts +6 -6
  20. package/dist/types/routes/management-api/email-templates.d.ts +18 -18
  21. package/dist/types/routes/management-api/forms.d.ts +126 -126
  22. package/dist/types/routes/management-api/guardian.d.ts +5 -5
  23. package/dist/types/routes/management-api/helpers.d.ts +47 -0
  24. package/dist/types/routes/management-api/index.d.ts +280 -211
  25. package/dist/types/routes/management-api/logs.d.ts +4 -4
  26. package/dist/types/routes/management-api/migration-sources.d.ts +6 -6
  27. package/dist/types/routes/management-api/organizations.d.ts +8 -8
  28. package/dist/types/routes/management-api/prompts.d.ts +4 -4
  29. package/dist/types/routes/management-api/roles.d.ts +1 -1
  30. package/dist/types/routes/management-api/tenant-members.d.ts +304 -0
  31. package/dist/types/routes/management-api/tenant-members.test.d.ts +1 -0
  32. package/dist/types/routes/management-api/tenants.d.ts +8 -8
  33. package/dist/types/routes/management-api/users.d.ts +71 -2
  34. package/dist/types/routes/proxy-control-plane/custom-domains.d.ts +45 -0
  35. package/dist/types/routes/proxy-control-plane/index.d.ts +47 -6
  36. package/dist/types/routes/proxy-control-plane/scopes.d.ts +20 -0
  37. package/dist/types/routes/proxy-control-plane/tenant-members.d.ts +37 -0
  38. package/dist/types/routes/proxy-control-plane/verify.d.ts +29 -4
  39. package/dist/types/routes/proxy-control-plane/verify.test.d.ts +1 -0
  40. package/dist/types/routes/universal-login/common.d.ts +8 -8
  41. package/dist/types/routes/universal-login/flow-api.d.ts +12 -12
  42. package/dist/types/routes/universal-login/identifier.d.ts +2 -2
  43. package/dist/types/routes/universal-login/index.d.ts +2 -2
  44. package/dist/types/routes/universal-login/u2-index.d.ts +5 -5
  45. package/dist/types/routes/universal-login/u2-routes.d.ts +5 -5
  46. package/dist/types/tenant-members/control-plane-roundtrip.test.d.ts +1 -0
  47. package/dist/types/tenant-members/local-backend.d.ts +43 -0
  48. package/dist/types/tenant-members/local-backend.test.d.ts +1 -0
  49. package/dist/types/tenant-members/remote-backend.d.ts +17 -0
  50. package/dist/types/tenant-members/types.d.ts +112 -0
  51. package/dist/types/tenant-members/wire.d.ts +28 -0
  52. package/dist/types/types/AuthHeroConfig.d.ts +54 -7
  53. package/dist/types/types/IdToken.d.ts +4 -4
  54. package/package.json +6 -6
@@ -162,6 +162,75 @@ export declare const userRoutes: OpenAPIHono<{
162
162
  }[];
163
163
  total?: number | undefined;
164
164
  next?: string | undefined;
165
+ } | {
166
+ users: {
167
+ created_at: string;
168
+ updated_at: string;
169
+ connection: string;
170
+ user_id: string;
171
+ email_verified: boolean;
172
+ provider: string;
173
+ is_social: boolean;
174
+ login_count: number;
175
+ name?: string | undefined;
176
+ email?: string | undefined;
177
+ username?: string | undefined;
178
+ given_name?: string | undefined;
179
+ phone_number?: string | undefined;
180
+ phone_verified?: boolean | undefined;
181
+ family_name?: string | undefined;
182
+ profileData?: string | undefined;
183
+ address?: {
184
+ formatted?: string | undefined;
185
+ street_address?: string | undefined;
186
+ locality?: string | undefined;
187
+ region?: string | undefined;
188
+ postal_code?: string | undefined;
189
+ country?: string | undefined;
190
+ } | undefined;
191
+ nickname?: string | undefined;
192
+ picture?: string | undefined;
193
+ locale?: string | undefined;
194
+ linked_to?: string | undefined;
195
+ app_metadata?: any;
196
+ user_metadata?: any;
197
+ middle_name?: string | undefined;
198
+ preferred_username?: string | undefined;
199
+ profile?: string | undefined;
200
+ website?: string | undefined;
201
+ gender?: string | undefined;
202
+ birthdate?: string | undefined;
203
+ zoneinfo?: string | undefined;
204
+ verify_email?: boolean | undefined;
205
+ last_ip?: string | undefined;
206
+ last_login?: string | undefined;
207
+ identities?: {
208
+ connection: string;
209
+ user_id: string;
210
+ provider: string;
211
+ isSocial: boolean;
212
+ email?: string | undefined;
213
+ email_verified?: boolean | undefined;
214
+ phone_number?: string | undefined;
215
+ phone_verified?: boolean | undefined;
216
+ username?: string | undefined;
217
+ access_token?: string | undefined;
218
+ access_token_secret?: string | undefined;
219
+ refresh_token?: string | undefined;
220
+ profileData?: {
221
+ [x: string]: any;
222
+ email?: string | undefined;
223
+ email_verified?: boolean | undefined;
224
+ name?: string | undefined;
225
+ username?: string | undefined;
226
+ given_name?: string | undefined;
227
+ phone_number?: string | undefined;
228
+ phone_verified?: boolean | undefined;
229
+ family_name?: string | undefined;
230
+ } | undefined;
231
+ }[] | undefined;
232
+ }[];
233
+ next?: string | undefined;
165
234
  };
166
235
  outputFormat: "json";
167
236
  status: 200;
@@ -740,7 +809,7 @@ export declare const userRoutes: OpenAPIHono<{
740
809
  };
741
810
  };
742
811
  output: {
743
- type: "fn" | "i" | "sapi" | "acls_summary" | "actions_execution_failed" | "api_limit" | "api_limit_warning" | "appi" | "ciba_exchange_failed" | "ciba_exchange_succeeded" | "ciba_start_failed" | "ciba_start_succeeded" | "cls" | "cs" | "depnote" | "f" | "fc" | "fce" | "fco" | "fcoa" | "fcp" | "fcph" | "fcpn" | "fcpr" | "fcpro" | "fcu" | "fd" | "fdeac" | "fdeaz" | "fdecc" | "fdu" | "feacft" | "feccft" | "fecte" | "fede" | "federated_logout_failed" | "fens" | "feoobft" | "feotpft" | "fepft" | "fepotpft" | "fercft" | "ferrt" | "fertft" | "festft" | "fh" | "fimp" | "fi" | "flo" | "flows_execution_completed" | "flows_execution_failed" | "forms_submission_failed" | "forms_submission_succeeded" | "fp" | "fpar" | "fpurh" | "fs" | "fsa" | "fu" | "fui" | "fv" | "fvr" | "gd_auth_email_verification" | "gd_auth_fail_email_verification" | "gd_auth_failed" | "gd_auth_rejected" | "gd_auth_succeed" | "gd_enrollment_complete" | "gd_otp_rate_limit_exceed" | "gd_recovery_failed" | "gd_recovery_rate_limit_exceed" | "gd_recovery_succeed" | "gd_send_email" | "gd_send_email_verification" | "gd_send_email_verification_failure" | "gd_send_pn" | "gd_send_pn_failure" | "gd_send_sms" | "gd_send_sms_failure" | "gd_send_voice" | "gd_send_voice_failure" | "gd_start_auth" | "gd_start_enroll" | "gd_start_enroll_failed" | "gd_tenant_update" | "gd_unenroll" | "gd_update_device_account" | "gd_webauthn_challenge_failed" | "gd_webauthn_enrollment_failed" | "kms_key_management_failure" | "kms_key_management_success" | "kms_key_state_changed" | "limit_delegation" | "limit_mu" | "limit_sul" | "limit_wc" | "mfar" | "mgmt_api_read" | "my_account_authentication_method_failed" | "my_account_authentication_method_succeeded" | "oidc_backchannel_logout_failed" | "oidc_backchannel_logout_succeeded" | "organization_member_added" | "passkey_challenge_failed" | "passkey_challenge_started" | "pla" | "pwd_leak" | "reset_pwd_leak" | "resource_cleanup" | "rich_consents_access_error" | "s" | "fapi" | "sce" | "scoa" | "scp" | "scpn" | "scpr" | "scu" | "scv" | "sd" | "sdu" | "seacft" | "seccft" | "secte" | "sede" | "sens" | "seoobft" | "seotpft" | "sepotpft" | "sepft" | "sepkoobft" | "sepkotpft" | "sepkrcft" | "sercft" | "sertft" | "sestft" | "simp" | "si" | "signup_pwd_leak" | "slo" | "sh" | "spm" | "srrt" | "ss" | "ss_sso_failure" | "ss_sso_info" | "ss_sso_success" | "ssa" | "sscim" | "sui" | "sv" | "svr" | "too_many_records" | "ublkdu" | "universal_logout_failed" | "universal_logout_succeeded" | "w" | "wn" | "wum";
812
+ type: "fn" | "i" | "cs" | "fi" | "sv" | "acls_summary" | "actions_execution_failed" | "api_limit" | "api_limit_warning" | "appi" | "ciba_exchange_failed" | "ciba_exchange_succeeded" | "ciba_start_failed" | "ciba_start_succeeded" | "cls" | "depnote" | "f" | "fc" | "fce" | "fco" | "fcoa" | "fcp" | "fcph" | "fcpn" | "fcpr" | "fcpro" | "fcu" | "fd" | "fdeac" | "fdeaz" | "fdecc" | "fdu" | "feacft" | "feccft" | "fecte" | "fede" | "federated_logout_failed" | "fens" | "feoobft" | "feotpft" | "fepft" | "fepotpft" | "fercft" | "ferrt" | "fertft" | "festft" | "fh" | "fimp" | "flo" | "flows_execution_completed" | "flows_execution_failed" | "forms_submission_failed" | "forms_submission_succeeded" | "fp" | "fpar" | "fpurh" | "fs" | "fsa" | "fu" | "fui" | "fv" | "fvr" | "gd_auth_email_verification" | "gd_auth_fail_email_verification" | "gd_auth_failed" | "gd_auth_rejected" | "gd_auth_succeed" | "gd_enrollment_complete" | "gd_otp_rate_limit_exceed" | "gd_recovery_failed" | "gd_recovery_rate_limit_exceed" | "gd_recovery_succeed" | "gd_send_email" | "gd_send_email_verification" | "gd_send_email_verification_failure" | "gd_send_pn" | "gd_send_pn_failure" | "gd_send_sms" | "gd_send_sms_failure" | "gd_send_voice" | "gd_send_voice_failure" | "gd_start_auth" | "gd_start_enroll" | "gd_start_enroll_failed" | "gd_tenant_update" | "gd_unenroll" | "gd_update_device_account" | "gd_webauthn_challenge_failed" | "gd_webauthn_enrollment_failed" | "kms_key_management_failure" | "kms_key_management_success" | "kms_key_state_changed" | "limit_delegation" | "limit_mu" | "limit_sul" | "limit_wc" | "mfar" | "mgmt_api_read" | "my_account_authentication_method_failed" | "my_account_authentication_method_succeeded" | "oidc_backchannel_logout_failed" | "oidc_backchannel_logout_succeeded" | "organization_member_added" | "passkey_challenge_failed" | "passkey_challenge_started" | "pla" | "pwd_leak" | "reset_pwd_leak" | "resource_cleanup" | "rich_consents_access_error" | "s" | "sapi" | "fapi" | "sce" | "scoa" | "scp" | "scpn" | "scpr" | "scu" | "scv" | "sd" | "sdu" | "seacft" | "seccft" | "secte" | "sede" | "sens" | "seoobft" | "seotpft" | "sepotpft" | "sepft" | "sepkoobft" | "sepkotpft" | "sepkrcft" | "sercft" | "sertft" | "sestft" | "simp" | "si" | "signup_pwd_leak" | "slo" | "sh" | "spm" | "srrt" | "ss" | "ss_sso_failure" | "ss_sso_info" | "ss_sso_success" | "ssa" | "sscim" | "sui" | "svr" | "too_many_records" | "ublkdu" | "universal_logout_failed" | "universal_logout_succeeded" | "w" | "wn" | "wum";
744
813
  date: string;
745
814
  isMobile: boolean;
746
815
  log_id: string;
@@ -779,7 +848,7 @@ export declare const userRoutes: OpenAPIHono<{
779
848
  limit: number;
780
849
  length: number;
781
850
  logs: {
782
- type: "fn" | "i" | "sapi" | "acls_summary" | "actions_execution_failed" | "api_limit" | "api_limit_warning" | "appi" | "ciba_exchange_failed" | "ciba_exchange_succeeded" | "ciba_start_failed" | "ciba_start_succeeded" | "cls" | "cs" | "depnote" | "f" | "fc" | "fce" | "fco" | "fcoa" | "fcp" | "fcph" | "fcpn" | "fcpr" | "fcpro" | "fcu" | "fd" | "fdeac" | "fdeaz" | "fdecc" | "fdu" | "feacft" | "feccft" | "fecte" | "fede" | "federated_logout_failed" | "fens" | "feoobft" | "feotpft" | "fepft" | "fepotpft" | "fercft" | "ferrt" | "fertft" | "festft" | "fh" | "fimp" | "fi" | "flo" | "flows_execution_completed" | "flows_execution_failed" | "forms_submission_failed" | "forms_submission_succeeded" | "fp" | "fpar" | "fpurh" | "fs" | "fsa" | "fu" | "fui" | "fv" | "fvr" | "gd_auth_email_verification" | "gd_auth_fail_email_verification" | "gd_auth_failed" | "gd_auth_rejected" | "gd_auth_succeed" | "gd_enrollment_complete" | "gd_otp_rate_limit_exceed" | "gd_recovery_failed" | "gd_recovery_rate_limit_exceed" | "gd_recovery_succeed" | "gd_send_email" | "gd_send_email_verification" | "gd_send_email_verification_failure" | "gd_send_pn" | "gd_send_pn_failure" | "gd_send_sms" | "gd_send_sms_failure" | "gd_send_voice" | "gd_send_voice_failure" | "gd_start_auth" | "gd_start_enroll" | "gd_start_enroll_failed" | "gd_tenant_update" | "gd_unenroll" | "gd_update_device_account" | "gd_webauthn_challenge_failed" | "gd_webauthn_enrollment_failed" | "kms_key_management_failure" | "kms_key_management_success" | "kms_key_state_changed" | "limit_delegation" | "limit_mu" | "limit_sul" | "limit_wc" | "mfar" | "mgmt_api_read" | "my_account_authentication_method_failed" | "my_account_authentication_method_succeeded" | "oidc_backchannel_logout_failed" | "oidc_backchannel_logout_succeeded" | "organization_member_added" | "passkey_challenge_failed" | "passkey_challenge_started" | "pla" | "pwd_leak" | "reset_pwd_leak" | "resource_cleanup" | "rich_consents_access_error" | "s" | "fapi" | "sce" | "scoa" | "scp" | "scpn" | "scpr" | "scu" | "scv" | "sd" | "sdu" | "seacft" | "seccft" | "secte" | "sede" | "sens" | "seoobft" | "seotpft" | "sepotpft" | "sepft" | "sepkoobft" | "sepkotpft" | "sepkrcft" | "sercft" | "sertft" | "sestft" | "simp" | "si" | "signup_pwd_leak" | "slo" | "sh" | "spm" | "srrt" | "ss" | "ss_sso_failure" | "ss_sso_info" | "ss_sso_success" | "ssa" | "sscim" | "sui" | "sv" | "svr" | "too_many_records" | "ublkdu" | "universal_logout_failed" | "universal_logout_succeeded" | "w" | "wn" | "wum";
851
+ type: "fn" | "i" | "cs" | "fi" | "sv" | "acls_summary" | "actions_execution_failed" | "api_limit" | "api_limit_warning" | "appi" | "ciba_exchange_failed" | "ciba_exchange_succeeded" | "ciba_start_failed" | "ciba_start_succeeded" | "cls" | "depnote" | "f" | "fc" | "fce" | "fco" | "fcoa" | "fcp" | "fcph" | "fcpn" | "fcpr" | "fcpro" | "fcu" | "fd" | "fdeac" | "fdeaz" | "fdecc" | "fdu" | "feacft" | "feccft" | "fecte" | "fede" | "federated_logout_failed" | "fens" | "feoobft" | "feotpft" | "fepft" | "fepotpft" | "fercft" | "ferrt" | "fertft" | "festft" | "fh" | "fimp" | "flo" | "flows_execution_completed" | "flows_execution_failed" | "forms_submission_failed" | "forms_submission_succeeded" | "fp" | "fpar" | "fpurh" | "fs" | "fsa" | "fu" | "fui" | "fv" | "fvr" | "gd_auth_email_verification" | "gd_auth_fail_email_verification" | "gd_auth_failed" | "gd_auth_rejected" | "gd_auth_succeed" | "gd_enrollment_complete" | "gd_otp_rate_limit_exceed" | "gd_recovery_failed" | "gd_recovery_rate_limit_exceed" | "gd_recovery_succeed" | "gd_send_email" | "gd_send_email_verification" | "gd_send_email_verification_failure" | "gd_send_pn" | "gd_send_pn_failure" | "gd_send_sms" | "gd_send_sms_failure" | "gd_send_voice" | "gd_send_voice_failure" | "gd_start_auth" | "gd_start_enroll" | "gd_start_enroll_failed" | "gd_tenant_update" | "gd_unenroll" | "gd_update_device_account" | "gd_webauthn_challenge_failed" | "gd_webauthn_enrollment_failed" | "kms_key_management_failure" | "kms_key_management_success" | "kms_key_state_changed" | "limit_delegation" | "limit_mu" | "limit_sul" | "limit_wc" | "mfar" | "mgmt_api_read" | "my_account_authentication_method_failed" | "my_account_authentication_method_succeeded" | "oidc_backchannel_logout_failed" | "oidc_backchannel_logout_succeeded" | "organization_member_added" | "passkey_challenge_failed" | "passkey_challenge_started" | "pla" | "pwd_leak" | "reset_pwd_leak" | "resource_cleanup" | "rich_consents_access_error" | "s" | "sapi" | "fapi" | "sce" | "scoa" | "scp" | "scpn" | "scpr" | "scu" | "scv" | "sd" | "sdu" | "seacft" | "seccft" | "secte" | "sede" | "sens" | "seoobft" | "seotpft" | "sepotpft" | "sepft" | "sepkoobft" | "sepkotpft" | "sepkrcft" | "sercft" | "sertft" | "sestft" | "simp" | "si" | "signup_pwd_leak" | "slo" | "sh" | "spm" | "srrt" | "ss" | "ss_sso_failure" | "ss_sso_info" | "ss_sso_success" | "ssa" | "sscim" | "sui" | "svr" | "too_many_records" | "ublkdu" | "universal_logout_failed" | "universal_logout_succeeded" | "w" | "wn" | "wum";
783
852
  date: string;
784
853
  isMobile: boolean;
785
854
  log_id: string;
@@ -0,0 +1,45 @@
1
+ import { Hono } from "hono";
2
+ import { CustomDomainsAdapter } from "@authhero/adapter-interfaces";
3
+ import { Bindings } from "../../types";
4
+ export type AuthenticateControlPlane = (c: {
5
+ req: {
6
+ raw: Request;
7
+ header(name: string): string | undefined;
8
+ url: string;
9
+ };
10
+ env: Bindings;
11
+ }) => Promise<{
12
+ ok: true;
13
+ tenantId?: string;
14
+ } | {
15
+ ok: false;
16
+ reason: string;
17
+ }>;
18
+ export interface CustomDomainsControlPlaneOptions {
19
+ /**
20
+ * The authoritative adapter. On the control plane this is the Cloudflare
21
+ * adapter (`@authhero/cloudflare-adapter`) wrapping the control-plane
22
+ * database, so a write both registers the hostname in the CF-for-SaaS zone
23
+ * and persists the row where the account credentials live.
24
+ */
25
+ customDomains: CustomDomainsAdapter;
26
+ authenticate: AuthenticateControlPlane;
27
+ }
28
+ type TenantVars = {
29
+ tenantId: string;
30
+ };
31
+ /**
32
+ * The authoritative `custom-domains` resource. Tenant shards reach it through
33
+ * `createControlPlaneCustomDomainsAdapter`; it is the only place that can both
34
+ * see every tenant's domains (so `login.acme.com` can be claimed exactly once)
35
+ * and hold the Cloudflare account credentials needed to register the hostname.
36
+ *
37
+ * Every operation is bound to the `tenant_id` claim of the verified token. The
38
+ * scope alone is not authorization: each shard holds it, so a request-supplied
39
+ * tenant id would let any shard read or delete any other tenant's domains.
40
+ */
41
+ export declare function createCustomDomainsControlPlaneApp(options: CustomDomainsControlPlaneOptions): Hono<{
42
+ Bindings: Bindings;
43
+ Variables: TenantVars;
44
+ }>;
45
+ export {};
@@ -3,6 +3,7 @@ import type { ResolvedHost } from "@authhero/proxy";
3
3
  import { CustomDomain, CustomDomainsAdapter, ProxyRoute, ProxyRoutesAdapter } from "@authhero/adapter-interfaces";
4
4
  import { SyncEvent } from "../../helpers/control-plane-sync-events";
5
5
  import { Bindings } from "../../types";
6
+ import { type TenantMembersControlPlaneOptions } from "./tenant-members";
6
7
  export interface ProxyControlPlaneOptions {
7
8
  /**
8
9
  * Cross-tenant host resolver. Typically delegated to a database adapter's
@@ -16,6 +17,16 @@ export interface ProxyControlPlaneOptions {
16
17
  * service binding by inspecting the URL and dispatching accordingly.
17
18
  */
18
19
  jwksFetch?: (url: string) => Promise<Response>;
20
+ /**
21
+ * Optional predicate that widens the set of accepted token issuers beyond
22
+ * `env.ISSUER` and the inbound host — specifically to a deployment's own
23
+ * Workers-for-Platforms tenant subdomains, whose per-tenant control-plane
24
+ * credential `jwksFetch` resolves locally (see #1139). Consulted before any
25
+ * JWKS fetch, so it still constrains where keys are fetched from; return
26
+ * `true` only for issuer hosts you serve. Applies to every mounted resource
27
+ * (custom-domains, tenant-members, sync).
28
+ */
29
+ isTrustedIssuer?: (iss: string) => boolean;
19
30
  /**
20
31
  * Optional handler for `POST /sync` — receives `controlplane.sync.*` events
21
32
  * emitted by tenant shards via `ControlPlaneSyncDestination` and replicates
@@ -24,10 +35,37 @@ export interface ProxyControlPlaneOptions {
24
35
  *
25
36
  * Implementations MUST be idempotent: the outbox retries on transient
26
37
  * failures even after the receiver applied the change.
27
- * `createDefaultApplySyncEvents` wires this to a local data adapter with
28
- * idempotent semantics.
38
+ * `createApplySyncEvents` wires this to a local adapter with idempotent
39
+ * semantics.
40
+ *
41
+ * Custom domains do NOT flow through here — the control plane is
42
+ * authoritative for them (see `customDomains` below); only `proxy_route`
43
+ * replicates upward.
29
44
  */
30
45
  applySyncEvents?: (events: SyncEvent[]) => Promise<void>;
46
+ /**
47
+ * The authoritative custom-domains adapter. When set, mounts the
48
+ * `/custom-domains` resource that tenant shards call through
49
+ * `createControlPlaneCustomDomainsAdapter`.
50
+ *
51
+ * Pass the Cloudflare adapter (`@authhero/cloudflare`) wrapping the
52
+ * control-plane database: registering a CF-for-SaaS hostname needs account
53
+ * credentials that only exist here, and enforcing "one tenant owns
54
+ * login.acme.com" needs a view across every tenant that only exists here.
55
+ */
56
+ customDomains?: CustomDomainsAdapter;
57
+ /**
58
+ * The authoritative tenant-team resource. When set, mounts
59
+ * `/api/v2/proxy/control-plane/tenant-members` — the resource tenant shards
60
+ * call through `createControlPlaneTenantMembersAdapter` to let their admins
61
+ * manage who administers the tenant (control-plane organization membership +
62
+ * org-scoped roles + invitations), rows the shard itself cannot write.
63
+ *
64
+ * Wire the backend to the control-plane database (see
65
+ * `createLocalTenantMembersBackend`); the org is pinned from the verified
66
+ * token, never the request.
67
+ */
68
+ tenantMembers?: TenantMembersControlPlaneOptions;
31
69
  }
32
70
  /**
33
71
  * Returns a Hono app exposing the privileged proxy control-plane endpoint
@@ -46,19 +84,22 @@ export declare function createProxyControlPlaneApp(options: ProxyControlPlaneOpt
46
84
  Bindings: Bindings;
47
85
  }>;
48
86
  export interface CreateApplySyncEventsOptions {
49
- customDomains: CustomDomainsAdapter;
50
- proxyRoutes?: ProxyRoutesAdapter;
87
+ proxyRoutes: ProxyRoutesAdapter;
51
88
  }
52
89
  /**
53
90
  * Build an idempotent `applySyncEvents` implementation backed by a local
54
- * `CustomDomainsAdapter` / `ProxyRoutesAdapter`. Handles the three retry
55
- * shapes the outbox can produce:
91
+ * `ProxyRoutesAdapter`. Handles the three retry shapes the outbox can produce:
56
92
  *
57
93
  * - duplicate `created` (retry after the previous succeeded but
58
94
  * `markProcessed` failed) — falls back to `update`.
59
95
  * - `updated` for a row that doesn't exist locally yet (a `created`
60
96
  * delivery is still in flight or lost) — falls back to `create`.
61
97
  * - `deleted` for a row that's already gone — no-op success.
98
+ *
99
+ * Custom domains are not replicated: the control plane is authoritative for
100
+ * them and tenant shards write through it (see
101
+ * `createControlPlaneCustomDomainsAdapter`), so there is no upward sync to
102
+ * apply.
62
103
  */
63
104
  export declare function createApplySyncEvents(options: CreateApplySyncEventsOptions): (events: SyncEvent[]) => Promise<void>;
64
105
  export type { CustomDomain, ProxyRoute };
@@ -0,0 +1,20 @@
1
+ import { PROXY_RESOLVE_HOST_SCOPE } from "@authhero/proxy";
2
+ /**
3
+ * Scope carried by the tokens `ControlPlaneSyncDestination` mints for
4
+ * `POST /api/v2/proxy/control-plane/sync`.
5
+ */
6
+ export declare const CONTROL_PLANE_SYNC_SCOPE = "controlplane:sync";
7
+ /**
8
+ * Scope required by the authoritative `/custom-domains` resource on the
9
+ * control plane. Tenant shards mint a token with this scope through
10
+ * `createControlPlaneCustomDomainsAdapter`.
11
+ */
12
+ export declare const CONTROL_PLANE_CUSTOM_DOMAINS_SCOPE = "controlplane:custom_domains";
13
+ /**
14
+ * Scope required by the authoritative `/tenant-members` resource on the control
15
+ * plane — a tenant's team (organization membership + org-scoped roles +
16
+ * invitations) that only the control plane can write. Tenant shards mint a
17
+ * token with this scope through `createControlPlaneTenantMembersAdapter`.
18
+ */
19
+ export declare const CONTROL_PLANE_TENANT_MEMBERS_SCOPE = "controlplane:tenant_members";
20
+ export { PROXY_RESOLVE_HOST_SCOPE };
@@ -0,0 +1,37 @@
1
+ import { Hono, type Context } from "hono";
2
+ import { Bindings } from "../../types";
3
+ import { TenantMembersBackend } from "../../tenant-members/types";
4
+ import type { AuthenticateControlPlane } from "./custom-domains";
5
+ type TenantVars = {
6
+ tenantId: string;
7
+ };
8
+ /** Context seen by the control-plane resource: env + the pinned tenant id. */
9
+ type CpCtx = Context<{
10
+ Bindings: Bindings;
11
+ Variables: TenantVars;
12
+ }>;
13
+ export interface TenantMembersControlPlaneOptions {
14
+ /**
15
+ * Build the backend for a request. Bound to the control-plane database
16
+ * (`c.env.data`) and, optionally, an email sender at wire-up time. The tenant
17
+ * is pinned separately from the verified token, so the factory does not need
18
+ * it.
19
+ */
20
+ getBackend: (c: CpCtx) => TenantMembersBackend | Promise<TenantMembersBackend>;
21
+ authenticate: AuthenticateControlPlane;
22
+ }
23
+ /**
24
+ * The authoritative `tenant-members` resource. Tenant shards reach it through
25
+ * `createControlPlaneTenantMembersAdapter`; it manages the control-plane
26
+ * organization that models a tenant's team — rows the shard cannot write.
27
+ *
28
+ * Every operation is bound to the `tenant_id` claim of the verified token. The
29
+ * scope alone is not authorization: each shard holds it, so a request-supplied
30
+ * tenant id would let any shard read or edit any other tenant's team. Mount
31
+ * under `/api/v2/proxy/control-plane/tenant-members`.
32
+ */
33
+ export declare function createTenantMembersControlPlaneApp(options: TenantMembersControlPlaneOptions): Hono<{
34
+ Bindings: Bindings;
35
+ Variables: TenantVars;
36
+ }>;
37
+ export {};
@@ -9,6 +9,13 @@ import { PROXY_RESOLVE_HOST_SCOPE } from "@authhero/proxy";
9
9
  export declare function isAllowedIssuer(iss: string, expected: string): boolean;
10
10
  export type VerifyControlPlaneTokenResult = {
11
11
  ok: true;
12
+ /**
13
+ * The verified `tenant_id` claim, when the token carries one. Service
14
+ * tokens minted by `createServiceTokenCore` always do; a client-credentials
15
+ * token minted for a proxy may not. Callers that act on a tenant's data
16
+ * MUST bind to this rather than to a tenant id taken from the request.
17
+ */
18
+ tenantId?: string;
12
19
  } | {
13
20
  ok: false;
14
21
  reason: string;
@@ -25,8 +32,26 @@ export interface VerifyControlPlaneTokenOptions {
25
32
  * so any host you list here must publish its own JWKS at that path.
26
33
  */
27
34
  expectedIssuers: string[];
28
- /** Required `scope` (space-separated). Defaults to `proxy:resolve_host`. */
29
- requiredScope?: string;
35
+ /**
36
+ * Required `scope` (the token's `scope` claim is space-separated). Pass an
37
+ * array to accept any one of several scopes. Defaults to
38
+ * `proxy:resolve_host`.
39
+ */
40
+ requiredScope?: string | string[];
41
+ /**
42
+ * Optional predicate consulted IN ADDITION to `expectedIssuers`, for issuers
43
+ * that can't be enumerated ahead of time — specifically a deployment's own
44
+ * Workers-for-Platforms tenant subdomains (`https://{tenant}.{host}/`), whose
45
+ * per-tenant control-plane credential the accompanying `jwksFetch` resolves
46
+ * locally (see #1139).
47
+ *
48
+ * Like `expectedIssuers`, it runs BEFORE the JWKS fetch, so it still gates
49
+ * where the verifier will fetch keys from: return `true` only for issuer
50
+ * hosts you actually serve. The signature must still verify against the
51
+ * resolved key, so this does not broaden trust beyond "a caller holding that
52
+ * tenant's registered private key."
53
+ */
54
+ isTrustedIssuer?: (iss: string) => boolean;
30
55
  }
31
56
  /**
32
57
  * Verify a bearer token for the proxy control plane. Returns `{ ok: true }`
@@ -34,8 +59,8 @@ export interface VerifyControlPlaneTokenOptions {
34
59
  * logs only and must not be surfaced to the caller.
35
60
  *
36
61
  * Accepted algs: RS256/384/512, ES256/384/512. The JWK's `alg` must match
37
- * the token header's `alg`. The token must carry the configured required
38
- * scope (`proxy:resolve_host` by default) and an `iss` that strictly equals
62
+ * the token header's `alg`. The token must carry one of the configured
63
+ * required scopes (`proxy:resolve_host` by default) and an `iss` that strictly equals
39
64
  * one of `expectedIssuers` after URL normalization. The JWKS document is
40
65
  * fetched from `<iss>/.well-known/jwks.json` AFTER the `iss` is allow-listed,
41
66
  * so an attacker cannot redirect the verifier to a JWKS they control.
@@ -357,7 +357,7 @@ export declare function initJSXRoute(ctx: Context<{
357
357
  active?: boolean | undefined;
358
358
  } | undefined;
359
359
  signup?: {
360
- status?: "required" | "optional" | "disabled" | undefined;
360
+ status?: "optional" | "required" | "disabled" | undefined;
361
361
  verification?: {
362
362
  active?: boolean | undefined;
363
363
  } | undefined;
@@ -374,7 +374,7 @@ export declare function initJSXRoute(ctx: Context<{
374
374
  active?: boolean | undefined;
375
375
  } | undefined;
376
376
  signup?: {
377
- status?: "required" | "optional" | "disabled" | undefined;
377
+ status?: "optional" | "required" | "disabled" | undefined;
378
378
  } | undefined;
379
379
  validation?: {
380
380
  max_length?: number | undefined;
@@ -391,7 +391,7 @@ export declare function initJSXRoute(ctx: Context<{
391
391
  active?: boolean | undefined;
392
392
  } | undefined;
393
393
  signup?: {
394
- status?: "required" | "optional" | "disabled" | undefined;
394
+ status?: "optional" | "required" | "disabled" | undefined;
395
395
  } | undefined;
396
396
  } | undefined;
397
397
  } | undefined;
@@ -456,7 +456,7 @@ export declare function initJSXRoute(ctx: Context<{
456
456
  custom_login_page_preview?: string | undefined;
457
457
  form_template?: string | undefined;
458
458
  addons?: Record<string, any> | undefined;
459
- token_endpoint_auth_method?: "none" | "private_key_jwt" | "client_secret_post" | "client_secret_basic" | "client_secret_jwt" | undefined;
459
+ token_endpoint_auth_method?: "client_secret_post" | "client_secret_basic" | "none" | "client_secret_jwt" | "private_key_jwt" | undefined;
460
460
  client_metadata?: Record<string, string> | undefined;
461
461
  hide_sign_up_disabled_error?: boolean | undefined;
462
462
  mobile?: Record<string, any> | undefined;
@@ -1086,7 +1086,7 @@ export declare function initJSXRouteWithSession(ctx: Context<{
1086
1086
  active?: boolean | undefined;
1087
1087
  } | undefined;
1088
1088
  signup?: {
1089
- status?: "required" | "optional" | "disabled" | undefined;
1089
+ status?: "optional" | "required" | "disabled" | undefined;
1090
1090
  verification?: {
1091
1091
  active?: boolean | undefined;
1092
1092
  } | undefined;
@@ -1103,7 +1103,7 @@ export declare function initJSXRouteWithSession(ctx: Context<{
1103
1103
  active?: boolean | undefined;
1104
1104
  } | undefined;
1105
1105
  signup?: {
1106
- status?: "required" | "optional" | "disabled" | undefined;
1106
+ status?: "optional" | "required" | "disabled" | undefined;
1107
1107
  } | undefined;
1108
1108
  validation?: {
1109
1109
  max_length?: number | undefined;
@@ -1120,7 +1120,7 @@ export declare function initJSXRouteWithSession(ctx: Context<{
1120
1120
  active?: boolean | undefined;
1121
1121
  } | undefined;
1122
1122
  signup?: {
1123
- status?: "required" | "optional" | "disabled" | undefined;
1123
+ status?: "optional" | "required" | "disabled" | undefined;
1124
1124
  } | undefined;
1125
1125
  } | undefined;
1126
1126
  } | undefined;
@@ -1185,7 +1185,7 @@ export declare function initJSXRouteWithSession(ctx: Context<{
1185
1185
  custom_login_page_preview?: string | undefined;
1186
1186
  form_template?: string | undefined;
1187
1187
  addons?: Record<string, any> | undefined;
1188
- token_endpoint_auth_method?: "none" | "private_key_jwt" | "client_secret_post" | "client_secret_basic" | "client_secret_jwt" | undefined;
1188
+ token_endpoint_auth_method?: "client_secret_post" | "client_secret_basic" | "none" | "client_secret_jwt" | "private_key_jwt" | undefined;
1189
1189
  client_metadata?: Record<string, string> | undefined;
1190
1190
  hide_sign_up_disabled_error?: boolean | undefined;
1191
1191
  mobile?: Record<string, any> | undefined;
@@ -27,7 +27,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
27
27
  output: {
28
28
  screen: {
29
29
  action: string;
30
- method: "POST" | "GET";
30
+ method: "GET" | "POST";
31
31
  components: {
32
32
  id: string;
33
33
  type: string;
@@ -43,7 +43,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
43
43
  hint?: string | undefined;
44
44
  messages?: {
45
45
  text: string;
46
- type: "error" | "success" | "info" | "warning";
46
+ type: "success" | "error" | "info" | "warning";
47
47
  id?: number | undefined;
48
48
  }[] | undefined;
49
49
  }[];
@@ -51,7 +51,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
51
51
  description?: string | undefined;
52
52
  messages?: {
53
53
  text: string;
54
- type: "error" | "success" | "info" | "warning";
54
+ type: "success" | "error" | "info" | "warning";
55
55
  id?: number | undefined;
56
56
  }[] | undefined;
57
57
  links?: {
@@ -107,7 +107,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
107
107
  output: {
108
108
  screen: {
109
109
  action: string;
110
- method: "POST" | "GET";
110
+ method: "GET" | "POST";
111
111
  components: {
112
112
  id: string;
113
113
  type: string;
@@ -123,7 +123,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
123
123
  hint?: string | undefined;
124
124
  messages?: {
125
125
  text: string;
126
- type: "error" | "success" | "info" | "warning";
126
+ type: "success" | "error" | "info" | "warning";
127
127
  id?: number | undefined;
128
128
  }[] | undefined;
129
129
  }[];
@@ -131,7 +131,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
131
131
  description?: string | undefined;
132
132
  messages?: {
133
133
  text: string;
134
- type: "error" | "success" | "info" | "warning";
134
+ type: "success" | "error" | "info" | "warning";
135
135
  id?: number | undefined;
136
136
  }[] | undefined;
137
137
  links?: {
@@ -204,7 +204,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
204
204
  output: {
205
205
  screen: {
206
206
  action: string;
207
- method: "POST" | "GET";
207
+ method: "GET" | "POST";
208
208
  components: {
209
209
  id: string;
210
210
  type: string;
@@ -220,7 +220,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
220
220
  hint?: string | undefined;
221
221
  messages?: {
222
222
  text: string;
223
- type: "error" | "success" | "info" | "warning";
223
+ type: "success" | "error" | "info" | "warning";
224
224
  id?: number | undefined;
225
225
  }[] | undefined;
226
226
  }[];
@@ -228,7 +228,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
228
228
  description?: string | undefined;
229
229
  messages?: {
230
230
  text: string;
231
- type: "error" | "success" | "info" | "warning";
231
+ type: "success" | "error" | "info" | "warning";
232
232
  id?: number | undefined;
233
233
  }[] | undefined;
234
234
  links?: {
@@ -319,7 +319,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
319
319
  output: {
320
320
  screen: {
321
321
  action: string;
322
- method: "POST" | "GET";
322
+ method: "GET" | "POST";
323
323
  components: {
324
324
  id: string;
325
325
  type: string;
@@ -335,7 +335,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
335
335
  hint?: string | undefined;
336
336
  messages?: {
337
337
  text: string;
338
- type: "error" | "success" | "info" | "warning";
338
+ type: "success" | "error" | "info" | "warning";
339
339
  id?: number | undefined;
340
340
  }[] | undefined;
341
341
  }[];
@@ -343,7 +343,7 @@ export declare const flowApiRoutes: OpenAPIHono<{
343
343
  description?: string | undefined;
344
344
  messages?: {
345
345
  text: string;
346
- type: "error" | "success" | "info" | "warning";
346
+ type: "success" | "error" | "info" | "warning";
347
347
  id?: number | undefined;
348
348
  }[] | undefined;
349
349
  links?: {
@@ -30,7 +30,7 @@ export declare const identifierRoutes: OpenAPIHono<{
30
30
  } & {
31
31
  form: {
32
32
  username: string;
33
- login_selection?: "password" | "code" | undefined;
33
+ login_selection?: "code" | "password" | undefined;
34
34
  };
35
35
  };
36
36
  output: {};
@@ -44,7 +44,7 @@ export declare const identifierRoutes: OpenAPIHono<{
44
44
  } & {
45
45
  form: {
46
46
  username: string;
47
- login_selection?: "password" | "code" | undefined;
47
+ login_selection?: "code" | "password" | undefined;
48
48
  };
49
49
  };
50
50
  output: {};
@@ -515,7 +515,7 @@ export default function create(config: AuthHeroConfig): OpenAPIHono<{
515
515
  } & {
516
516
  form: {
517
517
  username: string;
518
- login_selection?: "password" | "code" | undefined;
518
+ login_selection?: "code" | "password" | undefined;
519
519
  };
520
520
  };
521
521
  output: {};
@@ -529,7 +529,7 @@ export default function create(config: AuthHeroConfig): OpenAPIHono<{
529
529
  } & {
530
530
  form: {
531
531
  username: string;
532
- login_selection?: "password" | "code" | undefined;
532
+ login_selection?: "code" | "password" | undefined;
533
533
  };
534
534
  };
535
535
  output: {};