@objectstack/plugin-auth 11.0.0 → 11.1.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/dist/index.d.mts +194 -14
- package/dist/index.d.ts +194 -14
- package/dist/index.js +725 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +720 -28
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/dist/index.d.mts
CHANGED
|
@@ -286,6 +286,40 @@ interface AuthManagerOptions extends Partial<AuthConfig> {
|
|
|
286
286
|
* Reuses better-auth's native hash/verify — no bespoke crypto.
|
|
287
287
|
*/
|
|
288
288
|
passwordHistoryCount?: number;
|
|
289
|
+
/**
|
|
290
|
+
* ADR-0069 D1 — password expiry (days). When > 0, an authenticated user whose
|
|
291
|
+
* `sys_user.password_changed_at` is older than this is gated out of protected
|
|
292
|
+
* resources (`PASSWORD_EXPIRED`) until they change their password. Computed in
|
|
293
|
+
* `customSession` (→ `user.authGate`) and enforced at the transport seam. 0 =
|
|
294
|
+
* off. A null `password_changed_at` never expires (existing users on upgrade).
|
|
295
|
+
*/
|
|
296
|
+
passwordExpiryDays?: number;
|
|
297
|
+
/**
|
|
298
|
+
* ADR-0069 D3 — enforced MFA. When true, an authenticated user without TOTP
|
|
299
|
+
* enrolled (`sys_user.two_factor_enabled`) is gated out of protected resources
|
|
300
|
+
* (`MFA_REQUIRED`) once their grace window elapses, until they enroll. Shares
|
|
301
|
+
* the `customSession` → `user.authGate` seam with password expiry.
|
|
302
|
+
*/
|
|
303
|
+
mfaRequired?: boolean;
|
|
304
|
+
/** Days a user may defer MFA enrollment before the hard block. Default 7. */
|
|
305
|
+
mfaGracePeriodDays?: number;
|
|
306
|
+
/**
|
|
307
|
+
* ADR-0069 D4 — session controls. Enforced in `customSession` (idle/absolute)
|
|
308
|
+
* and the sign-in hook (concurrent). 0 = off for each. A revoked session is
|
|
309
|
+
* expired in place (`sys_session.expires_at` past + `revoked_at`/`revoke_reason`)
|
|
310
|
+
* so better-auth returns no session on the next request (→ 401 → re-login).
|
|
311
|
+
*/
|
|
312
|
+
sessionIdleTimeoutMinutes?: number;
|
|
313
|
+
sessionAbsoluteMaxHours?: number;
|
|
314
|
+
maxConcurrentSessions?: number;
|
|
315
|
+
/**
|
|
316
|
+
* ADR-0069 D5 — network gating. When non-empty, auth requests (sign-in,
|
|
317
|
+
* session) from a client IP outside these CIDR / exact ranges are rejected
|
|
318
|
+
* with `IP_NOT_ALLOWED` at the auth-route middleware. Requires a trusted proxy
|
|
319
|
+
* to set `x-forwarded-for` / `cf-connecting-ip`; fails OPEN when the client IP
|
|
320
|
+
* can't be determined (so a missing proxy header is a no-op, not a lockout).
|
|
321
|
+
*/
|
|
322
|
+
allowedIpRanges?: string[];
|
|
289
323
|
/**
|
|
290
324
|
* ADR-0069 D2 — better-auth-native per-IP rate limiting, passed through to
|
|
291
325
|
* better-auth's core `rateLimit`. The settings bind tightens `customRules`
|
|
@@ -294,21 +328,13 @@ interface AuthManagerOptions extends Partial<AuthConfig> {
|
|
|
294
328
|
*/
|
|
295
329
|
rateLimit?: BetterAuthOptions['rateLimit'];
|
|
296
330
|
}
|
|
297
|
-
/**
|
|
298
|
-
|
|
299
|
-
*
|
|
300
|
-
* Wraps better-auth and provides authentication services for ObjectStack.
|
|
301
|
-
* Supports multiple authentication methods:
|
|
302
|
-
* - Email/password
|
|
303
|
-
* - OAuth providers (Google, GitHub, etc.)
|
|
304
|
-
* - Magic links
|
|
305
|
-
* - Two-factor authentication
|
|
306
|
-
* - Passkeys
|
|
307
|
-
* - Organization/teams
|
|
308
|
-
*/
|
|
331
|
+
/** ADR-0069 D5 — does `ip` match `range` (IPv4 CIDR `a.b.c.d/n`, or exact IP)? */
|
|
332
|
+
declare function ipMatchesRange(ip: string, range: string): boolean;
|
|
309
333
|
declare class AuthManager {
|
|
310
334
|
private auth;
|
|
311
335
|
private config;
|
|
336
|
+
private _orgMfaCache;
|
|
337
|
+
private _orgMfaRefreshing;
|
|
312
338
|
/**
|
|
313
339
|
* Result of the dev-only admin seed (set by `AuthPlugin.maybeSeedDevAdmin`
|
|
314
340
|
* when it provisions the well-known admin on an empty DB). The `serve`
|
|
@@ -501,8 +527,21 @@ declare class AuthManager {
|
|
|
501
527
|
* in `buildPlugins()` (`ssoFromEnv ?? pluginConfig.sso ?? false`) so the
|
|
502
528
|
* advertised capability can never disagree with the actual `/sign-in/sso`
|
|
503
529
|
* route. `OS_SSO_ENABLED` (when set) wins over the config-file setting.
|
|
530
|
+
* Public so `AuthPlugin` can gate the Setup-nav "SSO Providers" entry on it
|
|
531
|
+
* (captures both self-host `OS_SSO_ENABLED` and the cloud per-env
|
|
532
|
+
* `planAllowsSso` config, since that arrives via `plugins.sso`).
|
|
504
533
|
*/
|
|
505
|
-
|
|
534
|
+
isSsoWired(): boolean;
|
|
535
|
+
/**
|
|
536
|
+
* Whether opt-in DNS domain-verification (ADR-0024 ②) is wired — i.e. the
|
|
537
|
+
* `/sso/request-domain-verification` + `/sso/verify-domain` endpoints are
|
|
538
|
+
* mounted (and the hard "domain must be verified to log in" gate is active).
|
|
539
|
+
* Resolved with the EXACT logic `buildPluginList` uses for the `sso()`
|
|
540
|
+
* `domainVerification.enabled` option, so the bridge can return a clear
|
|
541
|
+
* "not enabled for this environment" instead of a bare 404 when off.
|
|
542
|
+
* Implies `isSsoWired()` (the sso plugin must be loaded to honor it).
|
|
543
|
+
*/
|
|
544
|
+
isSsoDomainVerificationEnabled(): boolean;
|
|
506
545
|
/**
|
|
507
546
|
* Whether enterprise SSO is actually *usable*, not merely wired: the plugin
|
|
508
547
|
* is on AND at least one `sys_sso_provider` row exists. Per-email domain→IdP
|
|
@@ -590,6 +629,33 @@ declare class AuthManager {
|
|
|
590
629
|
* `PASSWORD_POLICY_VIOLATION` when fewer than `passwordMinClasses` are used.
|
|
591
630
|
*/
|
|
592
631
|
private assertPasswordComplexity;
|
|
632
|
+
/**
|
|
633
|
+
* ADR-0069 — is any authentication-policy gate enabled? Cheap, synchronous;
|
|
634
|
+
* lets the transport seams skip session lookups entirely when off (the
|
|
635
|
+
* default), keeping the gate zero-overhead until an admin opts in.
|
|
636
|
+
*/
|
|
637
|
+
isAuthGateActive(): boolean;
|
|
638
|
+
/**
|
|
639
|
+
* ADR-0069 — refresh the "any org requires MFA" cache in the background when
|
|
640
|
+
* stale (60s TTL). Fire-and-forget: a brand-new per-org requirement activates
|
|
641
|
+
* the gate on the next request, never blocking this one. No-op when global MFA
|
|
642
|
+
* is already on (the gate is active regardless).
|
|
643
|
+
*/
|
|
644
|
+
private refreshOrgMfaCacheIfStale;
|
|
645
|
+
/**
|
|
646
|
+
* ADR-0069 — compute the auth-policy gate posture for a session. Returns an
|
|
647
|
+
* `{ code, message }` when the user is currently blocked (e.g. password
|
|
648
|
+
* expired), else undefined. No-op (and no DB read) when no gate feature is
|
|
649
|
+
* enabled. Fails OPEN on any lookup error — a transient hiccup must never lock
|
|
650
|
+
* a compliant user out.
|
|
651
|
+
*/
|
|
652
|
+
private computeAuthGate;
|
|
653
|
+
/**
|
|
654
|
+
* ADR-0069 D1 — stamp `sys_user.password_changed_at = now` after a password is
|
|
655
|
+
* set (sign-up / change / reset). Best-effort; never throws. Written as a Date
|
|
656
|
+
* (never epoch-ms) per ADR-0074.
|
|
657
|
+
*/
|
|
658
|
+
private stampPasswordChangedAt;
|
|
593
659
|
/**
|
|
594
660
|
* ADR-0069 D1 — parse the bounded `previous_password_hashes` JSON column into
|
|
595
661
|
* a string[] of hashes, tolerating null / malformed values.
|
|
@@ -637,6 +703,27 @@ declare class AuthManager {
|
|
|
637
703
|
* engine is wired or the user does not exist.
|
|
638
704
|
*/
|
|
639
705
|
unlockUser(userId: string): Promise<boolean>;
|
|
706
|
+
/**
|
|
707
|
+
* ADR-0069 D4 — idle / absolute session enforcement, run per request from
|
|
708
|
+
* `customSession`. No-op when both are off. Revokes (expires in place +
|
|
709
|
+
* stamps revoked_at/revoke_reason) when a limit is exceeded so better-auth
|
|
710
|
+
* returns no session on the NEXT request; otherwise touches `last_activity_at`
|
|
711
|
+
* (throttled to once a minute). Best-effort — never throws.
|
|
712
|
+
*/
|
|
713
|
+
private enforceSessionControls;
|
|
714
|
+
/**
|
|
715
|
+
* ADR-0069 D4 — concurrent-session cap, run from the sign-in after-hook.
|
|
716
|
+
* Keeps the newest `maxConcurrentSessions` live sessions for the user and
|
|
717
|
+
* revokes the rest (oldest first). No-op when off. Best-effort.
|
|
718
|
+
*/
|
|
719
|
+
private enforceConcurrentCap;
|
|
720
|
+
/**
|
|
721
|
+
* ADR-0069 D5 — is `ip` within the configured allow-list? True (allow) when no
|
|
722
|
+
* ranges are configured, OR when the IP can't be determined (fail-open so a
|
|
723
|
+
* misconfigured proxy never locks everyone out — an admin enabling this must
|
|
724
|
+
* ensure forwarded headers are trusted). Supports IPv4 CIDR + exact IPv4/IPv6.
|
|
725
|
+
*/
|
|
726
|
+
isClientIpAllowed(ip: string | undefined): boolean;
|
|
640
727
|
/**
|
|
641
728
|
* Returns the data engine wired into this auth manager. Used by route
|
|
642
729
|
* handlers (e.g. bootstrap-status) that need to query identity tables
|
|
@@ -696,6 +783,98 @@ interface SetInitialPasswordResult {
|
|
|
696
783
|
*/
|
|
697
784
|
declare function runSetInitialPassword(authApi: SetPasswordCapableApi, request: Request): Promise<SetInitialPasswordResult>;
|
|
698
785
|
|
|
786
|
+
/**
|
|
787
|
+
* Shared `register-sso-provider` (form) handler.
|
|
788
|
+
*
|
|
789
|
+
* `@better-auth/sso`'s `POST /sso/register` expects the OIDC protocol fields
|
|
790
|
+
* NESTED under `oidcConfig` ({ clientId, clientSecret, discoveryEndpoint,
|
|
791
|
+
* scopes, mapping }). The `sys_sso_provider` `register_sso_provider` UI action
|
|
792
|
+
* collects FLAT form fields (the action param schema has no nested-path
|
|
793
|
+
* support), so posting them straight to `/sso/register` drops
|
|
794
|
+
* clientId/clientSecret at the top level (Zod-stripped) and persists an
|
|
795
|
+
* unusable `oidc_config = null` provider that can never complete a login
|
|
796
|
+
* (ADR-0024).
|
|
797
|
+
*
|
|
798
|
+
* This helper reshapes the flat form body into the nested shape and
|
|
799
|
+
* RE-DISPATCHES it through the real `/sso/register` endpoint (via the
|
|
800
|
+
* better-auth universal handler passed in) so the admin gate, the
|
|
801
|
+
* public-routable `trustedOrigins` allowance, discovery hydration, and secret
|
|
802
|
+
* handling all still run — no logic is duplicated. It is the single source of
|
|
803
|
+
* truth for the two mount points that must stay in lockstep: the full
|
|
804
|
+
* `AuthPlugin` (self-host / OSS host kernel) and the cloud `AuthProxyPlugin`
|
|
805
|
+
* (per-environment runtime) — mirroring `runSetInitialPassword`.
|
|
806
|
+
*/
|
|
807
|
+
interface RegisterSsoFormResult {
|
|
808
|
+
/** HTTP status to return to the caller. */
|
|
809
|
+
status: number;
|
|
810
|
+
/** JSON body; mirrors the `{ success, data?, error? }` envelope the client parses. */
|
|
811
|
+
body: {
|
|
812
|
+
success: boolean;
|
|
813
|
+
data?: {
|
|
814
|
+
providerId: string;
|
|
815
|
+
};
|
|
816
|
+
error?: {
|
|
817
|
+
code: string;
|
|
818
|
+
message: string;
|
|
819
|
+
};
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
/** A better-auth universal handler: `(request) => Response`. */
|
|
823
|
+
type AuthRequestHandler = (request: Request) => Promise<Response>;
|
|
824
|
+
/**
|
|
825
|
+
* Reshape a flat SSO-provider registration form body and register it.
|
|
826
|
+
*
|
|
827
|
+
* @param handle the better-auth universal handler (`AuthManager.handleRequest`
|
|
828
|
+
* on the host kernel, or the resolved per-env handler in the
|
|
829
|
+
* cloud proxy). Used to re-dispatch the nested body to the real
|
|
830
|
+
* `/sso/register` route so all of its gates run.
|
|
831
|
+
* @param request the raw Web `Request` — its headers carry the caller's session
|
|
832
|
+
* cookie / bearer + Origin; its body carries the flat form
|
|
833
|
+
* fields ({ providerId, issuer, domain, clientId, clientSecret,
|
|
834
|
+
* discoveryEndpoint?, scopes?, mapId?, mapEmail?, mapName? }).
|
|
835
|
+
*/
|
|
836
|
+
declare function runRegisterSsoProviderFromForm(handle: AuthRequestHandler, request: Request): Promise<RegisterSsoFormResult>;
|
|
837
|
+
/**
|
|
838
|
+
* ADR-0069 P3 — SAML 2.0 sibling of {@link runRegisterSsoProviderFromForm}.
|
|
839
|
+
*
|
|
840
|
+
* `@better-auth/sso` (samlify-backed) registers a SAML IdP via the SAME
|
|
841
|
+
* `/sso/register` endpoint, with the protocol fields nested under `samlConfig`
|
|
842
|
+
* ({ entryPoint, cert, callbackUrl, identifierFormat? }) instead of `oidcConfig`.
|
|
843
|
+
* The UI action collects FLAT fields; this helper reshapes them, derives the
|
|
844
|
+
* per-provider ACS callback URL (`/sso/saml2/sp/acs/<providerId>`), and
|
|
845
|
+
* re-dispatches through `/sso/register` so the admin gate + provisioning run.
|
|
846
|
+
* Returns the SP ACS + metadata URLs the admin must configure on the IdP.
|
|
847
|
+
*/
|
|
848
|
+
declare function runRegisterSamlProviderFromForm(handle: AuthRequestHandler, request: Request): Promise<RegisterSsoFormResult & {
|
|
849
|
+
body: RegisterSsoFormResult['body'] & {
|
|
850
|
+
acsUrl?: string;
|
|
851
|
+
spMetadataUrl?: string;
|
|
852
|
+
};
|
|
853
|
+
}>;
|
|
854
|
+
/**
|
|
855
|
+
* Request a DNS-TXT domain-verification challenge for a registered provider and
|
|
856
|
+
* return the ready-to-paste DNS record (for a one-shot `resultDialog`).
|
|
857
|
+
*
|
|
858
|
+
* Body: `{ providerId, domain? }` (domain only shapes the displayed record name).
|
|
859
|
+
*/
|
|
860
|
+
declare function runRequestDomainVerification(handle: AuthRequestHandler, request: Request): Promise<RegisterSsoFormResult & {
|
|
861
|
+
body: RegisterSsoFormResult['body'] & {
|
|
862
|
+
data?: any;
|
|
863
|
+
};
|
|
864
|
+
}>;
|
|
865
|
+
/**
|
|
866
|
+
* Verify a provider's domain ownership (re-checks the DNS-TXT record). Reshapes
|
|
867
|
+
* @better-auth/sso's empty `204` / `502` into a `{ success, data:{ message } }`
|
|
868
|
+
* envelope so the action surfaces a clear toast.
|
|
869
|
+
*
|
|
870
|
+
* Body: `{ providerId }`.
|
|
871
|
+
*/
|
|
872
|
+
declare function runVerifyDomain(handle: AuthRequestHandler, request: Request): Promise<RegisterSsoFormResult & {
|
|
873
|
+
body: RegisterSsoFormResult['body'] & {
|
|
874
|
+
data?: any;
|
|
875
|
+
};
|
|
876
|
+
}>;
|
|
877
|
+
|
|
699
878
|
/**
|
|
700
879
|
* Mapping from better-auth model names to ObjectStack protocol object names.
|
|
701
880
|
*
|
|
@@ -1478,6 +1657,7 @@ declare const AUTH_SSO_PROVIDER_SCHEMA: {
|
|
|
1478
1657
|
readonly samlConfig: "saml_config";
|
|
1479
1658
|
readonly userId: "user_id";
|
|
1480
1659
|
readonly organizationId: "organization_id";
|
|
1660
|
+
readonly domainVerified: "domain_verified";
|
|
1481
1661
|
};
|
|
1482
1662
|
};
|
|
1483
1663
|
/**
|
|
@@ -1530,4 +1710,4 @@ declare function buildDeviceAuthorizationPluginSchema(): {
|
|
|
1530
1710
|
};
|
|
1531
1711
|
};
|
|
1532
1712
|
|
|
1533
|
-
export { AUTH_ACCOUNT_CONFIG, AUTH_ADMIN_SESSION_FIELDS, AUTH_ADMIN_USER_FIELDS, AUTH_DEVICE_CODE_SCHEMA, AUTH_INVITATION_SCHEMA, AUTH_JWKS_SCHEMA, AUTH_MEMBER_SCHEMA, AUTH_MODEL_TO_PROTOCOL, AUTH_OAUTH_ACCESS_TOKEN_SCHEMA, AUTH_OAUTH_APPLICATION_SCHEMA, AUTH_OAUTH_CLIENT_SCHEMA, AUTH_OAUTH_CONSENT_SCHEMA, AUTH_OAUTH_REFRESH_TOKEN_SCHEMA, AUTH_ORGANIZATION_SCHEMA, AUTH_ORG_SESSION_FIELDS, AUTH_SCIM_PROVIDER_SCHEMA, AUTH_SESSION_CONFIG, AUTH_SSO_PROVIDER_SCHEMA, AUTH_TEAM_MEMBER_SCHEMA, AUTH_TEAM_SCHEMA, AUTH_TWO_FACTOR_SCHEMA, AUTH_TWO_FACTOR_USER_FIELDS, AUTH_USER_CONFIG, AUTH_VERIFICATION_CONFIG, AuthManager, type AuthManagerOptions, AuthPlugin, type AuthPluginOptions, type SetInitialPasswordResult, type SetPasswordCapableApi, buildAdminPluginSchema, buildDeviceAuthorizationPluginSchema, buildJwtPluginSchema, buildOauthProviderPluginSchema, buildOidcProviderPluginSchema, buildOrganizationPluginSchema, buildTwoFactorPluginSchema, createObjectQLAdapter, createObjectQLAdapterFactory, resolveProtocolName, runSetInitialPassword, withSystemReadContext };
|
|
1713
|
+
export { AUTH_ACCOUNT_CONFIG, AUTH_ADMIN_SESSION_FIELDS, AUTH_ADMIN_USER_FIELDS, AUTH_DEVICE_CODE_SCHEMA, AUTH_INVITATION_SCHEMA, AUTH_JWKS_SCHEMA, AUTH_MEMBER_SCHEMA, AUTH_MODEL_TO_PROTOCOL, AUTH_OAUTH_ACCESS_TOKEN_SCHEMA, AUTH_OAUTH_APPLICATION_SCHEMA, AUTH_OAUTH_CLIENT_SCHEMA, AUTH_OAUTH_CONSENT_SCHEMA, AUTH_OAUTH_REFRESH_TOKEN_SCHEMA, AUTH_ORGANIZATION_SCHEMA, AUTH_ORG_SESSION_FIELDS, AUTH_SCIM_PROVIDER_SCHEMA, AUTH_SESSION_CONFIG, AUTH_SSO_PROVIDER_SCHEMA, AUTH_TEAM_MEMBER_SCHEMA, AUTH_TEAM_SCHEMA, AUTH_TWO_FACTOR_SCHEMA, AUTH_TWO_FACTOR_USER_FIELDS, AUTH_USER_CONFIG, AUTH_VERIFICATION_CONFIG, AuthManager, type AuthManagerOptions, AuthPlugin, type AuthPluginOptions, type AuthRequestHandler, type RegisterSsoFormResult, type SetInitialPasswordResult, type SetPasswordCapableApi, buildAdminPluginSchema, buildDeviceAuthorizationPluginSchema, buildJwtPluginSchema, buildOauthProviderPluginSchema, buildOidcProviderPluginSchema, buildOrganizationPluginSchema, buildTwoFactorPluginSchema, createObjectQLAdapter, createObjectQLAdapterFactory, ipMatchesRange, resolveProtocolName, runRegisterSamlProviderFromForm, runRegisterSsoProviderFromForm, runRequestDomainVerification, runSetInitialPassword, runVerifyDomain, withSystemReadContext };
|
package/dist/index.d.ts
CHANGED
|
@@ -286,6 +286,40 @@ interface AuthManagerOptions extends Partial<AuthConfig> {
|
|
|
286
286
|
* Reuses better-auth's native hash/verify — no bespoke crypto.
|
|
287
287
|
*/
|
|
288
288
|
passwordHistoryCount?: number;
|
|
289
|
+
/**
|
|
290
|
+
* ADR-0069 D1 — password expiry (days). When > 0, an authenticated user whose
|
|
291
|
+
* `sys_user.password_changed_at` is older than this is gated out of protected
|
|
292
|
+
* resources (`PASSWORD_EXPIRED`) until they change their password. Computed in
|
|
293
|
+
* `customSession` (→ `user.authGate`) and enforced at the transport seam. 0 =
|
|
294
|
+
* off. A null `password_changed_at` never expires (existing users on upgrade).
|
|
295
|
+
*/
|
|
296
|
+
passwordExpiryDays?: number;
|
|
297
|
+
/**
|
|
298
|
+
* ADR-0069 D3 — enforced MFA. When true, an authenticated user without TOTP
|
|
299
|
+
* enrolled (`sys_user.two_factor_enabled`) is gated out of protected resources
|
|
300
|
+
* (`MFA_REQUIRED`) once their grace window elapses, until they enroll. Shares
|
|
301
|
+
* the `customSession` → `user.authGate` seam with password expiry.
|
|
302
|
+
*/
|
|
303
|
+
mfaRequired?: boolean;
|
|
304
|
+
/** Days a user may defer MFA enrollment before the hard block. Default 7. */
|
|
305
|
+
mfaGracePeriodDays?: number;
|
|
306
|
+
/**
|
|
307
|
+
* ADR-0069 D4 — session controls. Enforced in `customSession` (idle/absolute)
|
|
308
|
+
* and the sign-in hook (concurrent). 0 = off for each. A revoked session is
|
|
309
|
+
* expired in place (`sys_session.expires_at` past + `revoked_at`/`revoke_reason`)
|
|
310
|
+
* so better-auth returns no session on the next request (→ 401 → re-login).
|
|
311
|
+
*/
|
|
312
|
+
sessionIdleTimeoutMinutes?: number;
|
|
313
|
+
sessionAbsoluteMaxHours?: number;
|
|
314
|
+
maxConcurrentSessions?: number;
|
|
315
|
+
/**
|
|
316
|
+
* ADR-0069 D5 — network gating. When non-empty, auth requests (sign-in,
|
|
317
|
+
* session) from a client IP outside these CIDR / exact ranges are rejected
|
|
318
|
+
* with `IP_NOT_ALLOWED` at the auth-route middleware. Requires a trusted proxy
|
|
319
|
+
* to set `x-forwarded-for` / `cf-connecting-ip`; fails OPEN when the client IP
|
|
320
|
+
* can't be determined (so a missing proxy header is a no-op, not a lockout).
|
|
321
|
+
*/
|
|
322
|
+
allowedIpRanges?: string[];
|
|
289
323
|
/**
|
|
290
324
|
* ADR-0069 D2 — better-auth-native per-IP rate limiting, passed through to
|
|
291
325
|
* better-auth's core `rateLimit`. The settings bind tightens `customRules`
|
|
@@ -294,21 +328,13 @@ interface AuthManagerOptions extends Partial<AuthConfig> {
|
|
|
294
328
|
*/
|
|
295
329
|
rateLimit?: BetterAuthOptions['rateLimit'];
|
|
296
330
|
}
|
|
297
|
-
/**
|
|
298
|
-
|
|
299
|
-
*
|
|
300
|
-
* Wraps better-auth and provides authentication services for ObjectStack.
|
|
301
|
-
* Supports multiple authentication methods:
|
|
302
|
-
* - Email/password
|
|
303
|
-
* - OAuth providers (Google, GitHub, etc.)
|
|
304
|
-
* - Magic links
|
|
305
|
-
* - Two-factor authentication
|
|
306
|
-
* - Passkeys
|
|
307
|
-
* - Organization/teams
|
|
308
|
-
*/
|
|
331
|
+
/** ADR-0069 D5 — does `ip` match `range` (IPv4 CIDR `a.b.c.d/n`, or exact IP)? */
|
|
332
|
+
declare function ipMatchesRange(ip: string, range: string): boolean;
|
|
309
333
|
declare class AuthManager {
|
|
310
334
|
private auth;
|
|
311
335
|
private config;
|
|
336
|
+
private _orgMfaCache;
|
|
337
|
+
private _orgMfaRefreshing;
|
|
312
338
|
/**
|
|
313
339
|
* Result of the dev-only admin seed (set by `AuthPlugin.maybeSeedDevAdmin`
|
|
314
340
|
* when it provisions the well-known admin on an empty DB). The `serve`
|
|
@@ -501,8 +527,21 @@ declare class AuthManager {
|
|
|
501
527
|
* in `buildPlugins()` (`ssoFromEnv ?? pluginConfig.sso ?? false`) so the
|
|
502
528
|
* advertised capability can never disagree with the actual `/sign-in/sso`
|
|
503
529
|
* route. `OS_SSO_ENABLED` (when set) wins over the config-file setting.
|
|
530
|
+
* Public so `AuthPlugin` can gate the Setup-nav "SSO Providers" entry on it
|
|
531
|
+
* (captures both self-host `OS_SSO_ENABLED` and the cloud per-env
|
|
532
|
+
* `planAllowsSso` config, since that arrives via `plugins.sso`).
|
|
504
533
|
*/
|
|
505
|
-
|
|
534
|
+
isSsoWired(): boolean;
|
|
535
|
+
/**
|
|
536
|
+
* Whether opt-in DNS domain-verification (ADR-0024 ②) is wired — i.e. the
|
|
537
|
+
* `/sso/request-domain-verification` + `/sso/verify-domain` endpoints are
|
|
538
|
+
* mounted (and the hard "domain must be verified to log in" gate is active).
|
|
539
|
+
* Resolved with the EXACT logic `buildPluginList` uses for the `sso()`
|
|
540
|
+
* `domainVerification.enabled` option, so the bridge can return a clear
|
|
541
|
+
* "not enabled for this environment" instead of a bare 404 when off.
|
|
542
|
+
* Implies `isSsoWired()` (the sso plugin must be loaded to honor it).
|
|
543
|
+
*/
|
|
544
|
+
isSsoDomainVerificationEnabled(): boolean;
|
|
506
545
|
/**
|
|
507
546
|
* Whether enterprise SSO is actually *usable*, not merely wired: the plugin
|
|
508
547
|
* is on AND at least one `sys_sso_provider` row exists. Per-email domain→IdP
|
|
@@ -590,6 +629,33 @@ declare class AuthManager {
|
|
|
590
629
|
* `PASSWORD_POLICY_VIOLATION` when fewer than `passwordMinClasses` are used.
|
|
591
630
|
*/
|
|
592
631
|
private assertPasswordComplexity;
|
|
632
|
+
/**
|
|
633
|
+
* ADR-0069 — is any authentication-policy gate enabled? Cheap, synchronous;
|
|
634
|
+
* lets the transport seams skip session lookups entirely when off (the
|
|
635
|
+
* default), keeping the gate zero-overhead until an admin opts in.
|
|
636
|
+
*/
|
|
637
|
+
isAuthGateActive(): boolean;
|
|
638
|
+
/**
|
|
639
|
+
* ADR-0069 — refresh the "any org requires MFA" cache in the background when
|
|
640
|
+
* stale (60s TTL). Fire-and-forget: a brand-new per-org requirement activates
|
|
641
|
+
* the gate on the next request, never blocking this one. No-op when global MFA
|
|
642
|
+
* is already on (the gate is active regardless).
|
|
643
|
+
*/
|
|
644
|
+
private refreshOrgMfaCacheIfStale;
|
|
645
|
+
/**
|
|
646
|
+
* ADR-0069 — compute the auth-policy gate posture for a session. Returns an
|
|
647
|
+
* `{ code, message }` when the user is currently blocked (e.g. password
|
|
648
|
+
* expired), else undefined. No-op (and no DB read) when no gate feature is
|
|
649
|
+
* enabled. Fails OPEN on any lookup error — a transient hiccup must never lock
|
|
650
|
+
* a compliant user out.
|
|
651
|
+
*/
|
|
652
|
+
private computeAuthGate;
|
|
653
|
+
/**
|
|
654
|
+
* ADR-0069 D1 — stamp `sys_user.password_changed_at = now` after a password is
|
|
655
|
+
* set (sign-up / change / reset). Best-effort; never throws. Written as a Date
|
|
656
|
+
* (never epoch-ms) per ADR-0074.
|
|
657
|
+
*/
|
|
658
|
+
private stampPasswordChangedAt;
|
|
593
659
|
/**
|
|
594
660
|
* ADR-0069 D1 — parse the bounded `previous_password_hashes` JSON column into
|
|
595
661
|
* a string[] of hashes, tolerating null / malformed values.
|
|
@@ -637,6 +703,27 @@ declare class AuthManager {
|
|
|
637
703
|
* engine is wired or the user does not exist.
|
|
638
704
|
*/
|
|
639
705
|
unlockUser(userId: string): Promise<boolean>;
|
|
706
|
+
/**
|
|
707
|
+
* ADR-0069 D4 — idle / absolute session enforcement, run per request from
|
|
708
|
+
* `customSession`. No-op when both are off. Revokes (expires in place +
|
|
709
|
+
* stamps revoked_at/revoke_reason) when a limit is exceeded so better-auth
|
|
710
|
+
* returns no session on the NEXT request; otherwise touches `last_activity_at`
|
|
711
|
+
* (throttled to once a minute). Best-effort — never throws.
|
|
712
|
+
*/
|
|
713
|
+
private enforceSessionControls;
|
|
714
|
+
/**
|
|
715
|
+
* ADR-0069 D4 — concurrent-session cap, run from the sign-in after-hook.
|
|
716
|
+
* Keeps the newest `maxConcurrentSessions` live sessions for the user and
|
|
717
|
+
* revokes the rest (oldest first). No-op when off. Best-effort.
|
|
718
|
+
*/
|
|
719
|
+
private enforceConcurrentCap;
|
|
720
|
+
/**
|
|
721
|
+
* ADR-0069 D5 — is `ip` within the configured allow-list? True (allow) when no
|
|
722
|
+
* ranges are configured, OR when the IP can't be determined (fail-open so a
|
|
723
|
+
* misconfigured proxy never locks everyone out — an admin enabling this must
|
|
724
|
+
* ensure forwarded headers are trusted). Supports IPv4 CIDR + exact IPv4/IPv6.
|
|
725
|
+
*/
|
|
726
|
+
isClientIpAllowed(ip: string | undefined): boolean;
|
|
640
727
|
/**
|
|
641
728
|
* Returns the data engine wired into this auth manager. Used by route
|
|
642
729
|
* handlers (e.g. bootstrap-status) that need to query identity tables
|
|
@@ -696,6 +783,98 @@ interface SetInitialPasswordResult {
|
|
|
696
783
|
*/
|
|
697
784
|
declare function runSetInitialPassword(authApi: SetPasswordCapableApi, request: Request): Promise<SetInitialPasswordResult>;
|
|
698
785
|
|
|
786
|
+
/**
|
|
787
|
+
* Shared `register-sso-provider` (form) handler.
|
|
788
|
+
*
|
|
789
|
+
* `@better-auth/sso`'s `POST /sso/register` expects the OIDC protocol fields
|
|
790
|
+
* NESTED under `oidcConfig` ({ clientId, clientSecret, discoveryEndpoint,
|
|
791
|
+
* scopes, mapping }). The `sys_sso_provider` `register_sso_provider` UI action
|
|
792
|
+
* collects FLAT form fields (the action param schema has no nested-path
|
|
793
|
+
* support), so posting them straight to `/sso/register` drops
|
|
794
|
+
* clientId/clientSecret at the top level (Zod-stripped) and persists an
|
|
795
|
+
* unusable `oidc_config = null` provider that can never complete a login
|
|
796
|
+
* (ADR-0024).
|
|
797
|
+
*
|
|
798
|
+
* This helper reshapes the flat form body into the nested shape and
|
|
799
|
+
* RE-DISPATCHES it through the real `/sso/register` endpoint (via the
|
|
800
|
+
* better-auth universal handler passed in) so the admin gate, the
|
|
801
|
+
* public-routable `trustedOrigins` allowance, discovery hydration, and secret
|
|
802
|
+
* handling all still run — no logic is duplicated. It is the single source of
|
|
803
|
+
* truth for the two mount points that must stay in lockstep: the full
|
|
804
|
+
* `AuthPlugin` (self-host / OSS host kernel) and the cloud `AuthProxyPlugin`
|
|
805
|
+
* (per-environment runtime) — mirroring `runSetInitialPassword`.
|
|
806
|
+
*/
|
|
807
|
+
interface RegisterSsoFormResult {
|
|
808
|
+
/** HTTP status to return to the caller. */
|
|
809
|
+
status: number;
|
|
810
|
+
/** JSON body; mirrors the `{ success, data?, error? }` envelope the client parses. */
|
|
811
|
+
body: {
|
|
812
|
+
success: boolean;
|
|
813
|
+
data?: {
|
|
814
|
+
providerId: string;
|
|
815
|
+
};
|
|
816
|
+
error?: {
|
|
817
|
+
code: string;
|
|
818
|
+
message: string;
|
|
819
|
+
};
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
/** A better-auth universal handler: `(request) => Response`. */
|
|
823
|
+
type AuthRequestHandler = (request: Request) => Promise<Response>;
|
|
824
|
+
/**
|
|
825
|
+
* Reshape a flat SSO-provider registration form body and register it.
|
|
826
|
+
*
|
|
827
|
+
* @param handle the better-auth universal handler (`AuthManager.handleRequest`
|
|
828
|
+
* on the host kernel, or the resolved per-env handler in the
|
|
829
|
+
* cloud proxy). Used to re-dispatch the nested body to the real
|
|
830
|
+
* `/sso/register` route so all of its gates run.
|
|
831
|
+
* @param request the raw Web `Request` — its headers carry the caller's session
|
|
832
|
+
* cookie / bearer + Origin; its body carries the flat form
|
|
833
|
+
* fields ({ providerId, issuer, domain, clientId, clientSecret,
|
|
834
|
+
* discoveryEndpoint?, scopes?, mapId?, mapEmail?, mapName? }).
|
|
835
|
+
*/
|
|
836
|
+
declare function runRegisterSsoProviderFromForm(handle: AuthRequestHandler, request: Request): Promise<RegisterSsoFormResult>;
|
|
837
|
+
/**
|
|
838
|
+
* ADR-0069 P3 — SAML 2.0 sibling of {@link runRegisterSsoProviderFromForm}.
|
|
839
|
+
*
|
|
840
|
+
* `@better-auth/sso` (samlify-backed) registers a SAML IdP via the SAME
|
|
841
|
+
* `/sso/register` endpoint, with the protocol fields nested under `samlConfig`
|
|
842
|
+
* ({ entryPoint, cert, callbackUrl, identifierFormat? }) instead of `oidcConfig`.
|
|
843
|
+
* The UI action collects FLAT fields; this helper reshapes them, derives the
|
|
844
|
+
* per-provider ACS callback URL (`/sso/saml2/sp/acs/<providerId>`), and
|
|
845
|
+
* re-dispatches through `/sso/register` so the admin gate + provisioning run.
|
|
846
|
+
* Returns the SP ACS + metadata URLs the admin must configure on the IdP.
|
|
847
|
+
*/
|
|
848
|
+
declare function runRegisterSamlProviderFromForm(handle: AuthRequestHandler, request: Request): Promise<RegisterSsoFormResult & {
|
|
849
|
+
body: RegisterSsoFormResult['body'] & {
|
|
850
|
+
acsUrl?: string;
|
|
851
|
+
spMetadataUrl?: string;
|
|
852
|
+
};
|
|
853
|
+
}>;
|
|
854
|
+
/**
|
|
855
|
+
* Request a DNS-TXT domain-verification challenge for a registered provider and
|
|
856
|
+
* return the ready-to-paste DNS record (for a one-shot `resultDialog`).
|
|
857
|
+
*
|
|
858
|
+
* Body: `{ providerId, domain? }` (domain only shapes the displayed record name).
|
|
859
|
+
*/
|
|
860
|
+
declare function runRequestDomainVerification(handle: AuthRequestHandler, request: Request): Promise<RegisterSsoFormResult & {
|
|
861
|
+
body: RegisterSsoFormResult['body'] & {
|
|
862
|
+
data?: any;
|
|
863
|
+
};
|
|
864
|
+
}>;
|
|
865
|
+
/**
|
|
866
|
+
* Verify a provider's domain ownership (re-checks the DNS-TXT record). Reshapes
|
|
867
|
+
* @better-auth/sso's empty `204` / `502` into a `{ success, data:{ message } }`
|
|
868
|
+
* envelope so the action surfaces a clear toast.
|
|
869
|
+
*
|
|
870
|
+
* Body: `{ providerId }`.
|
|
871
|
+
*/
|
|
872
|
+
declare function runVerifyDomain(handle: AuthRequestHandler, request: Request): Promise<RegisterSsoFormResult & {
|
|
873
|
+
body: RegisterSsoFormResult['body'] & {
|
|
874
|
+
data?: any;
|
|
875
|
+
};
|
|
876
|
+
}>;
|
|
877
|
+
|
|
699
878
|
/**
|
|
700
879
|
* Mapping from better-auth model names to ObjectStack protocol object names.
|
|
701
880
|
*
|
|
@@ -1478,6 +1657,7 @@ declare const AUTH_SSO_PROVIDER_SCHEMA: {
|
|
|
1478
1657
|
readonly samlConfig: "saml_config";
|
|
1479
1658
|
readonly userId: "user_id";
|
|
1480
1659
|
readonly organizationId: "organization_id";
|
|
1660
|
+
readonly domainVerified: "domain_verified";
|
|
1481
1661
|
};
|
|
1482
1662
|
};
|
|
1483
1663
|
/**
|
|
@@ -1530,4 +1710,4 @@ declare function buildDeviceAuthorizationPluginSchema(): {
|
|
|
1530
1710
|
};
|
|
1531
1711
|
};
|
|
1532
1712
|
|
|
1533
|
-
export { AUTH_ACCOUNT_CONFIG, AUTH_ADMIN_SESSION_FIELDS, AUTH_ADMIN_USER_FIELDS, AUTH_DEVICE_CODE_SCHEMA, AUTH_INVITATION_SCHEMA, AUTH_JWKS_SCHEMA, AUTH_MEMBER_SCHEMA, AUTH_MODEL_TO_PROTOCOL, AUTH_OAUTH_ACCESS_TOKEN_SCHEMA, AUTH_OAUTH_APPLICATION_SCHEMA, AUTH_OAUTH_CLIENT_SCHEMA, AUTH_OAUTH_CONSENT_SCHEMA, AUTH_OAUTH_REFRESH_TOKEN_SCHEMA, AUTH_ORGANIZATION_SCHEMA, AUTH_ORG_SESSION_FIELDS, AUTH_SCIM_PROVIDER_SCHEMA, AUTH_SESSION_CONFIG, AUTH_SSO_PROVIDER_SCHEMA, AUTH_TEAM_MEMBER_SCHEMA, AUTH_TEAM_SCHEMA, AUTH_TWO_FACTOR_SCHEMA, AUTH_TWO_FACTOR_USER_FIELDS, AUTH_USER_CONFIG, AUTH_VERIFICATION_CONFIG, AuthManager, type AuthManagerOptions, AuthPlugin, type AuthPluginOptions, type SetInitialPasswordResult, type SetPasswordCapableApi, buildAdminPluginSchema, buildDeviceAuthorizationPluginSchema, buildJwtPluginSchema, buildOauthProviderPluginSchema, buildOidcProviderPluginSchema, buildOrganizationPluginSchema, buildTwoFactorPluginSchema, createObjectQLAdapter, createObjectQLAdapterFactory, resolveProtocolName, runSetInitialPassword, withSystemReadContext };
|
|
1713
|
+
export { AUTH_ACCOUNT_CONFIG, AUTH_ADMIN_SESSION_FIELDS, AUTH_ADMIN_USER_FIELDS, AUTH_DEVICE_CODE_SCHEMA, AUTH_INVITATION_SCHEMA, AUTH_JWKS_SCHEMA, AUTH_MEMBER_SCHEMA, AUTH_MODEL_TO_PROTOCOL, AUTH_OAUTH_ACCESS_TOKEN_SCHEMA, AUTH_OAUTH_APPLICATION_SCHEMA, AUTH_OAUTH_CLIENT_SCHEMA, AUTH_OAUTH_CONSENT_SCHEMA, AUTH_OAUTH_REFRESH_TOKEN_SCHEMA, AUTH_ORGANIZATION_SCHEMA, AUTH_ORG_SESSION_FIELDS, AUTH_SCIM_PROVIDER_SCHEMA, AUTH_SESSION_CONFIG, AUTH_SSO_PROVIDER_SCHEMA, AUTH_TEAM_MEMBER_SCHEMA, AUTH_TEAM_SCHEMA, AUTH_TWO_FACTOR_SCHEMA, AUTH_TWO_FACTOR_USER_FIELDS, AUTH_USER_CONFIG, AUTH_VERIFICATION_CONFIG, AuthManager, type AuthManagerOptions, AuthPlugin, type AuthPluginOptions, type AuthRequestHandler, type RegisterSsoFormResult, type SetInitialPasswordResult, type SetPasswordCapableApi, buildAdminPluginSchema, buildDeviceAuthorizationPluginSchema, buildJwtPluginSchema, buildOauthProviderPluginSchema, buildOidcProviderPluginSchema, buildOrganizationPluginSchema, buildTwoFactorPluginSchema, createObjectQLAdapter, createObjectQLAdapterFactory, ipMatchesRange, resolveProtocolName, runRegisterSamlProviderFromForm, runRegisterSsoProviderFromForm, runRequestDomainVerification, runSetInitialPassword, runVerifyDomain, withSystemReadContext };
|