@effect-auth/core 0.1.0-alpha.15 → 0.1.0-alpha.18
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/LICENSE +21 -0
- package/README.md +332 -131
- package/dist/AbuseProtection.d.ts +100 -0
- package/dist/AbuseProtection.d.ts.map +1 -0
- package/dist/AbuseProtection.js +71 -0
- package/dist/AbuseProtection.js.map +1 -0
- package/dist/AccessGrant.d.ts +344 -0
- package/dist/AccessGrant.d.ts.map +1 -0
- package/dist/AccessGrant.js +687 -0
- package/dist/AccessGrant.js.map +1 -0
- package/dist/ApiKey.d.ts +2 -14
- package/dist/ApiKey.d.ts.map +1 -1
- package/dist/ApiKey.js +36 -27
- package/dist/ApiKey.js.map +1 -1
- package/dist/Assurance.d.ts +415 -0
- package/dist/Assurance.d.ts.map +1 -0
- package/dist/Assurance.js +764 -0
- package/dist/Assurance.js.map +1 -0
- package/dist/AtomicPlanExecutor.d.ts +28 -0
- package/dist/AtomicPlanExecutor.d.ts.map +1 -0
- package/dist/AtomicPlanExecutor.js +5 -0
- package/dist/AtomicPlanExecutor.js.map +1 -0
- package/dist/AuditLog.d.ts +2314 -52
- package/dist/AuditLog.d.ts.map +1 -1
- package/dist/AuditLog.js +511 -33
- package/dist/AuditLog.js.map +1 -1
- package/dist/AuthConfig.d.ts.map +1 -1
- package/dist/AuthConfig.js.map +1 -1
- package/dist/AuthFlow.d.ts +326 -220
- package/dist/AuthFlow.d.ts.map +1 -1
- package/dist/AuthFlow.js +611 -263
- package/dist/AuthFlow.js.map +1 -1
- package/dist/AuthKernel.d.ts +1 -1
- package/dist/AuthRateLimit.d.ts +96 -0
- package/dist/AuthRateLimit.d.ts.map +1 -0
- package/dist/AuthRateLimit.js +212 -0
- package/dist/AuthRateLimit.js.map +1 -0
- package/dist/Challenge.d.ts +0 -3
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +1 -4
- package/dist/Challenge.js.map +1 -1
- package/dist/Client.d.ts +205 -10
- package/dist/Client.d.ts.map +1 -1
- package/dist/Client.js +397 -16
- package/dist/Client.js.map +1 -1
- package/dist/CloudflareTurnstile.d.ts +28 -0
- package/dist/CloudflareTurnstile.d.ts.map +1 -0
- package/dist/CloudflareTurnstile.js +205 -0
- package/dist/CloudflareTurnstile.js.map +1 -0
- package/dist/Crypto.d.ts +0 -3
- package/dist/Crypto.d.ts.map +1 -1
- package/dist/Crypto.js +5 -7
- package/dist/Crypto.js.map +1 -1
- package/dist/DelegatedAccessStorageMigrations.d.ts +7 -0
- package/dist/DelegatedAccessStorageMigrations.d.ts.map +1 -0
- package/dist/DelegatedAccessStorageMigrations.js +159 -0
- package/dist/DelegatedAccessStorageMigrations.js.map +1 -0
- package/dist/DisposableEmailDomains.d.ts +19 -0
- package/dist/DisposableEmailDomains.d.ts.map +1 -0
- package/dist/DisposableEmailDomains.js +85 -0
- package/dist/DisposableEmailDomains.js.map +1 -0
- package/dist/DomainVerification.d.ts +0 -9
- package/dist/DomainVerification.d.ts.map +1 -1
- package/dist/DomainVerification.js +36 -22
- package/dist/DomainVerification.js.map +1 -1
- package/dist/DrizzleBunSqliteStorage.d.ts +18 -0
- package/dist/DrizzleBunSqliteStorage.d.ts.map +1 -0
- package/dist/DrizzleBunSqliteStorage.js +11 -0
- package/dist/DrizzleBunSqliteStorage.js.map +1 -0
- package/dist/DrizzleD1SqliteStorage.d.ts +28 -0
- package/dist/DrizzleD1SqliteStorage.d.ts.map +1 -0
- package/dist/DrizzleD1SqliteStorage.js +10 -0
- package/dist/DrizzleD1SqliteStorage.js.map +1 -0
- package/dist/DrizzleDurableObjectSqliteStorage.d.ts +19 -0
- package/dist/DrizzleDurableObjectSqliteStorage.d.ts.map +1 -0
- package/dist/DrizzleDurableObjectSqliteStorage.js +14 -0
- package/dist/DrizzleDurableObjectSqliteStorage.js.map +1 -0
- package/dist/DrizzleEffectSqliteStorage.d.ts +31 -0
- package/dist/DrizzleEffectSqliteStorage.d.ts.map +1 -0
- package/dist/DrizzleEffectSqliteStorage.js +108 -0
- package/dist/DrizzleEffectSqliteStorage.js.map +1 -0
- package/dist/DrizzleNodeSqliteStorage.d.ts +18 -0
- package/dist/DrizzleNodeSqliteStorage.d.ts.map +1 -0
- package/dist/DrizzleNodeSqliteStorage.js +11 -0
- package/dist/DrizzleNodeSqliteStorage.js.map +1 -0
- package/dist/DrizzleWasmSqliteStorage.d.ts +23 -0
- package/dist/DrizzleWasmSqliteStorage.d.ts.map +1 -0
- package/dist/DrizzleWasmSqliteStorage.js +15 -0
- package/dist/DrizzleWasmSqliteStorage.js.map +1 -0
- package/dist/EffectQbPostgresDelegatedAccessStorage.d.ts +34 -0
- package/dist/EffectQbPostgresDelegatedAccessStorage.d.ts.map +1 -0
- package/dist/EffectQbPostgresDelegatedAccessStorage.js +499 -0
- package/dist/EffectQbPostgresDelegatedAccessStorage.js.map +1 -0
- package/dist/EffectQbPostgresStorage.d.ts +171 -0
- package/dist/EffectQbPostgresStorage.d.ts.map +1 -0
- package/dist/EffectQbPostgresStorage.js +4280 -0
- package/dist/EffectQbPostgresStorage.js.map +1 -0
- package/dist/EffectQbSqliteDelegatedAccessStorage.d.ts +112 -0
- package/dist/EffectQbSqliteDelegatedAccessStorage.d.ts.map +1 -0
- package/dist/EffectQbSqliteDelegatedAccessStorage.js +602 -0
- package/dist/EffectQbSqliteDelegatedAccessStorage.js.map +1 -0
- package/dist/EffectQbSqliteStorage.d.ts +184 -27
- package/dist/EffectQbSqliteStorage.d.ts.map +1 -1
- package/dist/EffectQbSqliteStorage.js +1729 -135
- package/dist/EffectQbSqliteStorage.js.map +1 -1
- package/dist/EmailAuth.d.ts +65 -0
- package/dist/EmailAuth.d.ts.map +1 -0
- package/dist/EmailAuth.js +171 -0
- package/dist/EmailAuth.js.map +1 -0
- package/dist/EmailIdentityAuth.d.ts +30 -0
- package/dist/EmailIdentityAuth.d.ts.map +1 -0
- package/dist/EmailIdentityAuth.js +142 -0
- package/dist/EmailIdentityAuth.js.map +1 -0
- package/dist/EmailOtp.d.ts +10 -11
- package/dist/EmailOtp.d.ts.map +1 -1
- package/dist/EmailOtp.js +44 -81
- package/dist/EmailOtp.js.map +1 -1
- package/dist/EmailRisk.d.ts +48 -0
- package/dist/EmailRisk.d.ts.map +1 -0
- package/dist/EmailRisk.js +33 -0
- package/dist/EmailRisk.js.map +1 -0
- package/dist/EmailVerification.d.ts +10 -16
- package/dist/EmailVerification.d.ts.map +1 -1
- package/dist/EmailVerification.js +85 -42
- package/dist/EmailVerification.js.map +1 -1
- package/dist/Guard.d.ts +11 -0
- package/dist/Guard.d.ts.map +1 -0
- package/dist/Guard.js +11 -0
- package/dist/Guard.js.map +1 -0
- package/dist/HibpPwnedPasswords.d.ts +56 -0
- package/dist/HibpPwnedPasswords.d.ts.map +1 -0
- package/dist/HibpPwnedPasswords.js +232 -0
- package/dist/HibpPwnedPasswords.js.map +1 -0
- package/dist/HttpApi/AdminPermissionDefinition.d.ts +4 -0
- package/dist/HttpApi/AdminPermissionDefinition.d.ts.map +1 -0
- package/dist/HttpApi/AdminPermissionDefinition.js +4 -0
- package/dist/HttpApi/AdminPermissionDefinition.js.map +1 -0
- package/dist/HttpApi/AdminSecurityTimeline.d.ts +4 -0
- package/dist/HttpApi/AdminSecurityTimeline.d.ts.map +1 -0
- package/dist/HttpApi/AdminSecurityTimeline.js +4 -0
- package/dist/HttpApi/AdminSecurityTimeline.js.map +1 -0
- package/dist/HttpApi/AdminSession.d.ts +4 -0
- package/dist/HttpApi/AdminSession.d.ts.map +1 -0
- package/dist/HttpApi/AdminSession.js +4 -0
- package/dist/HttpApi/AdminSession.js.map +1 -0
- package/dist/HttpApi/AdminTrustedDevice.d.ts +4 -0
- package/dist/HttpApi/AdminTrustedDevice.d.ts.map +1 -0
- package/dist/HttpApi/AdminTrustedDevice.js +4 -0
- package/dist/HttpApi/AdminTrustedDevice.js.map +1 -0
- package/dist/HttpApi/Api.d.ts +1172 -1172
- package/dist/HttpApi/Api.d.ts.map +1 -1
- package/dist/HttpApi/Api.js +2160 -896
- package/dist/HttpApi/Api.js.map +1 -1
- package/dist/HttpApi/ApiKey.d.ts +4 -0
- package/dist/HttpApi/ApiKey.d.ts.map +1 -0
- package/dist/HttpApi/ApiKey.js +4 -0
- package/dist/HttpApi/ApiKey.js.map +1 -0
- package/dist/HttpApi/Audit.d.ts +6 -0
- package/dist/HttpApi/Audit.d.ts.map +1 -0
- package/dist/HttpApi/Audit.js +10 -0
- package/dist/HttpApi/Audit.js.map +1 -0
- package/dist/HttpApi/ClientApi.d.ts +917 -0
- package/dist/HttpApi/ClientApi.d.ts.map +1 -0
- package/dist/HttpApi/ClientApi.js +101 -0
- package/dist/HttpApi/ClientApi.js.map +1 -0
- package/dist/HttpApi/EmailAuth.d.ts +6 -0
- package/dist/HttpApi/EmailAuth.d.ts.map +1 -0
- package/dist/HttpApi/EmailAuth.js +6 -0
- package/dist/HttpApi/EmailAuth.js.map +1 -0
- package/dist/HttpApi/EmailGuards.d.ts +115 -0
- package/dist/HttpApi/EmailGuards.d.ts.map +1 -0
- package/dist/HttpApi/EmailGuards.js +191 -0
- package/dist/HttpApi/EmailGuards.js.map +1 -0
- package/dist/HttpApi/EmailOtp.d.ts +6 -0
- package/dist/HttpApi/EmailOtp.d.ts.map +1 -0
- package/dist/HttpApi/EmailOtp.js +6 -0
- package/dist/HttpApi/EmailOtp.js.map +1 -0
- package/dist/HttpApi/EmailVerification.d.ts +6 -0
- package/dist/HttpApi/EmailVerification.d.ts.map +1 -0
- package/dist/HttpApi/EmailVerification.js +6 -0
- package/dist/HttpApi/EmailVerification.js.map +1 -0
- package/dist/HttpApi/Endpoints.d.ts +629 -301
- package/dist/HttpApi/Endpoints.d.ts.map +1 -1
- package/dist/HttpApi/Endpoints.js +178 -120
- package/dist/HttpApi/Endpoints.js.map +1 -1
- package/dist/HttpApi/Errors.d.ts +26 -7
- package/dist/HttpApi/Errors.d.ts.map +1 -1
- package/dist/HttpApi/Errors.js +26 -4
- package/dist/HttpApi/Errors.js.map +1 -1
- package/dist/HttpApi/Identity.d.ts +6 -0
- package/dist/HttpApi/Identity.d.ts.map +1 -0
- package/dist/HttpApi/Identity.js +6 -0
- package/dist/HttpApi/Identity.js.map +1 -0
- package/dist/HttpApi/IdentityGuards.d.ts +31 -0
- package/dist/HttpApi/IdentityGuards.d.ts.map +1 -0
- package/dist/HttpApi/IdentityGuards.js +33 -0
- package/dist/HttpApi/IdentityGuards.js.map +1 -0
- package/dist/HttpApi/Jwt.d.ts +4 -0
- package/dist/HttpApi/Jwt.d.ts.map +1 -0
- package/dist/HttpApi/Jwt.js +4 -0
- package/dist/HttpApi/Jwt.js.map +1 -0
- package/dist/HttpApi/JwtDiscovery.d.ts +4 -0
- package/dist/HttpApi/JwtDiscovery.d.ts.map +1 -0
- package/dist/HttpApi/JwtDiscovery.js +4 -0
- package/dist/HttpApi/JwtDiscovery.js.map +1 -0
- package/dist/HttpApi/LoginApproval.d.ts +5 -0
- package/dist/HttpApi/LoginApproval.d.ts.map +1 -0
- package/dist/HttpApi/LoginApproval.js +5 -0
- package/dist/HttpApi/LoginApproval.js.map +1 -0
- package/dist/HttpApi/LoginNotification.d.ts +5 -0
- package/dist/HttpApi/LoginNotification.d.ts.map +1 -0
- package/dist/HttpApi/LoginNotification.js +5 -0
- package/dist/HttpApi/LoginNotification.js.map +1 -0
- package/dist/HttpApi/MagicLink.d.ts +6 -0
- package/dist/HttpApi/MagicLink.d.ts.map +1 -0
- package/dist/HttpApi/MagicLink.js +6 -0
- package/dist/HttpApi/MagicLink.js.map +1 -0
- package/dist/HttpApi/Mfa.d.ts +5 -0
- package/dist/HttpApi/Mfa.d.ts.map +1 -0
- package/dist/HttpApi/Mfa.js +5 -0
- package/dist/HttpApi/Mfa.js.map +1 -0
- package/dist/HttpApi/Middleware.d.ts +14 -0
- package/dist/HttpApi/Middleware.d.ts.map +1 -0
- package/dist/HttpApi/Middleware.js +5 -0
- package/dist/HttpApi/Middleware.js.map +1 -0
- package/dist/HttpApi/OAuth.d.ts +4 -0
- package/dist/HttpApi/OAuth.d.ts.map +1 -0
- package/dist/HttpApi/OAuth.js +4 -0
- package/dist/HttpApi/OAuth.js.map +1 -0
- package/dist/HttpApi/OAuthProviderAuthorization.d.ts +4 -0
- package/dist/HttpApi/OAuthProviderAuthorization.d.ts.map +1 -0
- package/dist/HttpApi/OAuthProviderAuthorization.js +4 -0
- package/dist/HttpApi/OAuthProviderAuthorization.js.map +1 -0
- package/dist/HttpApi/OAuthToken.d.ts +4 -0
- package/dist/HttpApi/OAuthToken.d.ts.map +1 -0
- package/dist/HttpApi/OAuthToken.js +4 -0
- package/dist/HttpApi/OAuthToken.js.map +1 -0
- package/dist/HttpApi/OidcDiscovery.d.ts +4 -0
- package/dist/HttpApi/OidcDiscovery.d.ts.map +1 -0
- package/dist/HttpApi/OidcDiscovery.js +4 -0
- package/dist/HttpApi/OidcDiscovery.js.map +1 -0
- package/dist/HttpApi/Operations/AdminPermissionDefinition.d.ts +28 -0
- package/dist/HttpApi/Operations/AdminPermissionDefinition.d.ts.map +1 -0
- package/dist/HttpApi/Operations/AdminPermissionDefinition.js +5 -0
- package/dist/HttpApi/Operations/AdminPermissionDefinition.js.map +1 -0
- package/dist/HttpApi/Operations/AdminSession.d.ts +16 -0
- package/dist/HttpApi/Operations/AdminSession.d.ts.map +1 -0
- package/dist/HttpApi/Operations/AdminSession.js +5 -0
- package/dist/HttpApi/Operations/AdminSession.js.map +1 -0
- package/dist/HttpApi/Operations/ApiKey.d.ts +16 -0
- package/dist/HttpApi/Operations/ApiKey.d.ts.map +1 -0
- package/dist/HttpApi/Operations/ApiKey.js +5 -0
- package/dist/HttpApi/Operations/ApiKey.js.map +1 -0
- package/dist/HttpApi/Operations/Discovery.d.ts +19 -0
- package/dist/HttpApi/Operations/Discovery.d.ts.map +1 -0
- package/dist/HttpApi/Operations/Discovery.js +7 -0
- package/dist/HttpApi/Operations/Discovery.js.map +1 -0
- package/dist/HttpApi/Operations/EmailAuth.d.ts +12 -0
- package/dist/HttpApi/Operations/EmailAuth.d.ts.map +1 -0
- package/dist/HttpApi/Operations/EmailAuth.js +5 -0
- package/dist/HttpApi/Operations/EmailAuth.js.map +1 -0
- package/dist/HttpApi/Operations/EmailOtp.d.ts +14 -0
- package/dist/HttpApi/Operations/EmailOtp.d.ts.map +1 -0
- package/dist/HttpApi/Operations/EmailOtp.js +5 -0
- package/dist/HttpApi/Operations/EmailOtp.js.map +1 -0
- package/dist/HttpApi/Operations/EmailVerification.d.ts +14 -0
- package/dist/HttpApi/Operations/EmailVerification.d.ts.map +1 -0
- package/dist/HttpApi/Operations/EmailVerification.js +5 -0
- package/dist/HttpApi/Operations/EmailVerification.js.map +1 -0
- package/dist/HttpApi/Operations/HttpOperation.d.ts +4 -0
- package/dist/HttpApi/Operations/HttpOperation.d.ts.map +1 -0
- package/dist/HttpApi/Operations/HttpOperation.js +2 -0
- package/dist/HttpApi/Operations/HttpOperation.js.map +1 -0
- package/dist/HttpApi/Operations/Identity.d.ts +22 -0
- package/dist/HttpApi/Operations/Identity.d.ts.map +1 -0
- package/dist/HttpApi/Operations/Identity.js +5 -0
- package/dist/HttpApi/Operations/Identity.js.map +1 -0
- package/dist/HttpApi/Operations/Jwt.d.ts +14 -0
- package/dist/HttpApi/Operations/Jwt.d.ts.map +1 -0
- package/dist/HttpApi/Operations/Jwt.js +5 -0
- package/dist/HttpApi/Operations/Jwt.js.map +1 -0
- package/dist/HttpApi/Operations/LoginApproval.d.ts +16 -0
- package/dist/HttpApi/Operations/LoginApproval.d.ts.map +1 -0
- package/dist/HttpApi/Operations/LoginApproval.js +5 -0
- package/dist/HttpApi/Operations/LoginApproval.js.map +1 -0
- package/dist/HttpApi/Operations/MagicLink.d.ts +14 -0
- package/dist/HttpApi/Operations/MagicLink.d.ts.map +1 -0
- package/dist/HttpApi/Operations/MagicLink.js +5 -0
- package/dist/HttpApi/Operations/MagicLink.js.map +1 -0
- package/dist/HttpApi/Operations/Mfa.d.ts +24 -0
- package/dist/HttpApi/Operations/Mfa.d.ts.map +1 -0
- package/dist/HttpApi/Operations/Mfa.js +5 -0
- package/dist/HttpApi/Operations/Mfa.js.map +1 -0
- package/dist/HttpApi/Operations/OAuth.d.ts +20 -0
- package/dist/HttpApi/Operations/OAuth.d.ts.map +1 -0
- package/dist/HttpApi/Operations/OAuth.js +5 -0
- package/dist/HttpApi/Operations/OAuth.js.map +1 -0
- package/dist/HttpApi/Operations/OAuthProviderAuthorization.d.ts +12 -0
- package/dist/HttpApi/Operations/OAuthProviderAuthorization.d.ts.map +1 -0
- package/dist/HttpApi/Operations/OAuthProviderAuthorization.js +5 -0
- package/dist/HttpApi/Operations/OAuthProviderAuthorization.js.map +1 -0
- package/dist/HttpApi/Operations/OAuthToken.d.ts +16 -0
- package/dist/HttpApi/Operations/OAuthToken.d.ts.map +1 -0
- package/dist/HttpApi/Operations/OAuthToken.js +5 -0
- package/dist/HttpApi/Operations/OAuthToken.js.map +1 -0
- package/dist/HttpApi/Operations/Passkey.d.ts +22 -0
- package/dist/HttpApi/Operations/Passkey.d.ts.map +1 -0
- package/dist/HttpApi/Operations/Passkey.js +5 -0
- package/dist/HttpApi/Operations/Passkey.js.map +1 -0
- package/dist/HttpApi/Operations/Password.d.ts +22 -0
- package/dist/HttpApi/Operations/Password.d.ts.map +1 -0
- package/dist/HttpApi/Operations/Password.js +5 -0
- package/dist/HttpApi/Operations/Password.js.map +1 -0
- package/dist/HttpApi/Operations/RecoveryCodes.d.ts +20 -0
- package/dist/HttpApi/Operations/RecoveryCodes.d.ts.map +1 -0
- package/dist/HttpApi/Operations/RecoveryCodes.js +5 -0
- package/dist/HttpApi/Operations/RecoveryCodes.js.map +1 -0
- package/dist/HttpApi/Operations/RefreshToken.d.ts +12 -0
- package/dist/HttpApi/Operations/RefreshToken.d.ts.map +1 -0
- package/dist/HttpApi/Operations/RefreshToken.js +5 -0
- package/dist/HttpApi/Operations/RefreshToken.js.map +1 -0
- package/dist/HttpApi/Operations/Security.d.ts +12 -0
- package/dist/HttpApi/Operations/Security.d.ts.map +1 -0
- package/dist/HttpApi/Operations/Security.js +5 -0
- package/dist/HttpApi/Operations/Security.js.map +1 -0
- package/dist/HttpApi/Operations/SecurityTimeline.d.ts +19 -0
- package/dist/HttpApi/Operations/SecurityTimeline.d.ts.map +1 -0
- package/dist/HttpApi/Operations/SecurityTimeline.js +7 -0
- package/dist/HttpApi/Operations/SecurityTimeline.js.map +1 -0
- package/dist/HttpApi/Operations/Session.d.ts +22 -0
- package/dist/HttpApi/Operations/Session.d.ts.map +1 -0
- package/dist/HttpApi/Operations/Session.js +5 -0
- package/dist/HttpApi/Operations/Session.js.map +1 -0
- package/dist/HttpApi/Operations/StepUp.d.ts +22 -0
- package/dist/HttpApi/Operations/StepUp.d.ts.map +1 -0
- package/dist/HttpApi/Operations/StepUp.js +5 -0
- package/dist/HttpApi/Operations/StepUp.js.map +1 -0
- package/dist/HttpApi/Operations/Totp.d.ts +20 -0
- package/dist/HttpApi/Operations/Totp.d.ts.map +1 -0
- package/dist/HttpApi/Operations/Totp.js +5 -0
- package/dist/HttpApi/Operations/Totp.js.map +1 -0
- package/dist/HttpApi/Operations/TrustedDevice.d.ts +23 -0
- package/dist/HttpApi/Operations/TrustedDevice.d.ts.map +1 -0
- package/dist/HttpApi/Operations/TrustedDevice.js +7 -0
- package/dist/HttpApi/Operations/TrustedDevice.js.map +1 -0
- package/dist/HttpApi/Operations/index.d.ts +27 -0
- package/dist/HttpApi/Operations/index.d.ts.map +1 -0
- package/dist/HttpApi/Operations/index.js +27 -0
- package/dist/HttpApi/Operations/index.js.map +1 -0
- package/dist/HttpApi/Passkey.d.ts +5 -0
- package/dist/HttpApi/Passkey.d.ts.map +1 -0
- package/dist/HttpApi/Passkey.js +5 -0
- package/dist/HttpApi/Passkey.js.map +1 -0
- package/dist/HttpApi/Password.d.ts +220 -0
- package/dist/HttpApi/Password.d.ts.map +1 -0
- package/dist/HttpApi/Password.js +237 -0
- package/dist/HttpApi/Password.js.map +1 -0
- package/dist/HttpApi/PasswordContract.d.ts +307 -0
- package/dist/HttpApi/PasswordContract.d.ts.map +1 -0
- package/dist/HttpApi/PasswordContract.js +63 -0
- package/dist/HttpApi/PasswordContract.js.map +1 -0
- package/dist/HttpApi/PasswordGuards.d.ts +82 -0
- package/dist/HttpApi/PasswordGuards.d.ts.map +1 -0
- package/dist/HttpApi/PasswordGuards.js +179 -0
- package/dist/HttpApi/PasswordGuards.js.map +1 -0
- package/dist/HttpApi/RecoveryCodes.d.ts +5 -0
- package/dist/HttpApi/RecoveryCodes.d.ts.map +1 -0
- package/dist/HttpApi/RecoveryCodes.js +5 -0
- package/dist/HttpApi/RecoveryCodes.js.map +1 -0
- package/dist/HttpApi/RefreshToken.d.ts +4 -0
- package/dist/HttpApi/RefreshToken.d.ts.map +1 -0
- package/dist/HttpApi/RefreshToken.js +4 -0
- package/dist/HttpApi/RefreshToken.js.map +1 -0
- package/dist/HttpApi/RequestGuards.d.ts +42 -0
- package/dist/HttpApi/RequestGuards.d.ts.map +1 -0
- package/dist/HttpApi/RequestGuards.js +194 -0
- package/dist/HttpApi/RequestGuards.js.map +1 -0
- package/dist/HttpApi/Schemas.d.ts +361 -84
- package/dist/HttpApi/Schemas.d.ts.map +1 -1
- package/dist/HttpApi/Schemas.js +190 -27
- package/dist/HttpApi/Schemas.js.map +1 -1
- package/dist/HttpApi/Security.d.ts +12 -93
- package/dist/HttpApi/Security.d.ts.map +1 -1
- package/dist/HttpApi/Security.js +65 -356
- package/dist/HttpApi/Security.js.map +1 -1
- package/dist/HttpApi/SecurityTimeline.d.ts +4 -0
- package/dist/HttpApi/SecurityTimeline.d.ts.map +1 -0
- package/dist/HttpApi/SecurityTimeline.js +4 -0
- package/dist/HttpApi/SecurityTimeline.js.map +1 -0
- package/dist/HttpApi/Session.d.ts +5 -0
- package/dist/HttpApi/Session.d.ts.map +1 -0
- package/dist/HttpApi/Session.js +5 -0
- package/dist/HttpApi/Session.js.map +1 -0
- package/dist/HttpApi/SessionGuards.d.ts +15 -0
- package/dist/HttpApi/SessionGuards.d.ts.map +1 -0
- package/dist/HttpApi/SessionGuards.js +37 -0
- package/dist/HttpApi/SessionGuards.js.map +1 -0
- package/dist/HttpApi/StepUp.d.ts +5 -0
- package/dist/HttpApi/StepUp.d.ts.map +1 -0
- package/dist/HttpApi/StepUp.js +5 -0
- package/dist/HttpApi/StepUp.js.map +1 -0
- package/dist/HttpApi/Totp.d.ts +5 -0
- package/dist/HttpApi/Totp.d.ts.map +1 -0
- package/dist/HttpApi/Totp.js +5 -0
- package/dist/HttpApi/Totp.js.map +1 -0
- package/dist/HttpApi/Transport.d.ts +4 -6
- package/dist/HttpApi/Transport.d.ts.map +1 -1
- package/dist/HttpApi/Transport.js +62 -36
- package/dist/HttpApi/Transport.js.map +1 -1
- package/dist/HttpApi/TrustedDevice.d.ts +4 -0
- package/dist/HttpApi/TrustedDevice.d.ts.map +1 -0
- package/dist/HttpApi/TrustedDevice.js +4 -0
- package/dist/HttpApi/TrustedDevice.js.map +1 -0
- package/dist/HttpApi/index.d.ts +9 -3
- package/dist/HttpApi/index.d.ts.map +1 -1
- package/dist/HttpApi/index.js +8 -2
- package/dist/HttpApi/index.js.map +1 -1
- package/dist/Identity.d.ts +138 -0
- package/dist/Identity.d.ts.map +1 -0
- package/dist/Identity.js +105 -0
- package/dist/Identity.js.map +1 -0
- package/dist/IdentityManagement.d.ts +189 -0
- package/dist/IdentityManagement.d.ts.map +1 -0
- package/dist/IdentityManagement.js +317 -0
- package/dist/IdentityManagement.js.map +1 -0
- package/dist/IncidentAction.d.ts +1 -4
- package/dist/IncidentAction.d.ts.map +1 -1
- package/dist/IncidentAction.js +15 -9
- package/dist/IncidentAction.js.map +1 -1
- package/dist/Internal.d.ts +2 -0
- package/dist/Internal.d.ts.map +1 -1
- package/dist/Internal.js +30 -0
- package/dist/Internal.js.map +1 -1
- package/dist/Invitation.d.ts +362 -0
- package/dist/Invitation.d.ts.map +1 -0
- package/dist/Invitation.js +633 -0
- package/dist/Invitation.js.map +1 -0
- package/dist/IpQualityScore.d.ts +38 -0
- package/dist/IpQualityScore.d.ts.map +1 -0
- package/dist/IpQualityScore.js +215 -0
- package/dist/IpQualityScore.js.map +1 -0
- package/dist/Jwt.d.ts +9 -30
- package/dist/Jwt.d.ts.map +1 -1
- package/dist/Jwt.js +72 -60
- package/dist/Jwt.js.map +1 -1
- package/dist/LoginApproval.d.ts +16 -31
- package/dist/LoginApproval.d.ts.map +1 -1
- package/dist/LoginApproval.js +47 -41
- package/dist/LoginApproval.js.map +1 -1
- package/dist/LoginNotification.d.ts +8 -12
- package/dist/LoginNotification.d.ts.map +1 -1
- package/dist/LoginNotification.js +30 -17
- package/dist/LoginNotification.js.map +1 -1
- package/dist/LoginRisk.d.ts +11 -13
- package/dist/LoginRisk.d.ts.map +1 -1
- package/dist/LoginRisk.js +66 -26
- package/dist/LoginRisk.js.map +1 -1
- package/dist/LoginRiskEnrichment.d.ts +145 -0
- package/dist/LoginRiskEnrichment.d.ts.map +1 -0
- package/dist/LoginRiskEnrichment.js +220 -0
- package/dist/LoginRiskEnrichment.js.map +1 -0
- package/dist/MachineAuth.d.ts.map +1 -1
- package/dist/MachineAuth.js.map +1 -1
- package/dist/MagicLink.d.ts +9 -8
- package/dist/MagicLink.d.ts.map +1 -1
- package/dist/MagicLink.js +40 -73
- package/dist/MagicLink.js.map +1 -1
- package/dist/Mailer.d.ts +20 -13
- package/dist/Mailer.d.ts.map +1 -1
- package/dist/Mailer.js +10 -12
- package/dist/Mailer.js.map +1 -1
- package/dist/OAuth.d.ts +100 -103
- package/dist/OAuth.d.ts.map +1 -1
- package/dist/OAuth.js +404 -217
- package/dist/OAuth.js.map +1 -1
- package/dist/Passkey.d.ts +17 -19
- package/dist/Passkey.d.ts.map +1 -1
- package/dist/Passkey.js +86 -39
- package/dist/Passkey.js.map +1 -1
- package/dist/PasskeyBrowser.d.ts +39 -0
- package/dist/PasskeyBrowser.d.ts.map +1 -0
- package/dist/PasskeyBrowser.js +206 -0
- package/dist/PasskeyBrowser.js.map +1 -0
- package/dist/PasskeySimpleWebAuthn.d.ts.map +1 -1
- package/dist/PasskeySimpleWebAuthn.js +19 -7
- package/dist/PasskeySimpleWebAuthn.js.map +1 -1
- package/dist/Password.d.ts +71 -37
- package/dist/Password.d.ts.map +1 -1
- package/dist/Password.js +296 -82
- package/dist/Password.js.map +1 -1
- package/dist/PasswordRisk.d.ts +63 -0
- package/dist/PasswordRisk.d.ts.map +1 -0
- package/dist/PasswordRisk.js +37 -0
- package/dist/PasswordRisk.js.map +1 -0
- package/dist/Permission.d.ts +308 -0
- package/dist/Permission.d.ts.map +1 -0
- package/dist/Permission.js +827 -0
- package/dist/Permission.js.map +1 -0
- package/dist/Policy.d.ts +31 -18
- package/dist/Policy.d.ts.map +1 -1
- package/dist/Policy.js +58 -14
- package/dist/Policy.js.map +1 -1
- package/dist/PostgresDelegatedAccessStorageMigrations.d.ts +7 -0
- package/dist/PostgresDelegatedAccessStorageMigrations.d.ts.map +1 -0
- package/dist/PostgresDelegatedAccessStorageMigrations.js +119 -0
- package/dist/PostgresDelegatedAccessStorageMigrations.js.map +1 -0
- package/dist/PostgresStorageMigrations.d.ts +15 -0
- package/dist/PostgresStorageMigrations.d.ts.map +1 -0
- package/dist/PostgresStorageMigrations.js +620 -0
- package/dist/PostgresStorageMigrations.js.map +1 -0
- package/dist/Privacy.d.ts +0 -3
- package/dist/Privacy.d.ts.map +1 -1
- package/dist/Privacy.js +2 -5
- package/dist/Privacy.js.map +1 -1
- package/dist/RateLimiter.d.ts +0 -3
- package/dist/RateLimiter.d.ts.map +1 -1
- package/dist/RateLimiter.js +1 -4
- package/dist/RateLimiter.js.map +1 -1
- package/dist/RecoveryCode.d.ts +84 -12
- package/dist/RecoveryCode.d.ts.map +1 -1
- package/dist/RecoveryCode.js +273 -75
- package/dist/RecoveryCode.js.map +1 -1
- package/dist/RecoveryPolicy.d.ts +81 -0
- package/dist/RecoveryPolicy.d.ts.map +1 -0
- package/dist/RecoveryPolicy.js +119 -0
- package/dist/RecoveryPolicy.js.map +1 -0
- package/dist/RefreshToken.d.ts +1 -10
- package/dist/RefreshToken.d.ts.map +1 -1
- package/dist/RefreshToken.js +31 -22
- package/dist/RefreshToken.js.map +1 -1
- package/dist/Retention.d.ts.map +1 -1
- package/dist/Retention.js.map +1 -1
- package/dist/SecurityTimeline.d.ts +1 -4
- package/dist/SecurityTimeline.d.ts.map +1 -1
- package/dist/SecurityTimeline.js +110 -17
- package/dist/SecurityTimeline.js.map +1 -1
- package/dist/Sessions.d.ts +124 -29
- package/dist/Sessions.d.ts.map +1 -1
- package/dist/Sessions.js +626 -94
- package/dist/Sessions.js.map +1 -1
- package/dist/StepUp.d.ts +132 -11
- package/dist/StepUp.d.ts.map +1 -1
- package/dist/StepUp.js +387 -19
- package/dist/StepUp.js.map +1 -1
- package/dist/Storage.d.ts +89 -23
- package/dist/Storage.d.ts.map +1 -1
- package/dist/Storage.js +10 -12
- package/dist/Storage.js.map +1 -1
- package/dist/StorageMigrations.d.ts +6 -1
- package/dist/StorageMigrations.d.ts.map +1 -1
- package/dist/StorageMigrations.js +224 -0
- package/dist/StorageMigrations.js.map +1 -1
- package/dist/StorageSchemaGenerator.d.ts +486 -0
- package/dist/StorageSchemaGenerator.d.ts.map +1 -0
- package/dist/StorageSchemaGenerator.js +1022 -0
- package/dist/StorageSchemaGenerator.js.map +1 -0
- package/dist/StrongFactor.d.ts +64 -0
- package/dist/StrongFactor.d.ts.map +1 -0
- package/dist/StrongFactor.js +100 -0
- package/dist/StrongFactor.js.map +1 -0
- package/dist/Testing.d.ts +1 -1
- package/dist/Testing.js +2 -2
- package/dist/Testing.js.map +1 -1
- package/dist/Totp.d.ts +88 -13
- package/dist/Totp.d.ts.map +1 -1
- package/dist/Totp.js +194 -36
- package/dist/Totp.js.map +1 -1
- package/dist/TrustedDevice.d.ts +0 -9
- package/dist/TrustedDevice.d.ts.map +1 -1
- package/dist/TrustedDevice.js +15 -17
- package/dist/TrustedDevice.js.map +1 -1
- package/dist/WaitUntil.d.ts +0 -3
- package/dist/WaitUntil.d.ts.map +1 -1
- package/dist/WaitUntil.js +1 -4
- package/dist/WaitUntil.js.map +1 -1
- package/dist/Webhook.d.ts +42 -12
- package/dist/Webhook.d.ts.map +1 -1
- package/dist/Webhook.js +165 -26
- package/dist/Webhook.js.map +1 -1
- package/dist/alchemy/cloudflare/Email.d.ts +4 -4
- package/dist/alchemy/cloudflare/Email.d.ts.map +1 -1
- package/dist/alchemy/cloudflare/Email.js +10 -5
- package/dist/alchemy/cloudflare/Email.js.map +1 -1
- package/dist/alchemy/cloudflare/RateLimitDurableObject.d.ts +4 -4
- package/dist/alchemy/cloudflare/RateLimitDurableObject.d.ts.map +1 -1
- package/dist/alchemy/cloudflare/RateLimitDurableObject.js +5 -4
- package/dist/alchemy/cloudflare/RateLimitDurableObject.js.map +1 -1
- package/dist/cloudflare/Email.d.ts.map +1 -1
- package/dist/cloudflare/Email.js +1 -1
- package/dist/cloudflare/Email.js.map +1 -1
- package/dist/cloudflare/RateLimitDurableObject.d.ts +6 -5
- package/dist/cloudflare/RateLimitDurableObject.d.ts.map +1 -1
- package/dist/cloudflare/RateLimitDurableObject.js.map +1 -1
- package/dist/cloudflare/RequestMetadata.d.ts +49 -0
- package/dist/cloudflare/RequestMetadata.d.ts.map +1 -0
- package/dist/cloudflare/RequestMetadata.js +71 -0
- package/dist/cloudflare/RequestMetadata.js.map +1 -0
- package/dist/index.d.ts +0 -35
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -35
- package/dist/index.js.map +1 -1
- package/dist/internal/AuthFlowFinalizer.d.ts +31 -0
- package/dist/internal/AuthFlowFinalizer.d.ts.map +1 -0
- package/dist/internal/AuthFlowFinalizer.js +182 -0
- package/dist/internal/AuthFlowFinalizer.js.map +1 -0
- package/dist/internal/RateLimitDurableObject.d.ts.map +1 -1
- package/dist/internal/RateLimitDurableObject.js +3 -0
- package/dist/internal/RateLimitDurableObject.js.map +1 -1
- package/migrations/0001_auth_storage.sql +1 -0
- package/migrations/0006_auth_totp_factor.sql +1 -0
- package/migrations/0024_auth_permission.sql +39 -0
- package/migrations/0025_auth_permission_definition.sql +19 -0
- package/migrations/0026_auth_user_identity.sql +59 -0
- package/migrations/0027_auth_audit_log_storage_order.sql +46 -0
- package/migrations/0028_auth_oauth_client_secret_key.sql +39 -0
- package/migrations/delegated-access/0001_auth_delegated_access.sql +152 -0
- package/migrations/postgres/0001_auth_core.sql +77 -0
- package/migrations/postgres/0002_auth_security_oauth.sql +65 -0
- package/migrations/postgres/0003_auth_credentials_tokens.sql +43 -0
- package/migrations/postgres/0004_auth_security_history.sql +103 -0
- package/migrations/postgres/0005_auth_webhooks.sql +29 -0
- package/migrations/postgres/0006_auth_domain_timeline.sql +50 -0
- package/migrations/postgres/0007_auth_oauth_clients.sql +85 -0
- package/migrations/postgres/0008_auth_oauth_tokens.sql +63 -0
- package/migrations/postgres/0009_auth_permissions.sql +66 -0
- package/migrations/postgres/delegated-access/0001_auth_delegated_access.sql +112 -0
- package/package.json +309 -2
|
@@ -1,33 +1,59 @@
|
|
|
1
1
|
import { Data, Effect, Layer, Option, Redacted } from "effect";
|
|
2
|
-
import { Column, Executor, Query, Scalar, Table } from "effect-qb
|
|
3
|
-
import {
|
|
2
|
+
import { Column, Executor as EffectQbExecutor, Query, Scalar, Table, } from "effect-qb";
|
|
3
|
+
import { Executor, Query as SqliteQuery, Renderer } from "effect-qb/sqlite";
|
|
4
4
|
import { ApiKeyId, ApiKeyPrefix, ApiKeySecretHash, ApiKeyStore, ApiKeyStoreError, } from "./ApiKey.js";
|
|
5
|
+
import { deriveAssurance, deriveMfaVerifiedAt, parseAuthenticationEvents, } from "./Assurance.js";
|
|
6
|
+
import { AtomicPlanError } from "./AtomicPlanExecutor.js";
|
|
7
|
+
import { AuditLogStore, AuditLogStoreError, auditEventActorUserId, auditEventUserId, decodeAuditEvent, filterAuditLogEvents, } from "./AuditLog.js";
|
|
5
8
|
import { DomainName, DomainVerificationId, DomainVerificationProofToken, DomainVerificationStore, DomainVerificationStoreError, } from "./DomainVerification.js";
|
|
6
|
-
import { AuthFlowId, ChallengeId, CredentialId, Email, OAuthAccountId, OAuthClientId, OAuthProviderId, SessionId, UnixMillis, UserId, } from "./Identifiers.js";
|
|
9
|
+
import { AuthFlowId, ChallengeId, CredentialId, Email, IdentityId, OAuthAccountId, OAuthClientId, OAuthProviderId, PermissionId, RoleId, SessionId, UnixMillis, UserId, } from "./Identifiers.js";
|
|
10
|
+
import { isIdentityLoginEligible } from "./Identity.js";
|
|
7
11
|
import { currentUnixMillis } from "./Internal.js";
|
|
8
12
|
import { JwtError, JwtRevocationStore } from "./Jwt.js";
|
|
9
13
|
import { LoginApprovalReviewError, LoginApprovalReviewStore, } from "./LoginApproval.js";
|
|
10
14
|
import { LoginRiskContext, LoginRiskHistory, LoginRiskHistoryError, LoginRiskSignal, maxLoginRiskLevel, } from "./LoginRisk.js";
|
|
11
|
-
import { OAuthAccountStore, OAuthAccountStoreError, OAuthAuthorizationCodeStore, OAuthAuthorizationCodeStoreError, OAuthClientStore, OAuthClientStoreError, OAuthConsentStore, OAuthConsentStoreError, OAuthClientSecretHash, OAuthClientSecretPrefix, OAuthClientSecretStore, OAuthClientSecretStoreError, OAuthCodeChallenge, OAuthProviderModeTokenStore, OAuthProviderModeTokenStoreError, OAuthProviderTokenVault, OAuthProviderTokenVaultError, } from "./OAuth.js";
|
|
15
|
+
import { OAuthAccountStore, OAuthAccountStoreError, OAuthIdentityBridgeConflictError, OAuthIdentityBridgeStore, OAuthIdentityBridgeStoreError, OAuthAuthorizationCodeStore, OAuthAuthorizationCodeStoreError, OAuthClientStore, OAuthClientStoreError, OAuthConsentStore, OAuthConsentStoreError, OAuthClientSecretHash, OAuthClientSecretPrefix, OAuthClientSecretStore, OAuthClientSecretStoreError, OAuthCodeChallenge, OAuthProviderModeTokenStore, OAuthProviderModeTokenStoreError, OAuthProviderTokenVault, OAuthProviderTokenVaultError, } from "./OAuth.js";
|
|
12
16
|
import { PasskeyCredentialId, PasskeyCredentialStore, PasskeyCredentialStoreError, } from "./Passkey.js";
|
|
13
|
-
import {
|
|
17
|
+
import { canonicalPermissionScope, normalizePermissionDefinitionListLimit, PermissionStore, PermissionStoreError, } from "./Permission.js";
|
|
18
|
+
import { RecoveryCodeHash, RecoveryCodeLoginCommitStore, RecoveryCodeLoginCommitStoreError, RecoveryCodeStore, RecoveryCodeStoreError, RecoveryCodeSessionRotateStore, RecoveryCodeSessionRotateStoreError, } from "./RecoveryCode.js";
|
|
14
19
|
import { RefreshTokenFamilyId, RefreshTokenId, RefreshTokenSecretHash, RefreshTokenStore, RefreshTokenStoreError, } from "./RefreshToken.js";
|
|
15
20
|
import { filterSecurityTimelineEvents, SecurityTimelineEventId, SecurityTimelineStore, SecurityTimelineStoreError, } from "./SecurityTimeline.js";
|
|
16
|
-
import { CredentialStore, SessionStore, StorageError, UserStore, VerificationStore, } from "./Storage.js";
|
|
17
|
-
import { TotpFactorStore, TotpFactorStoreError, TotpSecret, } from "./Totp.js";
|
|
21
|
+
import { CredentialStore, IdentityConflictError, IdentityStore, RegistrationConflictError, RegistrationFailureError, RegistrationStore, SessionStore, StorageError, UserStore, VerificationStore, } from "./Storage.js";
|
|
22
|
+
import { TotpFactorStore, TotpFactorStoreError, TotpLoginCommitStore, TotpLoginCommitStoreError, TotpSessionRotateStore, TotpSessionRotateStoreError, TotpSecret, } from "./Totp.js";
|
|
18
23
|
import { filterTrustedDevicesForUser, TrustedDeviceError, TrustedDeviceStore, } from "./TrustedDevice.js";
|
|
19
24
|
import { filterDueWebhookOutboxRecords, WebhookOutboxItemId, WebhookOutboxStore, WebhookOutboxStoreError, WebhookReplayStore, WebhookReplayStoreError, } from "./Webhook.js";
|
|
25
|
+
const boundColumnName = (expression) => {
|
|
26
|
+
const state = expression[Symbol.for("effect-qb/BoundColumn")];
|
|
27
|
+
if (typeof state?.columnName !== "string") {
|
|
28
|
+
throw new TypeError("Atomic SQLite storage requires bound table columns");
|
|
29
|
+
}
|
|
30
|
+
return state.columnName;
|
|
31
|
+
};
|
|
20
32
|
export class D1EffectQbError extends Data.TaggedError("D1EffectQbError") {
|
|
21
33
|
}
|
|
22
34
|
const makeAuthUserTable = (name) => Table.make(name, {
|
|
23
35
|
id: Column.text().pipe(Column.primaryKey),
|
|
24
|
-
email: Column.text(),
|
|
25
|
-
email_verified: Column.int(),
|
|
26
36
|
created_at: Column.int(),
|
|
27
37
|
updated_at: Column.int(),
|
|
28
38
|
disabled_at: Column.int().pipe(Column.nullable),
|
|
29
39
|
metadata: Column.text().pipe(Column.nullable),
|
|
30
40
|
});
|
|
41
|
+
const makeAuthUserIdentityTable = (name) => Table.make(name, {
|
|
42
|
+
id: Column.text().pipe(Column.primaryKey),
|
|
43
|
+
user_id: Column.text(),
|
|
44
|
+
scope_type: Column.text(),
|
|
45
|
+
scope_id: Column.text(),
|
|
46
|
+
kind: Column.text(),
|
|
47
|
+
value: Column.text(),
|
|
48
|
+
normalized_value: Column.text(),
|
|
49
|
+
verified_at: Column.int().pipe(Column.nullable),
|
|
50
|
+
is_primary_login: Column.int(),
|
|
51
|
+
created_at: Column.int(),
|
|
52
|
+
updated_at: Column.int(),
|
|
53
|
+
revoked_at: Column.int().pipe(Column.nullable),
|
|
54
|
+
replaced_by_id: Column.text().pipe(Column.nullable),
|
|
55
|
+
metadata: Column.text().pipe(Column.nullable),
|
|
56
|
+
});
|
|
31
57
|
const makeAuthCredentialTable = (name) => Table.make(name, {
|
|
32
58
|
id: Column.text().pipe(Column.primaryKey),
|
|
33
59
|
user_id: Column.text(),
|
|
@@ -45,6 +71,7 @@ const makeAuthSessionTable = (name) => Table.make(name, {
|
|
|
45
71
|
created_at: Column.int(),
|
|
46
72
|
expires_at: Column.int(),
|
|
47
73
|
auth_time: Column.int(),
|
|
74
|
+
authentication_events: Column.text(),
|
|
48
75
|
aal: Column.text(),
|
|
49
76
|
amr: Column.text(),
|
|
50
77
|
mfa_verified_at: Column.int().pipe(Column.nullable),
|
|
@@ -64,6 +91,7 @@ const makeAuthVerificationTable = (name) => Table.make(name, {
|
|
|
64
91
|
consumed_at: Column.int().pipe(Column.nullable),
|
|
65
92
|
});
|
|
66
93
|
const makeAuthAuditLogTable = (name) => Table.make(name, {
|
|
94
|
+
storage_id: Column.int().pipe(Column.primaryKey),
|
|
67
95
|
id: Column.text().pipe(Column.nullable),
|
|
68
96
|
type: Column.text(),
|
|
69
97
|
user_id: Column.text().pipe(Column.nullable),
|
|
@@ -141,6 +169,7 @@ const makeAuthTotpFactorTable = (name) => Table.make(name, {
|
|
|
141
169
|
created_at: Column.int(),
|
|
142
170
|
confirmed_at: Column.int().pipe(Column.nullable),
|
|
143
171
|
last_used_at: Column.int().pipe(Column.nullable),
|
|
172
|
+
last_accepted_counter: Column.int().pipe(Column.nullable),
|
|
144
173
|
revoked_at: Column.int().pipe(Column.nullable),
|
|
145
174
|
metadata: Column.text().pipe(Column.nullable),
|
|
146
175
|
});
|
|
@@ -249,7 +278,7 @@ const makeAuthOAuthAuthorizationCodeTable = (name) => Table.make(name, {
|
|
|
249
278
|
metadata: Column.text().pipe(Column.nullable),
|
|
250
279
|
});
|
|
251
280
|
const makeAuthOAuthClientSecretTable = (name) => Table.make(name, {
|
|
252
|
-
prefix: Column.text()
|
|
281
|
+
prefix: Column.text(),
|
|
253
282
|
client_id: Column.text(),
|
|
254
283
|
secret_hash: Column.text(),
|
|
255
284
|
authentication_methods: Column.text(),
|
|
@@ -319,6 +348,52 @@ const makeAuthWebhookReplayTable = (name) => Table.make(name, {
|
|
|
319
348
|
expires_at: Column.int(),
|
|
320
349
|
created_at: Column.int(),
|
|
321
350
|
});
|
|
351
|
+
const makeAuthPermissionDefinitionTable = (name) => Table.make(name, {
|
|
352
|
+
id: Column.text().pipe(Column.primaryKey),
|
|
353
|
+
description: Column.text().pipe(Column.nullable),
|
|
354
|
+
scope_type_present: Column.int(),
|
|
355
|
+
scope_type: Column.text(),
|
|
356
|
+
created_at: Column.int(),
|
|
357
|
+
updated_at: Column.int(),
|
|
358
|
+
disabled_at: Column.int().pipe(Column.nullable),
|
|
359
|
+
deleted_at: Column.int().pipe(Column.nullable),
|
|
360
|
+
});
|
|
361
|
+
const makeAuthRoleDefinitionTable = (name) => Table.make(name, {
|
|
362
|
+
id: Column.text().pipe(Column.primaryKey),
|
|
363
|
+
description: Column.text().pipe(Column.nullable),
|
|
364
|
+
created_at: Column.int(),
|
|
365
|
+
updated_at: Column.int(),
|
|
366
|
+
disabled_at: Column.int().pipe(Column.nullable),
|
|
367
|
+
deleted_at: Column.int().pipe(Column.nullable),
|
|
368
|
+
});
|
|
369
|
+
const makeAuthPermissionGrantTable = (name) => Table.make(name, {
|
|
370
|
+
subject_type: Column.text(),
|
|
371
|
+
subject_id: Column.text(),
|
|
372
|
+
permission_id: Column.text(),
|
|
373
|
+
scope_type: Column.text(),
|
|
374
|
+
scope_id_present: Column.int(),
|
|
375
|
+
scope_id: Column.text(),
|
|
376
|
+
expires_at: Column.int().pipe(Column.nullable),
|
|
377
|
+
metadata: Column.text().pipe(Column.nullable),
|
|
378
|
+
revoked_at: Column.int().pipe(Column.nullable),
|
|
379
|
+
});
|
|
380
|
+
const makeAuthRoleGrantTable = (name) => Table.make(name, {
|
|
381
|
+
subject_type: Column.text(),
|
|
382
|
+
subject_id: Column.text(),
|
|
383
|
+
role_id: Column.text(),
|
|
384
|
+
scope_type: Column.text(),
|
|
385
|
+
scope_id_present: Column.int(),
|
|
386
|
+
scope_id: Column.text(),
|
|
387
|
+
expires_at: Column.int().pipe(Column.nullable),
|
|
388
|
+
metadata: Column.text().pipe(Column.nullable),
|
|
389
|
+
revoked_at: Column.int().pipe(Column.nullable),
|
|
390
|
+
});
|
|
391
|
+
const makeAuthRolePermissionTable = (name) => Table.make(name, {
|
|
392
|
+
role_id: Column.text(),
|
|
393
|
+
permission_id: Column.text(),
|
|
394
|
+
scope_type_present: Column.int(),
|
|
395
|
+
scope_type: Column.text(),
|
|
396
|
+
});
|
|
322
397
|
const authSqliteTablesTypeId = Symbol.for("@effect-auth/core/EffectQbSqliteStorage/AuthSqliteTables");
|
|
323
398
|
const jsonEncode = (value) => value === undefined ? null : JSON.stringify(value);
|
|
324
399
|
const jsonDecode = (value) => {
|
|
@@ -345,14 +420,50 @@ const decodeBoolean = (value) => {
|
|
|
345
420
|
};
|
|
346
421
|
const mutationValues = (value) => value;
|
|
347
422
|
const queryInsert = (table, values) => Query.insert(table, values);
|
|
423
|
+
const queryInsertMany = (table, values) => Query.from(Query.values(values))(Query.insert(table));
|
|
348
424
|
const queryUpdate = (table, values) => Query.update(table, values);
|
|
349
425
|
const queryEq = (left, right) => Query.eq(left, right);
|
|
426
|
+
const queryNeq = (left, right) => Query.neq(left, right);
|
|
350
427
|
const queryGt = (left, right) => Query.gt(left, right);
|
|
351
428
|
const queryLt = (left, right) => Query.lt(left, right);
|
|
352
429
|
const queryLte = (left, right) => Query.lte(left, right);
|
|
353
430
|
const queryIsNull = (value) => Query.isNull(value);
|
|
431
|
+
const queryIsNotNull = (value) => Query.isNotNull(value);
|
|
432
|
+
const queryIsNotDistinctFrom = (left, right) => Query.isNotDistinctFrom(left, right);
|
|
433
|
+
const queryAnd = (...values) => Query.and(...values);
|
|
434
|
+
const queryOr = (...values) => Query.or(...values);
|
|
435
|
+
const queryNot = (value) => Query.not(value);
|
|
436
|
+
const queryExists = (plan) => Query.exists(plan);
|
|
437
|
+
const queryScalar = (plan) => Query.scalar(plan);
|
|
438
|
+
const queryChanges = () => Query.call("changes");
|
|
439
|
+
const queryChangedOne = () => queryEq(queryChanges(), 1);
|
|
440
|
+
const queryWithoutNullRefinement = (value) => Query.cast(value, value[Scalar.TypeId].dbType);
|
|
354
441
|
const queryIn = (value, first, ...rest) => Query.in(value, first, ...rest);
|
|
442
|
+
const aliasTable = (table, alias) => Table.alias(table, alias);
|
|
443
|
+
const aliasColumn = (table, column) => table[boundColumnName(column)];
|
|
444
|
+
const queryAstTypeId = Symbol.for("effect-qb/QueryAst");
|
|
445
|
+
const targetMutationExpression = (table, columnName, value) => {
|
|
446
|
+
if (value === null) {
|
|
447
|
+
return Query.literal(null);
|
|
448
|
+
}
|
|
449
|
+
const plan = queryUpdate(table, { [columnName]: value });
|
|
450
|
+
const ast = plan[queryAstTypeId];
|
|
451
|
+
const assignment = ast?.set?.find((candidate) => candidate.columnName === columnName);
|
|
452
|
+
if (assignment === undefined) {
|
|
453
|
+
throw new TypeError(`Invalid SQLite mutation column: ${columnName}`);
|
|
454
|
+
}
|
|
455
|
+
return assignment.value;
|
|
456
|
+
};
|
|
457
|
+
const queryInsertWhere = (table, values, predicate) => {
|
|
458
|
+
const selection = Object.fromEntries(Object.entries(values).map(([key, value]) => [
|
|
459
|
+
key,
|
|
460
|
+
targetMutationExpression(table, key, value),
|
|
461
|
+
]));
|
|
462
|
+
const select = Query.where(predicate)(Query.select(selection));
|
|
463
|
+
return Query.from(select)(Query.insert(table));
|
|
464
|
+
};
|
|
355
465
|
const executePlan = (executor, plan) => executor.execute(plan);
|
|
466
|
+
const executeAtomicPlans = (atomic, plans) => atomic.execute(plans);
|
|
356
467
|
const omitUndefined = (record) => Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
357
468
|
const sessionMetadataEnvelopeKey = "__effectAuthSession";
|
|
358
469
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -425,13 +536,31 @@ const encodeSessionMetadata = (row) => {
|
|
|
425
536
|
};
|
|
426
537
|
const decodeDefaultUserRow = (row) => omitUndefined({
|
|
427
538
|
id: UserId(row.id),
|
|
428
|
-
email: Email(row.email),
|
|
429
|
-
emailVerified: decodeBoolean(row.emailVerified),
|
|
430
539
|
createdAt: UnixMillis(Number(row.createdAt)),
|
|
431
540
|
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
432
541
|
disabledAt: optionalUnixMillis(row.disabledAt),
|
|
433
542
|
metadata: jsonDecode(row.metadata),
|
|
434
543
|
});
|
|
544
|
+
const decodeDefaultUserIdentityRow = (row) => omitUndefined({
|
|
545
|
+
id: IdentityId(row.id),
|
|
546
|
+
userId: UserId(row.userId),
|
|
547
|
+
scope: row.scopeType === "global"
|
|
548
|
+
? { type: "global" }
|
|
549
|
+
: { type: "tenant", tenantId: row.scopeId },
|
|
550
|
+
kind: row.kind,
|
|
551
|
+
value: row.value,
|
|
552
|
+
normalizedValue: row.normalizedValue,
|
|
553
|
+
verifiedAt: optionalUnixMillis(row.verifiedAt),
|
|
554
|
+
isPrimaryLogin: decodeBoolean(row.isPrimaryLogin),
|
|
555
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
556
|
+
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
557
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
558
|
+
replacedById: row.replacedById === null ? undefined : IdentityId(row.replacedById),
|
|
559
|
+
metadata: jsonDecode(row.metadata),
|
|
560
|
+
});
|
|
561
|
+
const decodeUserIdentityRow = (tables, row) => tables.identity.decode === undefined
|
|
562
|
+
? decodeDefaultUserIdentityRow(row)
|
|
563
|
+
: tables.identity.decode(row);
|
|
435
564
|
const decodeUserRow = (tables, row) => tables.user.decode === undefined
|
|
436
565
|
? decodeDefaultUserRow(row)
|
|
437
566
|
: tables.user.decode(row);
|
|
@@ -449,6 +578,34 @@ const decodePasswordCredentialRow = (tables, row) => tables.credential.decodePas
|
|
|
449
578
|
: tables.credential.decodePassword(row);
|
|
450
579
|
const decodeDefaultSessionRow = (row) => {
|
|
451
580
|
const sessionMetadata = decodeSessionMetadata(row.metadata);
|
|
581
|
+
const authenticationEvents = parseAuthenticationEvents(jsonDecode(row.authenticationEvents));
|
|
582
|
+
if (authenticationEvents.some((event) => event.type === "custom")) {
|
|
583
|
+
return omitUndefined({
|
|
584
|
+
id: SessionId(row.id),
|
|
585
|
+
userId: UserId(row.userId),
|
|
586
|
+
secretHash: row.secretHash,
|
|
587
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
588
|
+
expiresAt: UnixMillis(Number(row.expiresAt)),
|
|
589
|
+
authTime: UnixMillis(Number(row.authTime)),
|
|
590
|
+
authenticationEvents,
|
|
591
|
+
aal: row.aal,
|
|
592
|
+
amr: decodeAmr(row.amr),
|
|
593
|
+
mfaVerifiedAt: optionalUnixMillis(row.mfaVerifiedAt),
|
|
594
|
+
claims: sessionMetadata.claims,
|
|
595
|
+
metadata: sessionMetadata.metadata,
|
|
596
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
597
|
+
lastSeenAt: optionalUnixMillis(row.lastSeenAt),
|
|
598
|
+
rotatedAt: optionalUnixMillis(row.rotatedAt),
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
const assurance = deriveAssurance(authenticationEvents);
|
|
602
|
+
const mfaVerifiedAt = deriveMfaVerifiedAt(authenticationEvents);
|
|
603
|
+
if (row.aal !== assurance.level ||
|
|
604
|
+
JSON.stringify(decodeAmr(row.amr)) !== JSON.stringify(assurance.amr) ||
|
|
605
|
+
optionalUnixMillis(row.mfaVerifiedAt) !== mfaVerifiedAt ||
|
|
606
|
+
Number(row.authTime) !== Number(assurance.authenticatedAt)) {
|
|
607
|
+
throw new Error("Session assurance summary does not match authentication events");
|
|
608
|
+
}
|
|
452
609
|
return omitUndefined({
|
|
453
610
|
id: SessionId(row.id),
|
|
454
611
|
userId: UserId(row.userId),
|
|
@@ -456,6 +613,7 @@ const decodeDefaultSessionRow = (row) => {
|
|
|
456
613
|
createdAt: UnixMillis(Number(row.createdAt)),
|
|
457
614
|
expiresAt: UnixMillis(Number(row.expiresAt)),
|
|
458
615
|
authTime: UnixMillis(Number(row.authTime)),
|
|
616
|
+
authenticationEvents,
|
|
459
617
|
aal: row.aal,
|
|
460
618
|
amr: decodeAmr(row.amr),
|
|
461
619
|
mfaVerifiedAt: optionalUnixMillis(row.mfaVerifiedAt),
|
|
@@ -466,9 +624,13 @@ const decodeDefaultSessionRow = (row) => {
|
|
|
466
624
|
rotatedAt: optionalUnixMillis(row.rotatedAt),
|
|
467
625
|
});
|
|
468
626
|
};
|
|
469
|
-
const decodeSessionRow = (tables, row) =>
|
|
470
|
-
|
|
471
|
-
|
|
627
|
+
const decodeSessionRow = (tables, row) => {
|
|
628
|
+
const decoded = tables.session.decode === undefined
|
|
629
|
+
? decodeDefaultSessionRow(row)
|
|
630
|
+
: tables.session.decode(row);
|
|
631
|
+
assertSessionAssurance(decoded);
|
|
632
|
+
return decoded;
|
|
633
|
+
};
|
|
472
634
|
const decodeDefaultVerificationRow = (row) => omitUndefined({
|
|
473
635
|
id: ChallengeId(row.id),
|
|
474
636
|
type: row.type,
|
|
@@ -482,7 +644,7 @@ const decodeDefaultVerificationRow = (row) => omitUndefined({
|
|
|
482
644
|
const decodeVerificationRow = (tables, row) => tables.verification.decode === undefined
|
|
483
645
|
? decodeDefaultVerificationRow(row)
|
|
484
646
|
: tables.verification.decode(row);
|
|
485
|
-
const decodeDefaultAuditLogEvent = (row) => jsonDecode(row.event);
|
|
647
|
+
const decodeDefaultAuditLogEvent = (row) => decodeAuditEvent(jsonDecode(row.event));
|
|
486
648
|
const decodeAuditLogEvent = (tables, row) => tables.auditLog.decode === undefined
|
|
487
649
|
? decodeDefaultAuditLogEvent(row)
|
|
488
650
|
: tables.auditLog.decode(row);
|
|
@@ -522,7 +684,9 @@ const decodeDefaultLoginRiskHistoryRecord = (row) => omitUndefined({
|
|
|
522
684
|
locationKey: row.locationKey ?? undefined,
|
|
523
685
|
country: row.country ?? undefined,
|
|
524
686
|
region: row.region ?? undefined,
|
|
525
|
-
latitude: row.latitudeMicro === null
|
|
687
|
+
latitude: row.latitudeMicro === null
|
|
688
|
+
? undefined
|
|
689
|
+
: Number(row.latitudeMicro) / 1_000_000,
|
|
526
690
|
longitude: row.longitudeMicro === null
|
|
527
691
|
? undefined
|
|
528
692
|
: Number(row.longitudeMicro) / 1_000_000,
|
|
@@ -545,7 +709,9 @@ const decodeTrustedDeviceRow = (tables, row) => tables.trustedDevice.decode ===
|
|
|
545
709
|
: tables.trustedDevice.decode(row);
|
|
546
710
|
const decodePasskeyTransports = (value) => {
|
|
547
711
|
const decoded = jsonDecode(value);
|
|
548
|
-
return Array.isArray(decoded)
|
|
712
|
+
return Array.isArray(decoded)
|
|
713
|
+
? decoded
|
|
714
|
+
: undefined;
|
|
549
715
|
};
|
|
550
716
|
const decodeDefaultPasskeyCredentialRow = (row) => omitUndefined({
|
|
551
717
|
id: CredentialId(row.id),
|
|
@@ -575,6 +741,9 @@ const decodeDefaultTotpFactorRow = (row) => omitUndefined({
|
|
|
575
741
|
createdAt: UnixMillis(Number(row.createdAt)),
|
|
576
742
|
confirmedAt: optionalUnixMillis(row.confirmedAt),
|
|
577
743
|
lastUsedAt: optionalUnixMillis(row.lastUsedAt),
|
|
744
|
+
lastAcceptedCounter: row.lastAcceptedCounter === null
|
|
745
|
+
? undefined
|
|
746
|
+
: Number(row.lastAcceptedCounter),
|
|
578
747
|
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
579
748
|
metadata: jsonDecode(row.metadata),
|
|
580
749
|
});
|
|
@@ -711,7 +880,8 @@ const decodeOAuthConsentRow = (tables, row) => tables.oauthConsent.decode === un
|
|
|
711
880
|
: tables.oauthConsent.decode(row);
|
|
712
881
|
const oauthConsentCoversScopes = (record, scopes = []) => scopes.every((scope) => record.scopes.includes(scope));
|
|
713
882
|
const isOAuthConsentActive = (record, input) => record.revokedAt === undefined &&
|
|
714
|
-
(record.expiresAt === undefined ||
|
|
883
|
+
(record.expiresAt === undefined ||
|
|
884
|
+
Number(record.expiresAt) > Number(input.now)) &&
|
|
715
885
|
oauthConsentCoversScopes(record, input.scopes);
|
|
716
886
|
const oauthClientRedirectUriValidationResult = (client, input) => {
|
|
717
887
|
if (client === undefined) {
|
|
@@ -739,7 +909,9 @@ const decodeDefaultOAuthAuthorizationCodeRow = (row) => omitUndefined({
|
|
|
739
909
|
subject: row.subject,
|
|
740
910
|
redirectUri: row.redirectUri,
|
|
741
911
|
scopes: decodeStringArray(row.scopes),
|
|
742
|
-
codeChallenge: row.codeChallenge === null
|
|
912
|
+
codeChallenge: row.codeChallenge === null
|
|
913
|
+
? undefined
|
|
914
|
+
: OAuthCodeChallenge(row.codeChallenge),
|
|
743
915
|
codeChallengeMethod: row.codeChallengeMethod === null
|
|
744
916
|
? undefined
|
|
745
917
|
: row.codeChallengeMethod,
|
|
@@ -839,6 +1011,82 @@ const decodeDefaultWebhookOutboxRecord = (row) => omitUndefined({
|
|
|
839
1011
|
const decodeWebhookOutboxRecord = (tables, row) => tables.webhookOutbox.decode === undefined
|
|
840
1012
|
? decodeDefaultWebhookOutboxRecord(row)
|
|
841
1013
|
: tables.webhookOutbox.decode(row);
|
|
1014
|
+
const decodeDefaultPermissionDefinition = (row) => omitUndefined({
|
|
1015
|
+
id: PermissionId(row.id),
|
|
1016
|
+
description: row.description ?? undefined,
|
|
1017
|
+
scopeType: decodeBoolean(row.scopeTypePresent) ? row.scopeType : undefined,
|
|
1018
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
1019
|
+
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
1020
|
+
disabledAt: optionalUnixMillis(row.disabledAt),
|
|
1021
|
+
deletedAt: optionalUnixMillis(row.deletedAt),
|
|
1022
|
+
});
|
|
1023
|
+
const decodePermissionDefinition = (tables, row) => tables.permissionDefinition.decode === undefined
|
|
1024
|
+
? decodeDefaultPermissionDefinition(row)
|
|
1025
|
+
: tables.permissionDefinition.decode(row);
|
|
1026
|
+
const decodeDefaultRoleDefinition = (row) => omitUndefined({
|
|
1027
|
+
id: RoleId(row.id),
|
|
1028
|
+
description: row.description ?? undefined,
|
|
1029
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
1030
|
+
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
1031
|
+
disabledAt: optionalUnixMillis(row.disabledAt),
|
|
1032
|
+
deletedAt: optionalUnixMillis(row.deletedAt),
|
|
1033
|
+
});
|
|
1034
|
+
const decodeRoleDefinition = (tables, row) => tables.roleDefinition.decode === undefined
|
|
1035
|
+
? decodeDefaultRoleDefinition(row)
|
|
1036
|
+
: tables.roleDefinition.decode(row);
|
|
1037
|
+
const decodePermissionScope = (scopeType, scopeIdPresent, scopeId) => {
|
|
1038
|
+
const hasId = decodeBoolean(scopeIdPresent);
|
|
1039
|
+
if (scopeType === "global" && !hasId) {
|
|
1040
|
+
return undefined;
|
|
1041
|
+
}
|
|
1042
|
+
return hasId ? { type: scopeType, id: scopeId } : { type: scopeType };
|
|
1043
|
+
};
|
|
1044
|
+
const decodeDefaultPermissionGrant = (row) => omitUndefined({
|
|
1045
|
+
subject: { type: row.subjectType, id: row.subjectId },
|
|
1046
|
+
permission: PermissionId(row.permissionId),
|
|
1047
|
+
scope: decodePermissionScope(row.scopeType, row.scopeIdPresent, row.scopeId),
|
|
1048
|
+
expiresAt: optionalUnixMillis(row.expiresAt),
|
|
1049
|
+
metadata: jsonDecode(row.metadata),
|
|
1050
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
1051
|
+
});
|
|
1052
|
+
const decodePermissionGrant = (tables, row) => {
|
|
1053
|
+
const decoded = tables.permissionGrant.decode === undefined
|
|
1054
|
+
? decodeDefaultPermissionGrant(row)
|
|
1055
|
+
: tables.permissionGrant.decode(row);
|
|
1056
|
+
const { scope: _scope, ...withoutScope } = decoded;
|
|
1057
|
+
const scope = canonicalPermissionScope(decoded.scope);
|
|
1058
|
+
return {
|
|
1059
|
+
...withoutScope,
|
|
1060
|
+
...(scope === undefined ? {} : { scope }),
|
|
1061
|
+
};
|
|
1062
|
+
};
|
|
1063
|
+
const decodeDefaultRoleGrant = (row) => omitUndefined({
|
|
1064
|
+
subject: { type: row.subjectType, id: row.subjectId },
|
|
1065
|
+
role: RoleId(row.roleId),
|
|
1066
|
+
scope: decodePermissionScope(row.scopeType, row.scopeIdPresent, row.scopeId),
|
|
1067
|
+
expiresAt: optionalUnixMillis(row.expiresAt),
|
|
1068
|
+
metadata: jsonDecode(row.metadata),
|
|
1069
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
1070
|
+
});
|
|
1071
|
+
const decodeRoleGrant = (tables, row) => {
|
|
1072
|
+
const decoded = tables.roleGrant.decode === undefined
|
|
1073
|
+
? decodeDefaultRoleGrant(row)
|
|
1074
|
+
: tables.roleGrant.decode(row);
|
|
1075
|
+
const { scope: _scope, ...withoutScope } = decoded;
|
|
1076
|
+
const scope = canonicalPermissionScope(decoded.scope);
|
|
1077
|
+
return {
|
|
1078
|
+
...withoutScope,
|
|
1079
|
+
...(scope === undefined ? {} : { scope }),
|
|
1080
|
+
};
|
|
1081
|
+
};
|
|
1082
|
+
const decodeDefaultRolePermission = (row) => omitUndefined({
|
|
1083
|
+
role: RoleId(row.roleId),
|
|
1084
|
+
permission: PermissionId(row.permissionId),
|
|
1085
|
+
scopeType: decodeBoolean(row.scopeTypePresent) ? row.scopeType : undefined,
|
|
1086
|
+
});
|
|
1087
|
+
const decodeRolePermission = (tables, row) => tables.rolePermission.decode === undefined
|
|
1088
|
+
? decodeDefaultRolePermission(row)
|
|
1089
|
+
: tables.rolePermission.decode(row);
|
|
842
1090
|
const providerTokenVaultTokens = (record) => omitUndefined({
|
|
843
1091
|
accessTokenCiphertext: record.accessTokenCiphertext,
|
|
844
1092
|
refreshTokenCiphertext: record.refreshTokenCiphertext,
|
|
@@ -849,13 +1097,50 @@ const providerTokenVaultTokens = (record) => omitUndefined({
|
|
|
849
1097
|
});
|
|
850
1098
|
const userInsert = (row) => ({
|
|
851
1099
|
id: row.id,
|
|
852
|
-
email: row.email,
|
|
853
|
-
email_verified: row.emailVerified ? 1 : 0,
|
|
854
1100
|
created_at: Number(row.createdAt),
|
|
855
1101
|
updated_at: Number(row.updatedAt),
|
|
856
1102
|
disabled_at: row.disabledAt === undefined ? null : Number(row.disabledAt),
|
|
857
1103
|
metadata: jsonEncode(row.metadata),
|
|
858
1104
|
});
|
|
1105
|
+
const identityScopeId = (scope) => scope.type === "global" ? "global" : scope.tenantId;
|
|
1106
|
+
const userIdentityInsert = (row) => ({
|
|
1107
|
+
id: row.id,
|
|
1108
|
+
user_id: row.userId,
|
|
1109
|
+
scope_type: row.scope.type,
|
|
1110
|
+
scope_id: identityScopeId(row.scope),
|
|
1111
|
+
kind: row.kind,
|
|
1112
|
+
value: row.value,
|
|
1113
|
+
normalized_value: row.normalizedValue,
|
|
1114
|
+
verified_at: row.verifiedAt === undefined ? null : Number(row.verifiedAt),
|
|
1115
|
+
is_primary_login: row.isPrimaryLogin === true ? 1 : 0,
|
|
1116
|
+
created_at: Number(row.createdAt),
|
|
1117
|
+
updated_at: Number(row.createdAt),
|
|
1118
|
+
revoked_at: null,
|
|
1119
|
+
replaced_by_id: null,
|
|
1120
|
+
metadata: jsonEncode(row.metadata),
|
|
1121
|
+
});
|
|
1122
|
+
const userIdentityVerify = (input) => ({
|
|
1123
|
+
verified_at: Number(input.verifiedAt),
|
|
1124
|
+
updated_at: Number(input.verifiedAt),
|
|
1125
|
+
});
|
|
1126
|
+
const userIdentitySetPrimary = (input, primary) => ({
|
|
1127
|
+
is_primary_login: primary ? 1 : 0,
|
|
1128
|
+
updated_at: Number(input.updatedAt),
|
|
1129
|
+
});
|
|
1130
|
+
const userIdentityReplace = (input) => ({
|
|
1131
|
+
is_primary_login: 0,
|
|
1132
|
+
revoked_at: Number(input.replacedAt),
|
|
1133
|
+
replaced_by_id: input.replacement.id,
|
|
1134
|
+
updated_at: Number(input.replacedAt),
|
|
1135
|
+
});
|
|
1136
|
+
const userIdentityRevoke = (input, _row, metadata) => ({
|
|
1137
|
+
is_primary_login: 0,
|
|
1138
|
+
revoked_at: Number(input.revokedAt),
|
|
1139
|
+
updated_at: Number(input.revokedAt),
|
|
1140
|
+
metadata: input.reason === undefined
|
|
1141
|
+
? metadata
|
|
1142
|
+
: Query.call("json_set", Query.coalesce(metadata, "{}"), "$.revokeReason", input.reason),
|
|
1143
|
+
});
|
|
859
1144
|
const passwordCredentialInsert = (row) => ({
|
|
860
1145
|
id: row.id,
|
|
861
1146
|
user_id: row.userId,
|
|
@@ -870,21 +1155,40 @@ const passwordCredentialUpdatePassword = (input) => ({
|
|
|
870
1155
|
password_hash: input.passwordHash,
|
|
871
1156
|
updated_at: Number(input.updatedAt),
|
|
872
1157
|
});
|
|
873
|
-
const
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
}
|
|
1158
|
+
const assertSessionAssurance = (row) => {
|
|
1159
|
+
const authenticationEvents = parseAuthenticationEvents(row.authenticationEvents);
|
|
1160
|
+
// Sessions owns registry-aware validation for custom evidence.
|
|
1161
|
+
if (authenticationEvents.some((event) => event.type === "custom")) {
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
const assurance = deriveAssurance(authenticationEvents);
|
|
1165
|
+
if (assurance.authenticatedAt === undefined ||
|
|
1166
|
+
row.aal !== assurance.level ||
|
|
1167
|
+
JSON.stringify(row.amr) !== JSON.stringify(assurance.amr) ||
|
|
1168
|
+
row.mfaVerifiedAt !== deriveMfaVerifiedAt(authenticationEvents) ||
|
|
1169
|
+
Number(row.authTime) !== Number(assurance.authenticatedAt)) {
|
|
1170
|
+
throw new Error("Session assurance summary does not match authentication events");
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1173
|
+
const sessionInsert = (row) => {
|
|
1174
|
+
assertSessionAssurance(row);
|
|
1175
|
+
return {
|
|
1176
|
+
id: row.id,
|
|
1177
|
+
user_id: row.userId,
|
|
1178
|
+
secret_hash: row.secretHash,
|
|
1179
|
+
created_at: Number(row.createdAt),
|
|
1180
|
+
expires_at: Number(row.expiresAt),
|
|
1181
|
+
auth_time: Number(row.authTime),
|
|
1182
|
+
authentication_events: JSON.stringify(row.authenticationEvents),
|
|
1183
|
+
aal: row.aal,
|
|
1184
|
+
amr: JSON.stringify(row.amr),
|
|
1185
|
+
mfa_verified_at: row.mfaVerifiedAt === undefined ? null : Number(row.mfaVerifiedAt),
|
|
1186
|
+
metadata: encodeSessionMetadata(row),
|
|
1187
|
+
revoked_at: null,
|
|
1188
|
+
last_seen_at: null,
|
|
1189
|
+
rotated_at: null,
|
|
1190
|
+
};
|
|
1191
|
+
};
|
|
888
1192
|
const verificationInsert = (row) => ({
|
|
889
1193
|
id: row.id,
|
|
890
1194
|
type: row.type,
|
|
@@ -945,10 +1249,6 @@ const loginRiskHistoryInsert = (row) => ({
|
|
|
945
1249
|
risk_level: row.riskLevel ?? null,
|
|
946
1250
|
created_at: Number(row.createdAt),
|
|
947
1251
|
});
|
|
948
|
-
const userMarkEmailVerifiedUpdate = (input) => ({
|
|
949
|
-
email_verified: 1,
|
|
950
|
-
updated_at: Number(input.updatedAt),
|
|
951
|
-
});
|
|
952
1252
|
const sessionRefreshUpdate = (input) => omitUndefined({
|
|
953
1253
|
expires_at: Number(input.expiresAt),
|
|
954
1254
|
last_seen_at: input.lastSeenAt === undefined ? undefined : Number(input.lastSeenAt),
|
|
@@ -959,14 +1259,28 @@ const sessionRotateUpdate = (input) => omitUndefined({
|
|
|
959
1259
|
expires_at: input.expiresAt === undefined ? undefined : Number(input.expiresAt),
|
|
960
1260
|
});
|
|
961
1261
|
const sessionUpdateClaimsUpdate = (row, input) => omitUndefined({
|
|
962
|
-
aal: input.aal,
|
|
963
|
-
amr: input.amr === undefined ? undefined : JSON.stringify(input.amr),
|
|
964
|
-
mfa_verified_at: input.mfaVerifiedAt === undefined ? undefined : Number(input.mfaVerifiedAt),
|
|
965
1262
|
metadata: encodeSessionMetadata({
|
|
966
1263
|
...row,
|
|
967
1264
|
claims: input.claims ?? row.claims,
|
|
968
1265
|
}),
|
|
969
1266
|
});
|
|
1267
|
+
const sessionAppendEvidenceUpdate = (input) => {
|
|
1268
|
+
assertSessionAssurance(input);
|
|
1269
|
+
return {
|
|
1270
|
+
authentication_events: JSON.stringify(input.authenticationEvents),
|
|
1271
|
+
auth_time: Number(input.authTime),
|
|
1272
|
+
aal: input.aal,
|
|
1273
|
+
amr: JSON.stringify(input.amr),
|
|
1274
|
+
mfa_verified_at: input.mfaVerifiedAt === undefined ? null : Number(input.mfaVerifiedAt),
|
|
1275
|
+
};
|
|
1276
|
+
};
|
|
1277
|
+
const sessionAssureAndRotateUpdate = (row, input) => ({
|
|
1278
|
+
...sessionAppendEvidenceUpdate(input),
|
|
1279
|
+
...sessionRotateUpdate(input),
|
|
1280
|
+
...(input.claims === undefined
|
|
1281
|
+
? {}
|
|
1282
|
+
: { metadata: encodeSessionMetadata({ ...row, claims: input.claims }) }),
|
|
1283
|
+
});
|
|
970
1284
|
const sessionRevokeUpdate = (input) => ({
|
|
971
1285
|
revoked_at: Number(input.revokedAt),
|
|
972
1286
|
});
|
|
@@ -1031,6 +1345,7 @@ const totpFactorInsert = (row) => ({
|
|
|
1031
1345
|
created_at: Number(row.createdAt),
|
|
1032
1346
|
confirmed_at: row.confirmedAt === undefined ? null : Number(row.confirmedAt),
|
|
1033
1347
|
last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
|
|
1348
|
+
last_accepted_counter: row.lastAcceptedCounter ?? null,
|
|
1034
1349
|
revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
|
|
1035
1350
|
metadata: jsonEncode(row.metadata),
|
|
1036
1351
|
});
|
|
@@ -1038,8 +1353,9 @@ const totpFactorConfirm = (input) => omitUndefined({
|
|
|
1038
1353
|
confirmed_at: Number(input.confirmedAt),
|
|
1039
1354
|
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
1040
1355
|
});
|
|
1041
|
-
const
|
|
1356
|
+
const totpFactorAcceptCounter = (input) => omitUndefined({
|
|
1042
1357
|
last_used_at: Number(input.lastUsedAt),
|
|
1358
|
+
last_accepted_counter: input.counter,
|
|
1043
1359
|
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
1044
1360
|
});
|
|
1045
1361
|
const totpFactorRevoke = (input, row) => ({
|
|
@@ -1059,6 +1375,14 @@ const recoveryCodeMarkUsed = (input) => omitUndefined({
|
|
|
1059
1375
|
used_at: Number(input.usedAt),
|
|
1060
1376
|
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
1061
1377
|
});
|
|
1378
|
+
const recoveryCodeReplaceActive = (input, metadata) => ({
|
|
1379
|
+
revoked_at: Number(input.revokedAt),
|
|
1380
|
+
...(input.revokeReason === undefined
|
|
1381
|
+
? {}
|
|
1382
|
+
: {
|
|
1383
|
+
metadata: Query.call("json_set", Query.coalesce(metadata, "{}"), "$.revokeReason", input.revokeReason),
|
|
1384
|
+
}),
|
|
1385
|
+
});
|
|
1062
1386
|
const recoveryCodeRevoke = (input, row) => ({
|
|
1063
1387
|
revoked_at: Number(input.revokedAt),
|
|
1064
1388
|
metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
|
|
@@ -1144,7 +1468,9 @@ const oauthProviderTokenVaultValues = (row) => ({
|
|
|
1144
1468
|
refresh_token_ciphertext: row.refreshTokenCiphertext === undefined
|
|
1145
1469
|
? null
|
|
1146
1470
|
: Redacted.value(row.refreshTokenCiphertext),
|
|
1147
|
-
id_token_ciphertext: row.idTokenCiphertext === undefined
|
|
1471
|
+
id_token_ciphertext: row.idTokenCiphertext === undefined
|
|
1472
|
+
? null
|
|
1473
|
+
: Redacted.value(row.idTokenCiphertext),
|
|
1148
1474
|
token_type: row.tokenType,
|
|
1149
1475
|
scopes: jsonEncode(row.scopes),
|
|
1150
1476
|
expires_at: row.expiresAt === undefined ? null : Number(row.expiresAt),
|
|
@@ -1320,6 +1646,101 @@ const webhookReplayUpdate = (input) => ({
|
|
|
1320
1646
|
expires_at: Number(input.expiresAt),
|
|
1321
1647
|
created_at: Number(input.now),
|
|
1322
1648
|
});
|
|
1649
|
+
const permissionScopeValues = (scope) => ({
|
|
1650
|
+
scope_type: scope?.type ?? "global",
|
|
1651
|
+
scope_id_present: scope?.id === undefined ? 0 : 1,
|
|
1652
|
+
scope_id: scope?.id ?? "",
|
|
1653
|
+
});
|
|
1654
|
+
const permissionGrantValues = (input) => ({
|
|
1655
|
+
subject_type: input.subject.type,
|
|
1656
|
+
subject_id: input.subject.id,
|
|
1657
|
+
permission_id: input.permission,
|
|
1658
|
+
...permissionScopeValues(input.scope),
|
|
1659
|
+
expires_at: input.expiresAt === undefined ? null : Number(input.expiresAt),
|
|
1660
|
+
metadata: jsonEncode(input.metadata),
|
|
1661
|
+
revoked_at: null,
|
|
1662
|
+
});
|
|
1663
|
+
const permissionGrantUpdate = (input) => ({
|
|
1664
|
+
expires_at: input.expiresAt === undefined ? null : Number(input.expiresAt),
|
|
1665
|
+
metadata: jsonEncode(input.metadata),
|
|
1666
|
+
revoked_at: null,
|
|
1667
|
+
});
|
|
1668
|
+
const permissionGrantRevoke = (_input, revokedAt) => ({ revoked_at: Number(revokedAt) });
|
|
1669
|
+
const roleGrantValues = (input) => ({
|
|
1670
|
+
subject_type: input.subject.type,
|
|
1671
|
+
subject_id: input.subject.id,
|
|
1672
|
+
role_id: input.role,
|
|
1673
|
+
...permissionScopeValues(input.scope),
|
|
1674
|
+
expires_at: input.expiresAt === undefined ? null : Number(input.expiresAt),
|
|
1675
|
+
metadata: jsonEncode(input.metadata),
|
|
1676
|
+
revoked_at: null,
|
|
1677
|
+
});
|
|
1678
|
+
const roleGrantUpdate = (input) => ({
|
|
1679
|
+
expires_at: input.expiresAt === undefined ? null : Number(input.expiresAt),
|
|
1680
|
+
metadata: jsonEncode(input.metadata),
|
|
1681
|
+
revoked_at: null,
|
|
1682
|
+
});
|
|
1683
|
+
const roleGrantRevoke = (_input, revokedAt) => ({
|
|
1684
|
+
revoked_at: Number(revokedAt),
|
|
1685
|
+
});
|
|
1686
|
+
const rolePermissionValues = (input) => ({
|
|
1687
|
+
role_id: input.role,
|
|
1688
|
+
permission_id: input.permission,
|
|
1689
|
+
scope_type_present: input.scopeType === undefined ? 0 : 1,
|
|
1690
|
+
scope_type: input.scopeType ?? "",
|
|
1691
|
+
});
|
|
1692
|
+
const permissionDefinitionValues = (definition) => ({
|
|
1693
|
+
id: definition.id,
|
|
1694
|
+
description: definition.description ?? null,
|
|
1695
|
+
scope_type_present: definition.scopeType === undefined ? 0 : 1,
|
|
1696
|
+
scope_type: definition.scopeType ?? "",
|
|
1697
|
+
created_at: Number(definition.createdAt),
|
|
1698
|
+
updated_at: Number(definition.updatedAt),
|
|
1699
|
+
disabled_at: definition.disabledAt === undefined ? null : Number(definition.disabledAt),
|
|
1700
|
+
deleted_at: definition.deletedAt === undefined ? null : Number(definition.deletedAt),
|
|
1701
|
+
});
|
|
1702
|
+
const permissionDefinitionUpdate = (input) => ({
|
|
1703
|
+
...(input.description === undefined
|
|
1704
|
+
? {}
|
|
1705
|
+
: { description: input.description }),
|
|
1706
|
+
...(input.scopeType === undefined
|
|
1707
|
+
? {}
|
|
1708
|
+
: {
|
|
1709
|
+
scope_type_present: input.scopeType === null ? 0 : 1,
|
|
1710
|
+
scope_type: input.scopeType ?? "",
|
|
1711
|
+
}),
|
|
1712
|
+
updated_at: Number(input.updatedAt),
|
|
1713
|
+
});
|
|
1714
|
+
const permissionDefinitionSetDisabled = (input) => ({
|
|
1715
|
+
disabled_at: input.disabledAt === undefined ? null : Number(input.disabledAt),
|
|
1716
|
+
updated_at: Number(input.updatedAt),
|
|
1717
|
+
});
|
|
1718
|
+
const permissionDefinitionDelete = (input) => ({
|
|
1719
|
+
deleted_at: Number(input.deletedAt),
|
|
1720
|
+
updated_at: Number(input.updatedAt),
|
|
1721
|
+
});
|
|
1722
|
+
const roleDefinitionValues = (definition) => ({
|
|
1723
|
+
id: definition.id,
|
|
1724
|
+
description: definition.description ?? null,
|
|
1725
|
+
created_at: Number(definition.createdAt),
|
|
1726
|
+
updated_at: Number(definition.updatedAt),
|
|
1727
|
+
disabled_at: definition.disabledAt === undefined ? null : Number(definition.disabledAt),
|
|
1728
|
+
deleted_at: definition.deletedAt === undefined ? null : Number(definition.deletedAt),
|
|
1729
|
+
});
|
|
1730
|
+
const roleDefinitionUpdate = (input) => ({
|
|
1731
|
+
...(input.description === undefined
|
|
1732
|
+
? {}
|
|
1733
|
+
: { description: input.description }),
|
|
1734
|
+
updated_at: Number(input.updatedAt),
|
|
1735
|
+
});
|
|
1736
|
+
const roleDefinitionSetDisabled = (input) => ({
|
|
1737
|
+
disabled_at: input.disabledAt === undefined ? null : Number(input.disabledAt),
|
|
1738
|
+
updated_at: Number(input.updatedAt),
|
|
1739
|
+
});
|
|
1740
|
+
const roleDefinitionDelete = (input) => ({
|
|
1741
|
+
deleted_at: Number(input.deletedAt),
|
|
1742
|
+
updated_at: Number(input.updatedAt),
|
|
1743
|
+
});
|
|
1323
1744
|
const jwtRevocationUpdate = (record) => ({
|
|
1324
1745
|
revoked_at: Number(record.revokedAt),
|
|
1325
1746
|
expires_at: record.expiresAt === undefined ? null : Number(record.expiresAt),
|
|
@@ -1328,6 +1749,7 @@ const jwtRevocationUpdate = (record) => ({
|
|
|
1328
1749
|
const authSqliteTableName = (name, defaultName) => name ?? defaultName;
|
|
1329
1750
|
const makeInternalAuthSqliteTables = (names = {}) => {
|
|
1330
1751
|
const user = makeAuthUserTable(names.user ?? "auth_user");
|
|
1752
|
+
const identity = makeAuthUserIdentityTable(names.identity ?? "auth_user_identity");
|
|
1331
1753
|
const credential = makeAuthCredentialTable(names.credential ?? "auth_credential");
|
|
1332
1754
|
const session = makeAuthSessionTable(names.session ?? "auth_session");
|
|
1333
1755
|
const verification = makeAuthVerificationTable(names.verification ?? "auth_verification");
|
|
@@ -1352,20 +1774,46 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1352
1774
|
const securityTimeline = makeAuthSecurityTimelineTable(names.securityTimeline ?? "auth_security_timeline");
|
|
1353
1775
|
const webhookOutbox = makeAuthWebhookOutboxTable(names.webhookOutbox ?? "auth_webhook_outbox");
|
|
1354
1776
|
const webhookReplay = makeAuthWebhookReplayTable(names.webhookReplay ?? "auth_webhook_replay");
|
|
1777
|
+
const permissionDefinition = makeAuthPermissionDefinitionTable(names.permissionDefinition ?? "auth_permission_definition");
|
|
1778
|
+
const roleDefinition = makeAuthRoleDefinitionTable(names.roleDefinition ?? "auth_role_definition");
|
|
1779
|
+
const permissionGrant = makeAuthPermissionGrantTable(names.permissionGrant ?? "auth_permission_grant");
|
|
1780
|
+
const roleGrant = makeAuthRoleGrantTable(names.roleGrant ?? "auth_role_grant");
|
|
1781
|
+
const rolePermission = makeAuthRolePermissionTable(names.rolePermission ?? "auth_role_permission");
|
|
1355
1782
|
return {
|
|
1356
1783
|
user: {
|
|
1357
1784
|
table: user,
|
|
1358
1785
|
select: {
|
|
1359
1786
|
id: user.id,
|
|
1360
|
-
email: user.email,
|
|
1361
|
-
emailVerified: user.email_verified,
|
|
1362
1787
|
createdAt: user.created_at,
|
|
1363
1788
|
updatedAt: user.updated_at,
|
|
1364
1789
|
disabledAt: user.disabled_at,
|
|
1365
1790
|
metadata: user.metadata,
|
|
1366
1791
|
},
|
|
1367
1792
|
insert: userInsert,
|
|
1368
|
-
|
|
1793
|
+
},
|
|
1794
|
+
identity: {
|
|
1795
|
+
table: identity,
|
|
1796
|
+
select: {
|
|
1797
|
+
id: identity.id,
|
|
1798
|
+
userId: identity.user_id,
|
|
1799
|
+
scopeType: identity.scope_type,
|
|
1800
|
+
scopeId: identity.scope_id,
|
|
1801
|
+
kind: identity.kind,
|
|
1802
|
+
value: identity.value,
|
|
1803
|
+
normalizedValue: identity.normalized_value,
|
|
1804
|
+
verifiedAt: identity.verified_at,
|
|
1805
|
+
isPrimaryLogin: identity.is_primary_login,
|
|
1806
|
+
createdAt: identity.created_at,
|
|
1807
|
+
updatedAt: identity.updated_at,
|
|
1808
|
+
revokedAt: identity.revoked_at,
|
|
1809
|
+
replacedById: identity.replaced_by_id,
|
|
1810
|
+
metadata: identity.metadata,
|
|
1811
|
+
},
|
|
1812
|
+
insert: userIdentityInsert,
|
|
1813
|
+
verify: userIdentityVerify,
|
|
1814
|
+
setPrimary: userIdentitySetPrimary,
|
|
1815
|
+
replace: userIdentityReplace,
|
|
1816
|
+
revoke: userIdentityRevoke,
|
|
1369
1817
|
},
|
|
1370
1818
|
credential: {
|
|
1371
1819
|
table: credential,
|
|
@@ -1391,6 +1839,7 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1391
1839
|
createdAt: session.created_at,
|
|
1392
1840
|
expiresAt: session.expires_at,
|
|
1393
1841
|
authTime: session.auth_time,
|
|
1842
|
+
authenticationEvents: session.authentication_events,
|
|
1394
1843
|
aal: session.aal,
|
|
1395
1844
|
amr: session.amr,
|
|
1396
1845
|
mfaVerifiedAt: session.mfa_verified_at,
|
|
@@ -1403,6 +1852,8 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1403
1852
|
refresh: sessionRefreshUpdate,
|
|
1404
1853
|
rotate: sessionRotateUpdate,
|
|
1405
1854
|
updateClaims: sessionUpdateClaimsUpdate,
|
|
1855
|
+
appendEvidence: sessionAppendEvidenceUpdate,
|
|
1856
|
+
assureAndRotate: sessionAssureAndRotateUpdate,
|
|
1406
1857
|
revoke: sessionRevokeUpdate,
|
|
1407
1858
|
},
|
|
1408
1859
|
verification: {
|
|
@@ -1423,6 +1874,7 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1423
1874
|
auditLog: {
|
|
1424
1875
|
table: auditLog,
|
|
1425
1876
|
select: {
|
|
1877
|
+
storageOrder: auditLog.storage_id,
|
|
1426
1878
|
id: auditLog.id,
|
|
1427
1879
|
type: auditLog.type,
|
|
1428
1880
|
userId: auditLog.user_id,
|
|
@@ -1525,12 +1977,13 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1525
1977
|
createdAt: totpFactor.created_at,
|
|
1526
1978
|
confirmedAt: totpFactor.confirmed_at,
|
|
1527
1979
|
lastUsedAt: totpFactor.last_used_at,
|
|
1980
|
+
lastAcceptedCounter: totpFactor.last_accepted_counter,
|
|
1528
1981
|
revokedAt: totpFactor.revoked_at,
|
|
1529
1982
|
metadata: totpFactor.metadata,
|
|
1530
1983
|
},
|
|
1531
1984
|
insert: totpFactorInsert,
|
|
1532
1985
|
confirm: totpFactorConfirm,
|
|
1533
|
-
|
|
1986
|
+
acceptCounter: totpFactorAcceptCounter,
|
|
1534
1987
|
revoke: totpFactorRevoke,
|
|
1535
1988
|
},
|
|
1536
1989
|
recoveryCode: {
|
|
@@ -1546,6 +1999,7 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1546
1999
|
},
|
|
1547
2000
|
insert: recoveryCodeInsert,
|
|
1548
2001
|
markUsed: recoveryCodeMarkUsed,
|
|
2002
|
+
replaceActive: recoveryCodeReplaceActive,
|
|
1549
2003
|
revoke: recoveryCodeRevoke,
|
|
1550
2004
|
},
|
|
1551
2005
|
apiKey: {
|
|
@@ -1798,6 +2252,84 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1798
2252
|
insert: webhookReplayInsert,
|
|
1799
2253
|
update: webhookReplayUpdate,
|
|
1800
2254
|
},
|
|
2255
|
+
permissionDefinition: {
|
|
2256
|
+
table: permissionDefinition,
|
|
2257
|
+
conflictTarget: "id",
|
|
2258
|
+
select: {
|
|
2259
|
+
id: permissionDefinition.id,
|
|
2260
|
+
description: permissionDefinition.description,
|
|
2261
|
+
scopeTypePresent: permissionDefinition.scope_type_present,
|
|
2262
|
+
scopeType: permissionDefinition.scope_type,
|
|
2263
|
+
createdAt: permissionDefinition.created_at,
|
|
2264
|
+
updatedAt: permissionDefinition.updated_at,
|
|
2265
|
+
disabledAt: permissionDefinition.disabled_at,
|
|
2266
|
+
deletedAt: permissionDefinition.deleted_at,
|
|
2267
|
+
},
|
|
2268
|
+
insert: permissionDefinitionValues,
|
|
2269
|
+
update: permissionDefinitionUpdate,
|
|
2270
|
+
setDisabled: permissionDefinitionSetDisabled,
|
|
2271
|
+
delete: permissionDefinitionDelete,
|
|
2272
|
+
},
|
|
2273
|
+
roleDefinition: {
|
|
2274
|
+
table: roleDefinition,
|
|
2275
|
+
conflictTarget: "id",
|
|
2276
|
+
select: {
|
|
2277
|
+
id: roleDefinition.id,
|
|
2278
|
+
description: roleDefinition.description,
|
|
2279
|
+
createdAt: roleDefinition.created_at,
|
|
2280
|
+
updatedAt: roleDefinition.updated_at,
|
|
2281
|
+
disabledAt: roleDefinition.disabled_at,
|
|
2282
|
+
deletedAt: roleDefinition.deleted_at,
|
|
2283
|
+
},
|
|
2284
|
+
insert: roleDefinitionValues,
|
|
2285
|
+
update: roleDefinitionUpdate,
|
|
2286
|
+
setDisabled: roleDefinitionSetDisabled,
|
|
2287
|
+
delete: roleDefinitionDelete,
|
|
2288
|
+
},
|
|
2289
|
+
permissionGrant: {
|
|
2290
|
+
table: permissionGrant,
|
|
2291
|
+
select: {
|
|
2292
|
+
subjectType: permissionGrant.subject_type,
|
|
2293
|
+
subjectId: permissionGrant.subject_id,
|
|
2294
|
+
permissionId: permissionGrant.permission_id,
|
|
2295
|
+
scopeType: permissionGrant.scope_type,
|
|
2296
|
+
scopeIdPresent: permissionGrant.scope_id_present,
|
|
2297
|
+
scopeId: permissionGrant.scope_id,
|
|
2298
|
+
expiresAt: permissionGrant.expires_at,
|
|
2299
|
+
metadata: permissionGrant.metadata,
|
|
2300
|
+
revokedAt: permissionGrant.revoked_at,
|
|
2301
|
+
},
|
|
2302
|
+
insert: permissionGrantValues,
|
|
2303
|
+
update: permissionGrantUpdate,
|
|
2304
|
+
revoke: permissionGrantRevoke,
|
|
2305
|
+
},
|
|
2306
|
+
roleGrant: {
|
|
2307
|
+
table: roleGrant,
|
|
2308
|
+
select: {
|
|
2309
|
+
subjectType: roleGrant.subject_type,
|
|
2310
|
+
subjectId: roleGrant.subject_id,
|
|
2311
|
+
roleId: roleGrant.role_id,
|
|
2312
|
+
scopeType: roleGrant.scope_type,
|
|
2313
|
+
scopeIdPresent: roleGrant.scope_id_present,
|
|
2314
|
+
scopeId: roleGrant.scope_id,
|
|
2315
|
+
expiresAt: roleGrant.expires_at,
|
|
2316
|
+
metadata: roleGrant.metadata,
|
|
2317
|
+
revokedAt: roleGrant.revoked_at,
|
|
2318
|
+
},
|
|
2319
|
+
insert: roleGrantValues,
|
|
2320
|
+
update: roleGrantUpdate,
|
|
2321
|
+
revoke: roleGrantRevoke,
|
|
2322
|
+
},
|
|
2323
|
+
rolePermission: {
|
|
2324
|
+
table: rolePermission,
|
|
2325
|
+
select: {
|
|
2326
|
+
roleId: rolePermission.role_id,
|
|
2327
|
+
permissionId: rolePermission.permission_id,
|
|
2328
|
+
scopeTypePresent: rolePermission.scope_type_present,
|
|
2329
|
+
scopeType: rolePermission.scope_type,
|
|
2330
|
+
},
|
|
2331
|
+
insert: rolePermissionValues,
|
|
2332
|
+
},
|
|
1801
2333
|
};
|
|
1802
2334
|
};
|
|
1803
2335
|
const asAuthSqliteTables = (tables) => tables;
|
|
@@ -1805,6 +2337,7 @@ export const defineEffectQbAuthTables = (tables) => asAuthSqliteTables(tables);
|
|
|
1805
2337
|
export const makeAuthSqliteTables = (names = {}) => asAuthSqliteTables(makeInternalAuthSqliteTables(names));
|
|
1806
2338
|
export const authSqliteTables = makeAuthSqliteTables();
|
|
1807
2339
|
const selectUserColumns = (tables) => tables.user.select;
|
|
2340
|
+
const selectIdentityColumns = (tables) => tables.identity.select;
|
|
1808
2341
|
const selectCredentialColumns = (tables) => tables.credential.select;
|
|
1809
2342
|
const selectSessionColumns = (tables) => tables.session.select;
|
|
1810
2343
|
const selectVerificationColumns = (tables) => tables.verification.select;
|
|
@@ -1829,7 +2362,16 @@ const selectDomainVerificationColumns = (tables) => tables.domainVerification.se
|
|
|
1829
2362
|
const selectSecurityTimelineColumns = (tables) => tables.securityTimeline.select;
|
|
1830
2363
|
const selectWebhookOutboxColumns = (tables) => tables.webhookOutbox.select;
|
|
1831
2364
|
const selectWebhookReplayColumns = (tables) => tables.webhookReplay.select;
|
|
2365
|
+
const selectPermissionDefinitionColumns = (tables) => tables.permissionDefinition.select;
|
|
2366
|
+
const selectRoleDefinitionColumns = (tables) => tables.roleDefinition.select;
|
|
2367
|
+
const selectPermissionGrantColumns = (tables) => tables.permissionGrant.select;
|
|
2368
|
+
const selectRoleGrantColumns = (tables) => tables.roleGrant.select;
|
|
2369
|
+
const selectRolePermissionColumns = (tables) => tables.rolePermission.select;
|
|
1832
2370
|
const storageError = (entity, operation, cause) => StorageError.fromUnknown(entity, operation, cause);
|
|
2371
|
+
const decodeSessionResult = (operation, decode) => Effect.try({
|
|
2372
|
+
try: decode,
|
|
2373
|
+
catch: (cause) => storageError("session", operation, cause),
|
|
2374
|
+
});
|
|
1833
2375
|
const auditLogStoreError = (operation, cause) => new AuditLogStoreError({
|
|
1834
2376
|
operation,
|
|
1835
2377
|
message: `Audit log ${operation} failed`,
|
|
@@ -1934,22 +2476,36 @@ const webhookReplayStoreError = (operation, cause) => new WebhookReplayStoreErro
|
|
|
1934
2476
|
message: `Webhook replay ${operation} failed`,
|
|
1935
2477
|
cause,
|
|
1936
2478
|
});
|
|
2479
|
+
const permissionStoreError = (operation, cause) => new PermissionStoreError({
|
|
2480
|
+
operation,
|
|
2481
|
+
message: `Permission store ${operation} failed`,
|
|
2482
|
+
cause,
|
|
2483
|
+
});
|
|
1937
2484
|
const findUserById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectUserColumns(tables)).pipe(Query.from(tables.user.table), Query.where(queryEq(tables.user.select.id, id)))).pipe(Effect.mapError((cause) => storageError("user", operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
1938
2485
|
? Option.none()
|
|
1939
2486
|
: Option.some(decodeUserRow(tables, rows[0]))));
|
|
1940
|
-
const
|
|
2487
|
+
const findIdentity = (executor, tables, where, operation = "find") => executePlan(executor, where.reduce((query, [column, value]) => query.pipe(Query.where(queryEq(column, value))), Query.select(selectIdentityColumns(tables)).pipe(Query.from(tables.identity.table)))).pipe(Effect.mapError((cause) => storageError("identity", operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
1941
2488
|
? Option.none()
|
|
1942
|
-
: Option.some(
|
|
2489
|
+
: Option.some(decodeUserIdentityRow(tables, rows[0]))));
|
|
1943
2490
|
const findPasswordCredentialByUserId = (executor, tables, userId, operation = "find") => executePlan(executor, Query.select(selectCredentialColumns(tables)).pipe(Query.from(tables.credential.table), Query.where(queryEq(tables.credential.select.userId, userId)), Query.where(queryEq(tables.credential.select.type, "password")))).pipe(Effect.mapError((cause) => storageError("credential", operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
1944
2491
|
? Option.none()
|
|
1945
2492
|
: Option.some(decodePasswordCredentialRow(tables, rows[0]))));
|
|
1946
2493
|
const findJwtRevocation = (executor, tables, jwtId, operation = "find") => executePlan(executor, Query.select(selectJwtRevocationColumns(tables)).pipe(Query.from(tables.jwtRevocation.table), Query.where(queryEq(tables.jwtRevocation.select.jwtId, jwtId)))).pipe(Effect.mapError((cause) => jwtRevocationStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
1947
2494
|
? Option.none()
|
|
1948
2495
|
: Option.some(decodeJwtRevocationRow(tables, rows[0]))));
|
|
1949
|
-
const findSessionById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectSessionColumns(tables)).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.id, id)))).pipe(Effect.mapError((cause) => storageError("session", operation, cause)), Effect.
|
|
2496
|
+
const findSessionById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectSessionColumns(tables)).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.id, id)))).pipe(Effect.mapError((cause) => storageError("session", operation, cause)), Effect.flatMap((rows) => decodeSessionResult(operation, () => rows[0] === undefined
|
|
1950
2497
|
? Option.none()
|
|
1951
|
-
: Option.some(decodeSessionRow(tables, rows[0]))));
|
|
1952
|
-
const
|
|
2498
|
+
: Option.some(decodeSessionRow(tables, rows[0])))));
|
|
2499
|
+
const findSessionForMutation = (executor, tables, id, operation) => executePlan(executor, Query.select(selectSessionColumns(tables)).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.id, id)))).pipe(Effect.mapError((cause) => storageError("session", operation, cause)), Effect.flatMap((rows) => decodeSessionResult(operation, () => {
|
|
2500
|
+
const [selected] = rows;
|
|
2501
|
+
return selected === undefined
|
|
2502
|
+
? Option.none()
|
|
2503
|
+
: Option.some({
|
|
2504
|
+
row: decodeSessionRow(tables, selected),
|
|
2505
|
+
authenticationEvents: selected.authenticationEvents,
|
|
2506
|
+
});
|
|
2507
|
+
})));
|
|
2508
|
+
const listSessionsByUser = (executor, tables, input, operation = "listByUser") => executePlan(executor, Query.select(selectSessionColumns(tables)).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.userId, input.userId)))).pipe(Effect.mapError((cause) => storageError("session", operation, cause)), Effect.flatMap((rows) => decodeSessionResult(operation, () => rows.map((row) => decodeSessionRow(tables, row)))));
|
|
1953
2509
|
const findVerificationById = (executor, tables, id, operation = "find") => executePlan(executor, Query.select(selectVerificationColumns(tables)).pipe(Query.from(tables.verification.table), Query.where(queryEq(tables.verification.select.id, id)))).pipe(Effect.mapError((cause) => storageError("verification", operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
1954
2510
|
? Option.none()
|
|
1955
2511
|
: Option.some(decodeVerificationRow(tables, rows[0]))));
|
|
@@ -2021,7 +2577,9 @@ const findOAuthClientSecretByClientIdAndPrefix = (executor, tables, input, opera
|
|
|
2021
2577
|
const findOAuthProviderModeTokenByHash = (executor, tables, input, operation = "find") => executePlan(executor, Query.select(selectOAuthProviderModeTokenColumns(tables)).pipe(Query.from(tables.oauthProviderModeToken.table), Query.where(queryEq(tables.oauthProviderModeToken.select.tokenHash, input.tokenHash)))).pipe(Effect.mapError((cause) => oauthProviderModeTokenStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
2022
2578
|
? Option.none()
|
|
2023
2579
|
: Option.some(decodeOAuthProviderModeTokenRow(tables, rows[0]))));
|
|
2024
|
-
const deleteOAuthProviderModeTokenRowsBefore = (executor, tables, column, before) => executePlan(executor, Query.delete(tables.oauthProviderModeToken.table).pipe(Query.where(queryLt(column, Number(before))), Query.returning({
|
|
2580
|
+
const deleteOAuthProviderModeTokenRowsBefore = (executor, tables, column, before) => executePlan(executor, Query.delete(tables.oauthProviderModeToken.table).pipe(Query.where(queryLt(column, Number(before))), Query.returning({
|
|
2581
|
+
tokenHash: tables.oauthProviderModeToken.select.tokenHash,
|
|
2582
|
+
}))).pipe(Effect.map((rows) => rows.length));
|
|
2025
2583
|
const deleteOAuthProviderModeRetiredTokens = (executor, tables, input) => Effect.gen(function* () {
|
|
2026
2584
|
const expiredDeleted = input.beforeExpiresAt === undefined
|
|
2027
2585
|
? 0
|
|
@@ -2095,7 +2653,14 @@ const listAuditLogRecords = (executor, tables, input = {}) => {
|
|
|
2095
2653
|
const query = input.types === undefined
|
|
2096
2654
|
? byOccurredAt
|
|
2097
2655
|
: byOccurredAt.pipe(Query.where(queryIn(tables.auditLog.select.type, input.types[0], ...input.types.slice(1))));
|
|
2098
|
-
|
|
2656
|
+
let ordered = query.pipe(Query.orderBy(tables.auditLog.select.occurredAt, "desc"), Query.orderBy(Query.coalesce(tables.auditLog.select.id, Query.literal("")), "desc"));
|
|
2657
|
+
if (tables.auditLog.select.storageOrder !== undefined) {
|
|
2658
|
+
ordered = ordered.pipe(Query.orderBy(tables.auditLog.select.storageOrder, "asc"));
|
|
2659
|
+
}
|
|
2660
|
+
return executePlan(executor, ordered).pipe(Effect.mapError((cause) => auditLogStoreError("list", cause)), Effect.flatMap((rows) => Effect.try({
|
|
2661
|
+
try: () => filterAuditLogEvents(rows.map((row) => decodeAuditLogEvent(tables, row)), input),
|
|
2662
|
+
catch: (cause) => auditLogStoreError("list", cause),
|
|
2663
|
+
})));
|
|
2099
2664
|
};
|
|
2100
2665
|
const deleteAuditLogRecordsBefore = (executor, tables, input) => {
|
|
2101
2666
|
const base = Query.delete(tables.auditLog.table).pipe(Query.where(queryLt(tables.auditLog.select.occurredAt, Number(input.beforeOccurredAt))));
|
|
@@ -2131,7 +2696,9 @@ const distanceKm = (first, second) => {
|
|
|
2131
2696
|
Math.cos(firstLatitude) *
|
|
2132
2697
|
Math.cos(secondLatitude) *
|
|
2133
2698
|
Math.sin(longitudeDelta / 2) ** 2;
|
|
2134
|
-
return (2 *
|
|
2699
|
+
return (2 *
|
|
2700
|
+
earthRadiusKm *
|
|
2701
|
+
Math.atan2(Math.sqrt(haversine), Math.sqrt(1 - haversine)));
|
|
2135
2702
|
};
|
|
2136
2703
|
const hasCoordinates = (value) => value.latitude !== undefined &&
|
|
2137
2704
|
Number.isFinite(value.latitude) &&
|
|
@@ -2214,18 +2781,287 @@ const deleteLoginRiskHistoryRecordsBefore = (executor, tables, input) => {
|
|
|
2214
2781
|
};
|
|
2215
2782
|
export const makeEffectQbSqliteUserStore = (executor, options = {}) => {
|
|
2216
2783
|
const tables = options.tables ?? authSqliteTables;
|
|
2217
|
-
return UserStore.
|
|
2784
|
+
return UserStore.of({
|
|
2218
2785
|
findById: (id) => findUserById(executor, tables, id),
|
|
2219
|
-
findByEmail: (email) => findUserByEmail(executor, tables, email),
|
|
2220
2786
|
insert: (row) => executePlan(executor, queryInsert(tables.user.table, mutationValues(tables.user.insert(row)))).pipe(Effect.mapError((cause) => storageError("user", "insert", cause)), Effect.asVoid),
|
|
2221
|
-
|
|
2787
|
+
});
|
|
2788
|
+
};
|
|
2789
|
+
const identityConflict = (row) => new IdentityConflictError({
|
|
2790
|
+
scope: row.scope,
|
|
2791
|
+
kind: row.kind,
|
|
2792
|
+
normalizedValue: row.normalizedValue,
|
|
2793
|
+
});
|
|
2794
|
+
const sqliteConstraintDiscriminator = (cause) => {
|
|
2795
|
+
const pending = [cause];
|
|
2796
|
+
const seen = new Set();
|
|
2797
|
+
while (pending.length > 0) {
|
|
2798
|
+
const current = pending.pop();
|
|
2799
|
+
if (current === undefined || current === null || seen.has(current)) {
|
|
2800
|
+
continue;
|
|
2801
|
+
}
|
|
2802
|
+
seen.add(current);
|
|
2803
|
+
if (typeof current === "string") {
|
|
2804
|
+
if (current === "UniqueViolation" ||
|
|
2805
|
+
/\bUNIQUE constraint failed\b/i.test(current) ||
|
|
2806
|
+
/\bPRIMARY KEY constraint failed\b/i.test(current)) {
|
|
2807
|
+
return true;
|
|
2808
|
+
}
|
|
2809
|
+
continue;
|
|
2810
|
+
}
|
|
2811
|
+
if (typeof current !== "object") {
|
|
2812
|
+
continue;
|
|
2813
|
+
}
|
|
2814
|
+
const record = current;
|
|
2815
|
+
const { code, errno, message, name, _tag } = record;
|
|
2816
|
+
if (_tag === "UniqueViolation" || name === "UniqueViolation") {
|
|
2817
|
+
return true;
|
|
2818
|
+
}
|
|
2819
|
+
if (code === "SQLITE_CONSTRAINT_UNIQUE" ||
|
|
2820
|
+
code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
|
|
2821
|
+
return true;
|
|
2822
|
+
}
|
|
2823
|
+
if (errno === 2067 || errno === 1555) {
|
|
2824
|
+
return true;
|
|
2825
|
+
}
|
|
2826
|
+
if (typeof message === "string" &&
|
|
2827
|
+
(/\bUNIQUE constraint failed\b/i.test(message) ||
|
|
2828
|
+
/\bPRIMARY KEY constraint failed\b/i.test(message))) {
|
|
2829
|
+
return true;
|
|
2830
|
+
}
|
|
2831
|
+
pending.push(record.cause, record.reason, record.error, record.raw);
|
|
2832
|
+
if (Array.isArray(record.failures)) {
|
|
2833
|
+
pending.push(...record.failures);
|
|
2834
|
+
}
|
|
2835
|
+
if (Array.isArray(record.reasons)) {
|
|
2836
|
+
pending.push(...record.reasons);
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
return false;
|
|
2840
|
+
};
|
|
2841
|
+
const isAtomicExecuteConflict = (cause, statement) => cause instanceof AtomicPlanError &&
|
|
2842
|
+
cause.phase === "execute" &&
|
|
2843
|
+
cause.statement === statement &&
|
|
2844
|
+
sqliteConstraintDiscriminator(cause.cause) === true;
|
|
2845
|
+
export const makeEffectQbSqliteIdentityStore = (executor, atomic, options = {}) => {
|
|
2846
|
+
const tables = options.tables ?? authSqliteTables;
|
|
2847
|
+
const byId = (id, operation = "find") => findIdentity(executor, tables, [[tables.identity.select.id, id]], operation);
|
|
2848
|
+
const insert = (row) => executePlan(executor, queryInsert(tables.identity.table, mutationValues(tables.identity.insert(row)))).pipe(Effect.mapError(() => identityConflict(row)), Effect.asVoid);
|
|
2849
|
+
return IdentityStore.of({
|
|
2850
|
+
findById: byId,
|
|
2851
|
+
findByKindAndNormalizedValue: (input) => executePlan(executor, Query.select(selectIdentityColumns(tables)).pipe(Query.from(tables.identity.table), Query.where(queryEq(tables.identity.select.scopeType, input.scope.type)), Query.where(queryEq(tables.identity.select.scopeId, identityScopeId(input.scope))), Query.where(queryEq(tables.identity.select.kind, input.kind)), Query.where(queryEq(tables.identity.select.normalizedValue, input.normalizedValue)), Query.where(queryIsNull(tables.identity.select.revokedAt)))).pipe(Effect.mapError((cause) => storageError("identity", "find", cause)), Effect.map((rows) => rows[0] === undefined
|
|
2222
2852
|
? Option.none()
|
|
2223
|
-
: Option.some(
|
|
2853
|
+
: Option.some(decodeUserIdentityRow(tables, rows[0])))),
|
|
2854
|
+
listByUser: (input) => {
|
|
2855
|
+
let query = Query.select(selectIdentityColumns(tables)).pipe(Query.from(tables.identity.table), Query.where(queryEq(tables.identity.select.userId, input.userId)));
|
|
2856
|
+
if (input.includeRevoked !== true) {
|
|
2857
|
+
query = query.pipe(Query.where(queryIsNull(tables.identity.select.revokedAt)));
|
|
2858
|
+
}
|
|
2859
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => storageError("identity", "find", cause)), Effect.map((rows) => rows.map((row) => decodeUserIdentityRow(tables, row))));
|
|
2860
|
+
},
|
|
2861
|
+
insert,
|
|
2862
|
+
markVerified: (input) => executePlan(executor, queryUpdate(tables.identity.table, mutationValues(tables.identity.verify(input))).pipe(Query.where(queryEq(tables.identity.select.id, input.id)), Query.where(queryEq(tables.identity.select.normalizedValue, input.expectedNormalizedValue)), Query.where(queryIsNull(tables.identity.select.revokedAt)), Query.returning(selectIdentityColumns(tables)))).pipe(Effect.mapError((cause) => storageError("identity", "update", cause)), Effect.map((rows) => rows[0]
|
|
2863
|
+
? Option.some(decodeUserIdentityRow(tables, rows[0]))
|
|
2864
|
+
: Option.none())),
|
|
2865
|
+
setPrimaryLogin: (input) => {
|
|
2866
|
+
const candidate = aliasTable(tables.identity.table, "primary_candidate");
|
|
2867
|
+
const candidateColumn = (column) => aliasColumn(candidate, column);
|
|
2868
|
+
const candidateExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(candidate), Query.where(queryEq(candidateColumn(tables.identity.select.id), input.identityId)), Query.where(queryEq(candidateColumn(tables.identity.select.userId), input.userId)), Query.where(queryEq(candidateColumn(tables.identity.select.updatedAt), Number(input.expectedUpdatedAt))), Query.where(queryIsNull(candidateColumn(tables.identity.select.revokedAt))), Query.where(queryOr(queryNeq(candidateColumn(tables.identity.select.kind), "email"), queryIsNotNull(candidateColumn(tables.identity.select.verifiedAt))))));
|
|
2869
|
+
const primaryColumnName = boundColumnName(tables.identity.select.isPrimaryLogin);
|
|
2870
|
+
const values = {
|
|
2871
|
+
...mutationValues(tables.identity.setPrimary(input, false)),
|
|
2872
|
+
[primaryColumnName]: Query.case()
|
|
2873
|
+
.when(queryEq(tables.identity.select.id, input.identityId), targetMutationExpression(tables.identity.table, primaryColumnName, 1))
|
|
2874
|
+
.else(targetMutationExpression(tables.identity.table, primaryColumnName, 0)),
|
|
2875
|
+
};
|
|
2876
|
+
const plan = queryUpdate(tables.identity.table, values).pipe(Query.where(queryEq(tables.identity.select.userId, input.userId)), Query.where(queryIsNull(tables.identity.select.revokedAt)), Query.where(candidateExists), Query.returning(selectIdentityColumns(tables)));
|
|
2877
|
+
return executeAtomicPlans(atomic, [plan]).pipe(Effect.mapError((cause) => storageError("identity", "update", cause)), Effect.map(([rows]) => {
|
|
2878
|
+
const selected = rows.find((row) => row.id === input.identityId);
|
|
2879
|
+
return selected === undefined
|
|
2880
|
+
? Option.none()
|
|
2881
|
+
: Option.some(decodeUserIdentityRow(tables, selected));
|
|
2882
|
+
}));
|
|
2883
|
+
},
|
|
2884
|
+
replace: (input) => byId(input.id, "update").pipe(Effect.flatMap((found) => {
|
|
2885
|
+
if (Option.isNone(found) ||
|
|
2886
|
+
found.value.userId !== input.userId ||
|
|
2887
|
+
input.replacement.userId !== input.userId) {
|
|
2888
|
+
return Effect.succeed(Option.none());
|
|
2889
|
+
}
|
|
2890
|
+
const row = {
|
|
2891
|
+
...input.replacement,
|
|
2892
|
+
isPrimaryLogin: input.replacement.isPrimaryLogin ?? found.value.isPrimaryLogin,
|
|
2893
|
+
};
|
|
2894
|
+
const active = aliasTable(tables.identity.table, "active_identity");
|
|
2895
|
+
const activeColumn = (column) => aliasColumn(active, column);
|
|
2896
|
+
const usableCount = queryScalar(Query.select({
|
|
2897
|
+
count: Query.count(activeColumn(tables.identity.select.id)),
|
|
2898
|
+
}).pipe(Query.from(active), Query.where(queryEq(activeColumn(tables.identity.select.userId), input.userId)), Query.where(queryIsNull(activeColumn(tables.identity.select.revokedAt))), Query.where(queryOr(queryNeq(activeColumn(tables.identity.select.kind), "email"), queryIsNotNull(activeColumn(tables.identity.select.verifiedAt))))));
|
|
2899
|
+
const updatePlanBase = queryUpdate(tables.identity.table, mutationValues(tables.identity.replace(input))).pipe(Query.where(queryEq(tables.identity.select.id, input.id)), Query.where(queryEq(tables.identity.select.userId, input.userId)), Query.where(queryEq(tables.identity.select.updatedAt, Number(input.expectedUpdatedAt))), Query.where(queryIsNull(tables.identity.select.revokedAt)));
|
|
2900
|
+
const updatePlan = (isIdentityLoginEligible(found.value) &&
|
|
2901
|
+
!isIdentityLoginEligible(row)
|
|
2902
|
+
? updatePlanBase.pipe(Query.where(queryGt(usableCount, 1)))
|
|
2903
|
+
: updatePlanBase).pipe(Query.returning({
|
|
2904
|
+
...selectIdentityColumns(tables),
|
|
2905
|
+
revokedAt: queryWithoutNullRefinement(tables.identity.select.revokedAt),
|
|
2906
|
+
replacedById: queryWithoutNullRefinement(tables.identity.select.replacedById),
|
|
2907
|
+
}));
|
|
2908
|
+
const insertPlan = queryInsertWhere(tables.identity.table, mutationValues(tables.identity.insert(row)), queryChangedOne()).pipe(Query.returning(selectIdentityColumns(tables)));
|
|
2909
|
+
return executeAtomicPlans(atomic, [
|
|
2910
|
+
updatePlan,
|
|
2911
|
+
insertPlan,
|
|
2912
|
+
]).pipe(Effect.mapError((cause) => isAtomicExecuteConflict(cause, 1)
|
|
2913
|
+
? identityConflict(row)
|
|
2914
|
+
: storageError("identity", "update", cause)), Effect.map(([updated, inserted]) => inserted[0] === undefined || updated[0] === undefined
|
|
2915
|
+
? Option.none()
|
|
2916
|
+
: Option.some({
|
|
2917
|
+
previous: decodeUserIdentityRow(tables, updated[0]),
|
|
2918
|
+
replacement: decodeUserIdentityRow(tables, inserted[0]),
|
|
2919
|
+
})));
|
|
2920
|
+
})),
|
|
2921
|
+
revoke: (input) => byId(input.id, "update").pipe(Effect.flatMap((found) => {
|
|
2922
|
+
if (Option.isNone(found)) {
|
|
2923
|
+
return Effect.succeed(Option.none());
|
|
2924
|
+
}
|
|
2925
|
+
const active = aliasTable(tables.identity.table, "active_identity");
|
|
2926
|
+
const activeColumn = (column) => aliasColumn(active, column);
|
|
2927
|
+
const usableCount = queryScalar(Query.select({
|
|
2928
|
+
count: Query.count(activeColumn(tables.identity.select.id)),
|
|
2929
|
+
}).pipe(Query.from(active), Query.where(queryEq(activeColumn(tables.identity.select.userId), input.userId)), Query.where(queryIsNull(activeColumn(tables.identity.select.revokedAt))), Query.where(queryOr(queryNeq(activeColumn(tables.identity.select.kind), "email"), queryIsNotNull(activeColumn(tables.identity.select.verifiedAt))))));
|
|
2930
|
+
const revokeValues = mutationValues(tables.identity.revoke(input, found.value, tables.identity.select.metadata));
|
|
2931
|
+
const revokePlan = queryUpdate(tables.identity.table, revokeValues).pipe(Query.where(queryEq(tables.identity.select.id, input.id)), Query.where(queryEq(tables.identity.select.userId, input.userId)), Query.where(queryEq(tables.identity.select.updatedAt, Number(input.expectedUpdatedAt))), Query.where(queryIsNull(tables.identity.select.revokedAt)), Query.where(queryOr(queryAnd(queryEq(tables.identity.select.kind, "email"), queryIsNull(tables.identity.select.verifiedAt)), queryGt(usableCount, 1))), Query.returning({
|
|
2932
|
+
...selectIdentityColumns(tables),
|
|
2933
|
+
revokedAt: queryWithoutNullRefinement(tables.identity.select.revokedAt),
|
|
2934
|
+
metadata: queryWithoutNullRefinement(tables.identity.select.metadata),
|
|
2935
|
+
}));
|
|
2936
|
+
const next = aliasTable(tables.identity.table, "next_identity");
|
|
2937
|
+
const nextColumn = (column) => aliasColumn(next, column);
|
|
2938
|
+
const nextId = queryScalar(Query.select({ id: nextColumn(tables.identity.select.id) }).pipe(Query.from(next), Query.where(queryEq(nextColumn(tables.identity.select.userId), input.userId)), Query.where(queryIsNull(nextColumn(tables.identity.select.revokedAt))), Query.where(queryOr(queryNeq(nextColumn(tables.identity.select.kind), "email"), queryIsNotNull(nextColumn(tables.identity.select.verifiedAt)))), Query.orderBy(nextColumn(tables.identity.select.createdAt), "asc"), Query.orderBy(nextColumn(tables.identity.select.id), "asc"), Query.limit(1)));
|
|
2939
|
+
const primary = aliasTable(tables.identity.table, "existing_primary");
|
|
2940
|
+
const primaryColumn = (column) => aliasColumn(primary, column);
|
|
2941
|
+
const primaryExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(primary), Query.where(queryEq(primaryColumn(tables.identity.select.userId), input.userId)), Query.where(queryIsNull(primaryColumn(tables.identity.select.revokedAt))), Query.where(queryEq(primaryColumn(tables.identity.select.isPrimaryLogin), 1))));
|
|
2942
|
+
const promotePlan = queryUpdate(tables.identity.table, mutationValues(tables.identity.setPrimary({
|
|
2943
|
+
userId: input.userId,
|
|
2944
|
+
identityId: input.id,
|
|
2945
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
2946
|
+
updatedAt: input.revokedAt,
|
|
2947
|
+
}, true))).pipe(Query.where(queryEq(tables.identity.select.id, nextId)), Query.where(queryNot(primaryExists)), Query.where(queryChangedOne()));
|
|
2948
|
+
return executeAtomicPlans(atomic, [
|
|
2949
|
+
revokePlan,
|
|
2950
|
+
promotePlan,
|
|
2951
|
+
]).pipe(Effect.mapError((cause) => storageError("identity", "update", cause)), Effect.map(([rows]) => rows[0] === undefined
|
|
2952
|
+
? Option.none()
|
|
2953
|
+
: Option.some(decodeUserIdentityRow(tables, rows[0]))));
|
|
2954
|
+
})),
|
|
2955
|
+
});
|
|
2956
|
+
};
|
|
2957
|
+
export const makeEffectQbSqliteRegistrationStore = (executor, atomic, options = {}) => {
|
|
2958
|
+
const tables = options.tables ?? authSqliteTables;
|
|
2959
|
+
const identityStore = makeEffectQbSqliteIdentityStore(executor, atomic, options);
|
|
2960
|
+
const credentialStore = makeEffectQbSqliteCredentialStore(executor, options);
|
|
2961
|
+
const classify = (input, cause) => {
|
|
2962
|
+
const failure = () => Effect.fail(new RegistrationFailureError({
|
|
2963
|
+
message: "Atomic registration failed",
|
|
2964
|
+
cause,
|
|
2965
|
+
}));
|
|
2966
|
+
if (cause instanceof AtomicPlanError && cause.phase !== "execute") {
|
|
2967
|
+
return failure();
|
|
2968
|
+
}
|
|
2969
|
+
const statement = cause instanceof AtomicPlanError ? (cause.statement ?? -1) : -1;
|
|
2970
|
+
if (statement >= 0 && !isAtomicExecuteConflict(cause, statement)) {
|
|
2971
|
+
return failure();
|
|
2972
|
+
}
|
|
2973
|
+
if (statement < 0) {
|
|
2974
|
+
return Effect.gen(function* () {
|
|
2975
|
+
if (Option.isSome(yield* findUserById(executor, options.tables ?? authSqliteTables, input.user.id))) {
|
|
2976
|
+
return yield* new RegistrationConflictError({ reason: "user-id" });
|
|
2977
|
+
}
|
|
2978
|
+
if (Option.isSome(yield* identityStore.findById(input.identity.id))) {
|
|
2979
|
+
return yield* new RegistrationConflictError({
|
|
2980
|
+
reason: "identity-id",
|
|
2981
|
+
});
|
|
2982
|
+
}
|
|
2983
|
+
if (Option.isSome(yield* identityStore.findByKindAndNormalizedValue({
|
|
2984
|
+
scope: input.identity.scope,
|
|
2985
|
+
kind: input.identity.kind,
|
|
2986
|
+
normalizedValue: input.identity.normalizedValue,
|
|
2987
|
+
}))) {
|
|
2988
|
+
return yield* new RegistrationConflictError({
|
|
2989
|
+
reason: "identity-value",
|
|
2990
|
+
});
|
|
2991
|
+
}
|
|
2992
|
+
if (input.passwordCredential !== undefined) {
|
|
2993
|
+
const credential = yield* credentialStore.findPasswordByUserId(input.user.id);
|
|
2994
|
+
if (Option.isSome(credential)) {
|
|
2995
|
+
return yield* new RegistrationConflictError({
|
|
2996
|
+
reason: credential.value.id === input.passwordCredential.id
|
|
2997
|
+
? "credential-id"
|
|
2998
|
+
: "password-credential",
|
|
2999
|
+
});
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
return yield* new RegistrationFailureError({
|
|
3003
|
+
message: "Atomic registration failed",
|
|
3004
|
+
cause,
|
|
3005
|
+
});
|
|
3006
|
+
}).pipe(Effect.catch((error) => error instanceof RegistrationConflictError ||
|
|
3007
|
+
error instanceof RegistrationFailureError
|
|
3008
|
+
? Effect.fail(error)
|
|
3009
|
+
: Effect.fail(new RegistrationFailureError({
|
|
3010
|
+
message: "Registration conflict classification failed",
|
|
3011
|
+
cause: error,
|
|
3012
|
+
}))));
|
|
3013
|
+
}
|
|
3014
|
+
if (statement === 0) {
|
|
3015
|
+
return Effect.fail(new RegistrationConflictError({ reason: "user-id" }));
|
|
3016
|
+
}
|
|
3017
|
+
if (statement === 1) {
|
|
3018
|
+
return identityStore.findById(input.identity.id).pipe(Effect.flatMap((found) => Option.isSome(found)
|
|
3019
|
+
? Effect.fail(new RegistrationConflictError({ reason: "identity-id" }))
|
|
3020
|
+
: Effect.fail(new RegistrationConflictError({ reason: "identity-value" }))), Effect.catch(() => Effect.fail(new RegistrationConflictError({ reason: "identity-value" }))));
|
|
3021
|
+
}
|
|
3022
|
+
if (statement === 2 && input.passwordCredential !== undefined) {
|
|
3023
|
+
return credentialStore.findPasswordByUserId(input.user.id).pipe(Effect.flatMap((found) => Effect.fail(new RegistrationConflictError({
|
|
3024
|
+
reason: Option.isSome(found) &&
|
|
3025
|
+
found.value.id === input.passwordCredential?.id
|
|
3026
|
+
? "credential-id"
|
|
3027
|
+
: "password-credential",
|
|
3028
|
+
}))), Effect.catch(() => Effect.fail(new RegistrationConflictError({ reason: "password-credential" }))));
|
|
3029
|
+
}
|
|
3030
|
+
return failure();
|
|
3031
|
+
};
|
|
3032
|
+
return RegistrationStore.of({
|
|
3033
|
+
create: (input) => {
|
|
3034
|
+
if (input.identity.userId !== input.user.id ||
|
|
3035
|
+
(input.passwordCredential !== undefined &&
|
|
3036
|
+
input.passwordCredential.userId !== input.user.id)) {
|
|
3037
|
+
return Effect.fail(new RegistrationConflictError({ reason: "user-id" }));
|
|
3038
|
+
}
|
|
3039
|
+
const userPlan = queryInsert(tables.user.table, mutationValues(tables.user.insert(input.user)));
|
|
3040
|
+
const identityPlan = queryInsert(tables.identity.table, mutationValues(tables.identity.insert(input.identity)));
|
|
3041
|
+
const plans = input.passwordCredential === undefined
|
|
3042
|
+
? [userPlan, identityPlan]
|
|
3043
|
+
: [
|
|
3044
|
+
userPlan,
|
|
3045
|
+
identityPlan,
|
|
3046
|
+
queryInsert(tables.credential.table, mutationValues(tables.credential.insertPassword(input.passwordCredential))),
|
|
3047
|
+
];
|
|
3048
|
+
return atomic.execute(plans).pipe(Effect.map(() => ({
|
|
3049
|
+
user: input.user,
|
|
3050
|
+
identity: {
|
|
3051
|
+
...input.identity,
|
|
3052
|
+
isPrimaryLogin: input.identity.isPrimaryLogin ?? false,
|
|
3053
|
+
updatedAt: input.identity.createdAt,
|
|
3054
|
+
},
|
|
3055
|
+
...(input.passwordCredential === undefined
|
|
3056
|
+
? {}
|
|
3057
|
+
: { passwordCredential: input.passwordCredential }),
|
|
3058
|
+
})), Effect.catch((error) => classify(input, error)));
|
|
3059
|
+
},
|
|
2224
3060
|
});
|
|
2225
3061
|
};
|
|
2226
3062
|
export const makeEffectQbSqliteCredentialStore = (executor, options = {}) => {
|
|
2227
3063
|
const tables = options.tables ?? authSqliteTables;
|
|
2228
|
-
return CredentialStore.
|
|
3064
|
+
return CredentialStore.of({
|
|
2229
3065
|
findPasswordByUserId: (userId) => findPasswordCredentialByUserId(executor, tables, userId),
|
|
2230
3066
|
insertPassword: (row) => executePlan(executor, queryInsert(tables.credential.table, mutationValues(tables.credential.insertPassword(row)))).pipe(Effect.mapError((cause) => storageError("credential", "insert", cause)), Effect.asVoid),
|
|
2231
3067
|
updatePassword: (input) => executePlan(executor, queryUpdate(tables.credential.table, mutationValues(tables.credential.updatePassword(input))).pipe(Query.where(queryEq(tables.credential.select.userId, input.userId)), Query.where(queryEq(tables.credential.select.type, "password")), Query.where(queryIsNull(tables.credential.select.revokedAt)), Query.returning({ id: tables.credential.select.id }))).pipe(Effect.mapError((cause) => storageError("credential", "updatePassword", cause)), Effect.flatMap((rows) => rows[0] === undefined
|
|
@@ -2235,29 +3071,44 @@ export const makeEffectQbSqliteCredentialStore = (executor, options = {}) => {
|
|
|
2235
3071
|
};
|
|
2236
3072
|
export const makeEffectQbSqliteSessionStore = (executor, options = {}) => {
|
|
2237
3073
|
const tables = options.tables ?? authSqliteTables;
|
|
2238
|
-
return SessionStore.
|
|
3074
|
+
return SessionStore.of({
|
|
2239
3075
|
findById: (id) => findSessionById(executor, tables, id),
|
|
2240
3076
|
listByUser: (input) => listSessionsByUser(executor, tables, input),
|
|
2241
|
-
insert: (row) => executePlan(executor, queryInsert(tables.session.table, mutationValues(tables.session.insert(row))))
|
|
2242
|
-
refresh: (input) =>
|
|
2243
|
-
|
|
2244
|
-
|
|
3077
|
+
insert: (row) => decodeSessionResult("insert", () => parseAuthenticationEvents(row.authenticationEvents)).pipe(Effect.flatMap(() => executePlan(executor, queryInsert(tables.session.table, mutationValues(tables.session.insert(row))))), Effect.mapError((cause) => storageError("session", "insert", cause)), Effect.asVoid),
|
|
3078
|
+
refresh: (input) => findSessionForMutation(executor, tables, input.sessionId, "refresh").pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
3079
|
+
? Effect.succeed(Option.none())
|
|
3080
|
+
: executePlan(executor, queryUpdate(tables.session.table, mutationValues(tables.session.refresh(input))).pipe(Query.where(queryEq(tables.session.select.id, input.sessionId)), Query.where(queryEq(tables.session.select.authenticationEvents, existing.value.authenticationEvents)), Query.returning(selectSessionColumns(tables)))).pipe(Effect.mapError((cause) => storageError("session", "refresh", cause)), Effect.flatMap((rows) => decodeSessionResult("refresh", () => rows[0] === undefined
|
|
2245
3081
|
? Option.none()
|
|
2246
|
-
: Option.some(decodeSessionRow(tables, rows[0]))))
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
return executePlan(executor, query).pipe(Effect.mapError((cause) => storageError("session", "rotate", cause)), Effect.map((rows) => rows[0] === undefined
|
|
3082
|
+
: Option.some(decodeSessionRow(tables, rows[0]))))))),
|
|
3083
|
+
rotate: (input) => findSessionForMutation(executor, tables, input.sessionId, "rotate").pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
3084
|
+
? Effect.succeed(Option.none())
|
|
3085
|
+
: executePlan(executor, queryUpdate(tables.session.table, mutationValues(tables.session.rotate(input))).pipe(Query.where(queryEq(tables.session.select.id, input.sessionId)), Query.where(queryEq(tables.session.select.authenticationEvents, existing.value.authenticationEvents)), Query.returning(selectSessionColumns(tables)))).pipe(Effect.mapError((cause) => storageError("session", "rotate", cause)), Effect.flatMap((rows) => decodeSessionResult("rotate", () => rows[0] === undefined
|
|
2251
3086
|
? Option.none()
|
|
2252
|
-
: Option.some(decodeSessionRow(tables, rows[0]))))
|
|
2253
|
-
|
|
2254
|
-
|
|
3087
|
+
: Option.some(decodeSessionRow(tables, rows[0]))))))),
|
|
3088
|
+
updateClaims: (input) => findSessionForMutation(executor, tables, input.sessionId, "updateClaims").pipe(Effect.flatMap((selected) => {
|
|
3089
|
+
if (Option.isNone(selected)) {
|
|
3090
|
+
return Effect.succeed(Option.none());
|
|
3091
|
+
}
|
|
3092
|
+
return executePlan(executor, queryUpdate(tables.session.table, mutationValues(tables.session.updateClaims(selected.value.row, input))).pipe(Query.where(queryEq(tables.session.select.id, input.sessionId)), Query.where(queryEq(tables.session.select.authenticationEvents, selected.value.authenticationEvents)), Query.returning(selectSessionColumns(tables)))).pipe(Effect.mapError((cause) => storageError("session", "updateClaims", cause)), Effect.flatMap((rows) => decodeSessionResult("updateClaims", () => rows[0] === undefined
|
|
3093
|
+
? Option.none()
|
|
3094
|
+
: Option.some(decodeSessionRow(tables, rows[0])))));
|
|
3095
|
+
})),
|
|
3096
|
+
appendEvidence: (input) => decodeSessionResult("appendEvidence", () => parseAuthenticationEvents(input.authenticationEvents)).pipe(Effect.flatMap((authenticationEvents) => findSessionForMutation(executor, tables, input.sessionId, "appendEvidence").pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
3097
|
+
? Effect.succeed(Option.none())
|
|
3098
|
+
: executePlan(executor, queryUpdate(tables.session.table, mutationValues(tables.session.appendEvidence({
|
|
3099
|
+
...input,
|
|
3100
|
+
authenticationEvents,
|
|
3101
|
+
}))).pipe(Query.where(queryEq(tables.session.select.id, input.sessionId)), Query.where(queryEq(tables.session.select.authenticationEvents, existing.value.authenticationEvents)), Query.returning(selectSessionColumns(tables)))).pipe(Effect.mapError((cause) => storageError("session", "appendEvidence", cause)), Effect.flatMap((rows) => decodeSessionResult("appendEvidence", () => rows[0] === undefined
|
|
3102
|
+
? Option.none()
|
|
3103
|
+
: Option.some(decodeSessionRow(tables, rows[0]))))))))),
|
|
3104
|
+
assureAndRotate: (input) => decodeSessionResult("assureAndRotate", () => parseAuthenticationEvents(input.authenticationEvents)).pipe(Effect.flatMap(() => findSessionById(executor, tables, input.sessionId, "assureAndRotate")), Effect.flatMap((rowOption) => {
|
|
2255
3105
|
if (Option.isNone(rowOption)) {
|
|
2256
3106
|
return Effect.succeed(Option.none());
|
|
2257
3107
|
}
|
|
2258
|
-
|
|
3108
|
+
const query = queryUpdate(tables.session.table, mutationValues(tables.session.assureAndRotate(rowOption.value, input))).pipe(Query.where(queryEq(tables.session.select.id, input.sessionId)), Query.where(queryEq(tables.session.select.secretHash, input.expectedSecretHash)), Query.where(queryEq(tables.session.select.authenticationEvents, JSON.stringify(input.expectedAuthenticationEvents))), Query.where(queryIsNull(tables.session.select.revokedAt)), Query.where(queryGt(tables.session.select.expiresAt, Number(input.now))), Query.returning(selectSessionColumns(tables)));
|
|
3109
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => storageError("session", "assureAndRotate", cause)), Effect.flatMap((rows) => decodeSessionResult("assureAndRotate", () => rows[0] === undefined
|
|
2259
3110
|
? Option.none()
|
|
2260
|
-
: Option.some(decodeSessionRow(tables, rows[0]))));
|
|
3111
|
+
: Option.some(decodeSessionRow(tables, rows[0])))));
|
|
2261
3112
|
})),
|
|
2262
3113
|
revoke: (input) => executePlan(executor, queryUpdate(tables.session.table, mutationValues(tables.session.revoke(input))).pipe(Query.where(queryEq(tables.session.select.id, input.sessionId)))).pipe(Effect.mapError((cause) => storageError("session", "revoke", cause)), Effect.asVoid),
|
|
2263
3114
|
revokeAllForUser: (input) => executePlan(executor, queryUpdate(tables.session.table, mutationValues(tables.session.revoke(input))).pipe(Query.where(queryEq(tables.session.select.userId, input.userId)))).pipe(Effect.mapError((cause) => storageError("session", "revokeAllForUser", cause)), Effect.asVoid),
|
|
@@ -2265,7 +3116,7 @@ export const makeEffectQbSqliteSessionStore = (executor, options = {}) => {
|
|
|
2265
3116
|
};
|
|
2266
3117
|
export const makeEffectQbSqliteVerificationStore = (executor, options = {}) => {
|
|
2267
3118
|
const tables = options.tables ?? authSqliteTables;
|
|
2268
|
-
return VerificationStore.
|
|
3119
|
+
return VerificationStore.of({
|
|
2269
3120
|
findById: (id) => findVerificationById(executor, tables, id),
|
|
2270
3121
|
insert: (row) => executePlan(executor, queryInsert(tables.verification.table, mutationValues(tables.verification.insert(row)))).pipe(Effect.mapError((cause) => storageError("verification", "insert", cause)), Effect.asVoid),
|
|
2271
3122
|
consume: (input) => {
|
|
@@ -2293,10 +3144,13 @@ export const makeEffectQbSqliteVerificationStore = (executor, options = {}) => {
|
|
|
2293
3144
|
};
|
|
2294
3145
|
export const makeEffectQbSqliteAuditLogStore = (executor, options = {}) => {
|
|
2295
3146
|
const tables = options.tables ?? authSqliteTables;
|
|
2296
|
-
return AuditLogStore.
|
|
2297
|
-
|
|
3147
|
+
return AuditLogStore.of({
|
|
3148
|
+
insertMany: (events) => Effect.gen(function* () {
|
|
3149
|
+
if (events.length === 0) {
|
|
3150
|
+
return;
|
|
3151
|
+
}
|
|
2298
3152
|
const createdAt = yield* currentUnixMillis;
|
|
2299
|
-
yield* executePlan(executor,
|
|
3153
|
+
yield* executePlan(executor, queryInsertMany(tables.auditLog.table, events.map((event) => mutationValues(tables.auditLog.insert(event, createdAt))))).pipe(Effect.mapError((cause) => auditLogStoreError("insertMany", cause)), Effect.asVoid);
|
|
2300
3154
|
}),
|
|
2301
3155
|
list: (input = {}) => listAuditLogRecords(executor, tables, input),
|
|
2302
3156
|
deleteBefore: (input) => deleteAuditLogRecordsBefore(executor, tables, input),
|
|
@@ -2304,7 +3158,7 @@ export const makeEffectQbSqliteAuditLogStore = (executor, options = {}) => {
|
|
|
2304
3158
|
};
|
|
2305
3159
|
export const makeEffectQbSqliteLoginApprovalReviewStore = (executor, options = {}) => {
|
|
2306
3160
|
const tables = options.tables ?? authSqliteTables;
|
|
2307
|
-
return LoginApprovalReviewStore.
|
|
3161
|
+
return LoginApprovalReviewStore.of({
|
|
2308
3162
|
insertPending: (input) => executePlan(executor, queryInsert(tables.loginApprovalReview.table, mutationValues(tables.loginApprovalReview.insertPending(input)))).pipe(Effect.mapError((cause) => loginApprovalReviewStoreError("insert", cause)), Effect.as(input)),
|
|
2309
3163
|
findByApprovalChallengeId: (input) => findLoginApprovalReviewByApprovalChallengeId(executor, tables, input),
|
|
2310
3164
|
approve: (input) => executePlan(executor, queryUpdate(tables.loginApprovalReview.table, mutationValues(tables.loginApprovalReview.approve(input))).pipe(Query.where(queryEq(tables.loginApprovalReview.select.approvalChallengeId, input.approvalChallengeId)), Query.where(queryEq(tables.loginApprovalReview.select.status, "pending")), Query.returning(selectLoginApprovalReviewColumns(tables)))).pipe(Effect.mapError((cause) => loginApprovalReviewStoreError("approve", cause)), Effect.map((rows) => rows[0] === undefined
|
|
@@ -2317,7 +3171,7 @@ export const makeEffectQbSqliteLoginApprovalReviewStore = (executor, options = {
|
|
|
2317
3171
|
};
|
|
2318
3172
|
export const makeEffectQbSqliteLoginRiskHistoryStore = (executor, options = {}) => {
|
|
2319
3173
|
const tables = options.tables ?? authSqliteTables;
|
|
2320
|
-
return LoginRiskHistory.
|
|
3174
|
+
return LoginRiskHistory.of({
|
|
2321
3175
|
lookup: (input) => lookupLoginRiskHistory(executor, tables, input, options.loginRiskHistory),
|
|
2322
3176
|
record: (input) => Effect.gen(function* () {
|
|
2323
3177
|
const createdAt = input.createdAt ?? (yield* currentUnixMillis);
|
|
@@ -2328,15 +3182,21 @@ export const makeEffectQbSqliteLoginRiskHistoryStore = (executor, options = {})
|
|
|
2328
3182
|
};
|
|
2329
3183
|
export const makeEffectQbSqliteTrustedDeviceStore = (executor, options = {}) => {
|
|
2330
3184
|
const tables = options.tables ?? authSqliteTables;
|
|
2331
|
-
|
|
3185
|
+
const updateExisting = (input, existing) => executePlan(executor, queryUpdate(tables.trustedDevice.table, mutationValues(tables.trustedDevice.update(input, existing))).pipe(Query.where(queryEq(tables.trustedDevice.select.userId, input.userId)), Query.where(queryEq(tables.trustedDevice.select.tokenHash, input.tokenHash)), Query.where(queryLte(tables.trustedDevice.select.lastSeenAt, Number(input.now))))).pipe(Effect.mapError((cause) => trustedDeviceStoreError("trust", cause)), Effect.asVoid);
|
|
3186
|
+
const updateAfterInsertConflict = (input, originalError) => findTrustedDeviceIncludingExpired(executor, tables, input).pipe(Effect.flatMap((existing) => Option.isSome(existing)
|
|
3187
|
+
? updateExisting(input, existing.value)
|
|
3188
|
+
: Effect.fail(trustedDeviceStoreError("trust", originalError))));
|
|
3189
|
+
return TrustedDeviceStore.of({
|
|
2332
3190
|
find: (input) => findTrustedDevice(executor, tables, input),
|
|
2333
3191
|
listForUser: (input) => listTrustedDevicesForUser(executor, tables, input),
|
|
2334
3192
|
revokeForUser: (input) => executePlan(executor, Query.delete(tables.trustedDevice.table).pipe(Query.where(queryEq(tables.trustedDevice.select.userId, input.userId)), Query.where(queryEq(tables.trustedDevice.select.tokenHash, input.tokenHash)))).pipe(Effect.mapError((cause) => trustedDeviceStoreError("revoke", cause)), Effect.asVoid),
|
|
2335
3193
|
upsert: (input) => findTrustedDeviceIncludingExpired(executor, tables, input).pipe(Effect.flatMap((existing) => {
|
|
2336
3194
|
if (Option.isNone(existing)) {
|
|
2337
|
-
return executePlan(executor, queryInsert(tables.trustedDevice.table, mutationValues(tables.trustedDevice.insert(input, input.now)))).pipe(Effect.
|
|
3195
|
+
return executePlan(executor, queryInsert(tables.trustedDevice.table, mutationValues(tables.trustedDevice.insert(input, input.now)))).pipe(Effect.catch((error) => sqliteConstraintDiscriminator(error)
|
|
3196
|
+
? updateAfterInsertConflict(input, error)
|
|
3197
|
+
: Effect.fail(trustedDeviceStoreError("trust", error))), Effect.asVoid);
|
|
2338
3198
|
}
|
|
2339
|
-
return
|
|
3199
|
+
return updateExisting(input, existing.value);
|
|
2340
3200
|
}), Effect.flatMap(() => findTrustedDeviceIncludingExpired(executor, tables, input)), Effect.flatMap((stored) => Option.isSome(stored)
|
|
2341
3201
|
? Effect.succeed(stored.value)
|
|
2342
3202
|
: Effect.fail(trustedDeviceStoreError("trust", "missing row")))),
|
|
@@ -2344,7 +3204,7 @@ export const makeEffectQbSqliteTrustedDeviceStore = (executor, options = {}) =>
|
|
|
2344
3204
|
};
|
|
2345
3205
|
export const makeEffectQbSqlitePasskeyCredentialStore = (executor, options = {}) => {
|
|
2346
3206
|
const tables = options.tables ?? authSqliteTables;
|
|
2347
|
-
return PasskeyCredentialStore.
|
|
3207
|
+
return PasskeyCredentialStore.of({
|
|
2348
3208
|
insert: (row) => executePlan(executor, queryInsert(tables.passkeyCredential.table, mutationValues(tables.passkeyCredential.insert(row)))).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("insert", cause)), Effect.asVoid),
|
|
2349
3209
|
findByCredentialId: (credentialId) => findPasskeyCredentialByCredentialId(executor, tables, credentialId),
|
|
2350
3210
|
listByUser: (input) => {
|
|
@@ -2354,7 +3214,7 @@ export const makeEffectQbSqlitePasskeyCredentialStore = (executor, options = {})
|
|
|
2354
3214
|
: base.pipe(Query.where(queryIsNull(tables.passkeyCredential.select.revokedAt)));
|
|
2355
3215
|
return executePlan(executor, query).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodePasskeyCredentialRow(tables, row))));
|
|
2356
3216
|
},
|
|
2357
|
-
updateSignCount: (input) => executePlan(executor, queryUpdate(tables.passkeyCredential.table, mutationValues(tables.passkeyCredential.updateSignCount(input))).pipe(Query.where(queryEq(tables.passkeyCredential.select.credentialId, input.credentialId)), Query.where(queryIsNull(tables.passkeyCredential.select.revokedAt)), Query.returning(selectPasskeyCredentialColumns(tables)))).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("update-sign-count", cause)), Effect.map((rows) => rows[0] === undefined
|
|
3217
|
+
updateSignCount: (input) => executePlan(executor, queryUpdate(tables.passkeyCredential.table, mutationValues(tables.passkeyCredential.updateSignCount(input))).pipe(Query.where(queryEq(tables.passkeyCredential.select.credentialId, input.credentialId)), Query.where(queryEq(tables.passkeyCredential.select.signCount, input.expectedSignCount)), Query.where(queryIsNull(tables.passkeyCredential.select.revokedAt)), Query.returning(selectPasskeyCredentialColumns(tables)))).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("update-sign-count", cause)), Effect.map((rows) => rows[0] === undefined
|
|
2358
3218
|
? Option.none()
|
|
2359
3219
|
: Option.some(decodePasskeyCredentialRow(tables, rows[0])))),
|
|
2360
3220
|
revoke: (input) => findPasskeyCredentialByCredentialId(executor, tables, input.credentialId, "revoke").pipe(Effect.flatMap((row) => {
|
|
@@ -2367,7 +3227,7 @@ export const makeEffectQbSqlitePasskeyCredentialStore = (executor, options = {})
|
|
|
2367
3227
|
};
|
|
2368
3228
|
export const makeEffectQbSqliteTotpFactorStore = (executor, options = {}) => {
|
|
2369
3229
|
const tables = options.tables ?? authSqliteTables;
|
|
2370
|
-
return TotpFactorStore.
|
|
3230
|
+
return TotpFactorStore.of({
|
|
2371
3231
|
insert: (row) => executePlan(executor, queryInsert(tables.totpFactor.table, mutationValues(tables.totpFactor.insert(row)))).pipe(Effect.mapError((cause) => totpFactorStoreError("insert", cause)), Effect.asVoid),
|
|
2372
3232
|
findById: (id) => findTotpFactorById(executor, tables, id),
|
|
2373
3233
|
listByUser: (input) => {
|
|
@@ -2383,9 +3243,35 @@ export const makeEffectQbSqliteTotpFactorStore = (executor, options = {}) => {
|
|
|
2383
3243
|
confirm: (input) => executePlan(executor, queryUpdate(tables.totpFactor.table, mutationValues(tables.totpFactor.confirm(input))).pipe(Query.where(queryEq(tables.totpFactor.select.id, input.id)), Query.where(queryIsNull(tables.totpFactor.select.revokedAt)), Query.returning(selectTotpFactorColumns(tables)))).pipe(Effect.mapError((cause) => totpFactorStoreError("confirm", cause)), Effect.map((rows) => rows[0] === undefined
|
|
2384
3244
|
? Option.none()
|
|
2385
3245
|
: Option.some(decodeTotpFactorRow(tables, rows[0])))),
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
3246
|
+
acceptCounter: (input) => {
|
|
3247
|
+
const attempt = (initial) => executePlan(executor, queryUpdate(tables.totpFactor.table, mutationValues(tables.totpFactor.acceptCounter(input))).pipe(Query.where(queryEq(tables.totpFactor.select.id, input.id)), Query.where(queryIsNull(tables.totpFactor.select.revokedAt)), Query.where(queryGt(tables.totpFactor.select.confirmedAt, 0)), Query.where(initial
|
|
3248
|
+
? queryIsNull(tables.totpFactor.select.lastAcceptedCounter)
|
|
3249
|
+
: queryLt(tables.totpFactor.select.lastAcceptedCounter, input.counter)), Query.returning({ id: tables.totpFactor.select.id })));
|
|
3250
|
+
return Effect.gen(function* () {
|
|
3251
|
+
let rows = yield* attempt(true).pipe(Effect.mapError((cause) => totpFactorStoreError("accept-counter", cause)));
|
|
3252
|
+
if (rows[0] === undefined) {
|
|
3253
|
+
rows = yield* attempt(false).pipe(Effect.mapError((cause) => totpFactorStoreError("accept-counter", cause)));
|
|
3254
|
+
}
|
|
3255
|
+
if (rows[0] !== undefined) {
|
|
3256
|
+
const factor = yield* findTotpFactorById(executor, tables, input.id, "accept-counter");
|
|
3257
|
+
if (Option.isNone(factor)) {
|
|
3258
|
+
return { _tag: "Unavailable" };
|
|
3259
|
+
}
|
|
3260
|
+
return {
|
|
3261
|
+
_tag: "Accepted",
|
|
3262
|
+
factor: factor.value,
|
|
3263
|
+
};
|
|
3264
|
+
}
|
|
3265
|
+
const factor = yield* findTotpFactorById(executor, tables, input.id, "accept-counter");
|
|
3266
|
+
return Option.isSome(factor) &&
|
|
3267
|
+
factor.value.confirmedAt !== undefined &&
|
|
3268
|
+
factor.value.revokedAt === undefined &&
|
|
3269
|
+
factor.value.lastAcceptedCounter !== undefined &&
|
|
3270
|
+
factor.value.lastAcceptedCounter >= input.counter
|
|
3271
|
+
? { _tag: "Replay" }
|
|
3272
|
+
: { _tag: "Unavailable" };
|
|
3273
|
+
});
|
|
3274
|
+
},
|
|
2389
3275
|
revoke: (input) => findTotpFactorById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
|
|
2390
3276
|
if (Option.isNone(row)) {
|
|
2391
3277
|
return Effect.void;
|
|
@@ -2394,9 +3280,9 @@ export const makeEffectQbSqliteTotpFactorStore = (executor, options = {}) => {
|
|
|
2394
3280
|
})),
|
|
2395
3281
|
});
|
|
2396
3282
|
};
|
|
2397
|
-
export const makeEffectQbSqliteRecoveryCodeStore = (executor, options = {}) => {
|
|
3283
|
+
export const makeEffectQbSqliteRecoveryCodeStore = (executor, atomic, options = {}) => {
|
|
2398
3284
|
const tables = options.tables ?? authSqliteTables;
|
|
2399
|
-
return RecoveryCodeStore.
|
|
3285
|
+
return RecoveryCodeStore.of({
|
|
2400
3286
|
insertMany: (rows) => Effect.forEach(rows, (row) => executePlan(executor, queryInsert(tables.recoveryCode.table, mutationValues(tables.recoveryCode.insert(row)))).pipe(Effect.mapError((cause) => recoveryCodeStoreError("insert", cause)), Effect.asVoid), { discard: true }),
|
|
2401
3287
|
findById: (id) => findRecoveryCodeById(executor, tables, id),
|
|
2402
3288
|
listByUser: (input) => {
|
|
@@ -2412,6 +3298,13 @@ export const makeEffectQbSqliteRecoveryCodeStore = (executor, options = {}) => {
|
|
|
2412
3298
|
markUsed: (input) => executePlan(executor, queryUpdate(tables.recoveryCode.table, mutationValues(tables.recoveryCode.markUsed(input))).pipe(Query.where(queryEq(tables.recoveryCode.select.id, input.id)), Query.where(queryIsNull(tables.recoveryCode.select.usedAt)), Query.where(queryIsNull(tables.recoveryCode.select.revokedAt)), Query.returning({ id: tables.recoveryCode.select.id }))).pipe(Effect.mapError((cause) => recoveryCodeStoreError("mark-used", cause)), Effect.flatMap((rows) => rows[0] === undefined
|
|
2413
3299
|
? Effect.succeed(Option.none())
|
|
2414
3300
|
: findRecoveryCodeById(executor, tables, input.id, "mark-used"))),
|
|
3301
|
+
replaceActiveForUser: (input) => {
|
|
3302
|
+
const plans = [
|
|
3303
|
+
queryUpdate(tables.recoveryCode.table, mutationValues(tables.recoveryCode.replaceActive(input, tables.recoveryCode.select.metadata))).pipe(Query.where(queryEq(tables.recoveryCode.select.userId, input.userId)), Query.where(queryIsNull(tables.recoveryCode.select.usedAt)), Query.where(queryIsNull(tables.recoveryCode.select.revokedAt))),
|
|
3304
|
+
...input.rows.map((row) => queryInsert(tables.recoveryCode.table, mutationValues(tables.recoveryCode.insert(row)))),
|
|
3305
|
+
];
|
|
3306
|
+
return atomic.execute(plans).pipe(Effect.mapError((cause) => recoveryCodeStoreError("replace-active", cause)), Effect.asVoid);
|
|
3307
|
+
},
|
|
2415
3308
|
revoke: (input) => findRecoveryCodeById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
|
|
2416
3309
|
if (Option.isNone(row)) {
|
|
2417
3310
|
return Effect.void;
|
|
@@ -2420,9 +3313,219 @@ export const makeEffectQbSqliteRecoveryCodeStore = (executor, options = {}) => {
|
|
|
2420
3313
|
})),
|
|
2421
3314
|
});
|
|
2422
3315
|
};
|
|
3316
|
+
export const makeEffectQbSqliteRecoveryCodeSessionRotateStore = (executor, atomic, options = {}) => {
|
|
3317
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3318
|
+
return RecoveryCodeSessionRotateStore.of({
|
|
3319
|
+
consumeAndRotate: (input) => findSessionById(executor, tables, input.sessionId, "assureAndRotate").pipe(Effect.flatMap((current) => {
|
|
3320
|
+
if (Option.isNone(current) || current.value.userId !== input.userId) {
|
|
3321
|
+
return Effect.succeed(Option.none());
|
|
3322
|
+
}
|
|
3323
|
+
try {
|
|
3324
|
+
assertSessionAssurance(input);
|
|
3325
|
+
}
|
|
3326
|
+
catch (error) {
|
|
3327
|
+
return Effect.fail(new RecoveryCodeSessionRotateStoreError({
|
|
3328
|
+
message: "Session assurance validation failed",
|
|
3329
|
+
cause: error,
|
|
3330
|
+
}));
|
|
3331
|
+
}
|
|
3332
|
+
const metadata = input.claims === undefined
|
|
3333
|
+
? undefined
|
|
3334
|
+
: encodeSessionMetadata({
|
|
3335
|
+
...current.value,
|
|
3336
|
+
claims: input.claims,
|
|
3337
|
+
});
|
|
3338
|
+
const sessionExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.id, input.sessionId)), Query.where(queryEq(tables.session.select.userId, input.userId)), Query.where(queryEq(tables.session.select.secretHash, input.expectedSecretHash)), Query.where(queryEq(tables.session.select.authenticationEvents, JSON.stringify(input.expectedAuthenticationEvents))), Query.where(queryIsNull(tables.session.select.revokedAt)), Query.where(queryGt(tables.session.select.expiresAt, Number(input.now)))));
|
|
3339
|
+
const consumePlan = queryUpdate(tables.recoveryCode.table, mutationValues(tables.recoveryCode.markUsed({
|
|
3340
|
+
id: input.codeId,
|
|
3341
|
+
usedAt: input.usedAt,
|
|
3342
|
+
...(input.codeMetadata === undefined
|
|
3343
|
+
? {}
|
|
3344
|
+
: { metadata: input.codeMetadata }),
|
|
3345
|
+
}))).pipe(Query.where(queryEq(tables.recoveryCode.select.id, input.codeId)), Query.where(queryEq(tables.recoveryCode.select.userId, input.userId)), Query.where(queryIsNull(tables.recoveryCode.select.usedAt)), Query.where(queryIsNull(tables.recoveryCode.select.revokedAt)), Query.where(sessionExists));
|
|
3346
|
+
const sessionValues = mutationValues(tables.session.assureAndRotate(current.value, input));
|
|
3347
|
+
if (metadata !== undefined) {
|
|
3348
|
+
sessionValues[boundColumnName(tables.session.select.metadata)] =
|
|
3349
|
+
metadata;
|
|
3350
|
+
}
|
|
3351
|
+
const rotatePlan = queryUpdate(tables.session.table, sessionValues).pipe(Query.where(queryEq(tables.session.select.id, input.sessionId)), Query.where(queryEq(tables.session.select.userId, input.userId)), Query.where(queryEq(tables.session.select.secretHash, input.expectedSecretHash)), Query.where(queryEq(tables.session.select.authenticationEvents, JSON.stringify(input.expectedAuthenticationEvents))), Query.where(queryIsNull(tables.session.select.revokedAt)), Query.where(queryGt(tables.session.select.expiresAt, Number(input.now))), Query.where(queryChangedOne()), Query.returning({ id: tables.session.select.id }));
|
|
3352
|
+
return executeAtomicPlans(atomic, [
|
|
3353
|
+
consumePlan,
|
|
3354
|
+
rotatePlan,
|
|
3355
|
+
]).pipe(Effect.mapError((cause) => new RecoveryCodeSessionRotateStoreError({
|
|
3356
|
+
message: "Atomic recovery session rotation failed",
|
|
3357
|
+
cause,
|
|
3358
|
+
})), Effect.flatMap((results) => results[1].length === 0
|
|
3359
|
+
? Effect.succeed(Option.none())
|
|
3360
|
+
: findSessionById(executor, tables, input.sessionId, "assureAndRotate").pipe(Effect.mapError((cause) => new RecoveryCodeSessionRotateStoreError({
|
|
3361
|
+
message: "Failed to load rotated session",
|
|
3362
|
+
cause,
|
|
3363
|
+
})))));
|
|
3364
|
+
}), Effect.mapError((cause) => cause instanceof RecoveryCodeSessionRotateStoreError
|
|
3365
|
+
? cause
|
|
3366
|
+
: new RecoveryCodeSessionRotateStoreError({
|
|
3367
|
+
message: "Failed to load current session",
|
|
3368
|
+
cause,
|
|
3369
|
+
}))),
|
|
3370
|
+
});
|
|
3371
|
+
};
|
|
3372
|
+
export const makeEffectQbSqliteTotpSessionRotateStore = (executor, atomic, options = {}) => {
|
|
3373
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3374
|
+
return TotpSessionRotateStore.of({
|
|
3375
|
+
acceptAndRotate: (input) => findSessionById(executor, tables, input.sessionId, "assureAndRotate").pipe(Effect.flatMap((current) => {
|
|
3376
|
+
if (Option.isNone(current) || current.value.userId !== input.userId) {
|
|
3377
|
+
return Effect.succeed(Option.none());
|
|
3378
|
+
}
|
|
3379
|
+
try {
|
|
3380
|
+
assertSessionAssurance(input);
|
|
3381
|
+
}
|
|
3382
|
+
catch (error) {
|
|
3383
|
+
return Effect.fail(new TotpSessionRotateStoreError({
|
|
3384
|
+
message: "Session assurance validation failed",
|
|
3385
|
+
cause: error,
|
|
3386
|
+
}));
|
|
3387
|
+
}
|
|
3388
|
+
const metadata = input.claims === undefined
|
|
3389
|
+
? undefined
|
|
3390
|
+
: encodeSessionMetadata({
|
|
3391
|
+
...current.value,
|
|
3392
|
+
claims: input.claims,
|
|
3393
|
+
});
|
|
3394
|
+
const sessionExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.id, input.sessionId)), Query.where(queryEq(tables.session.select.userId, input.userId)), Query.where(queryEq(tables.session.select.secretHash, input.expectedSecretHash)), Query.where(queryEq(tables.session.select.authenticationEvents, JSON.stringify(input.expectedAuthenticationEvents))), Query.where(queryIsNull(tables.session.select.revokedAt)), Query.where(queryGt(tables.session.select.expiresAt, Number(input.now)))));
|
|
3395
|
+
const acceptPlan = queryUpdate(tables.totpFactor.table, mutationValues(tables.totpFactor.acceptCounter({
|
|
3396
|
+
id: input.factorId,
|
|
3397
|
+
counter: input.counter,
|
|
3398
|
+
lastUsedAt: input.lastUsedAt,
|
|
3399
|
+
...(input.factorMetadata === undefined
|
|
3400
|
+
? {}
|
|
3401
|
+
: { metadata: input.factorMetadata }),
|
|
3402
|
+
}))).pipe(Query.where(queryEq(tables.totpFactor.select.id, input.factorId)), Query.where(queryEq(tables.totpFactor.select.userId, input.userId)), Query.where(queryIsNotNull(tables.totpFactor.select.confirmedAt)), Query.where(queryIsNull(tables.totpFactor.select.revokedAt)), Query.where(queryOr(queryIsNull(tables.totpFactor.select.lastAcceptedCounter), queryLt(tables.totpFactor.select.lastAcceptedCounter, input.counter))), Query.where(sessionExists));
|
|
3403
|
+
const sessionValues = mutationValues(tables.session.assureAndRotate(current.value, input));
|
|
3404
|
+
if (metadata !== undefined) {
|
|
3405
|
+
sessionValues[boundColumnName(tables.session.select.metadata)] =
|
|
3406
|
+
metadata;
|
|
3407
|
+
}
|
|
3408
|
+
const rotatePlan = queryUpdate(tables.session.table, sessionValues).pipe(Query.where(queryEq(tables.session.select.id, input.sessionId)), Query.where(queryEq(tables.session.select.userId, input.userId)), Query.where(queryEq(tables.session.select.secretHash, input.expectedSecretHash)), Query.where(queryEq(tables.session.select.authenticationEvents, JSON.stringify(input.expectedAuthenticationEvents))), Query.where(queryIsNull(tables.session.select.revokedAt)), Query.where(queryGt(tables.session.select.expiresAt, Number(input.now))), Query.where(queryChangedOne()), Query.returning({ id: tables.session.select.id }));
|
|
3409
|
+
return executeAtomicPlans(atomic, [
|
|
3410
|
+
acceptPlan,
|
|
3411
|
+
rotatePlan,
|
|
3412
|
+
]).pipe(Effect.mapError((cause) => new TotpSessionRotateStoreError({
|
|
3413
|
+
message: "Atomic TOTP session rotation failed",
|
|
3414
|
+
cause,
|
|
3415
|
+
})), Effect.flatMap((results) => results[1].length === 0
|
|
3416
|
+
? Effect.succeed(Option.none())
|
|
3417
|
+
: findSessionById(executor, tables, input.sessionId, "assureAndRotate").pipe(Effect.mapError((cause) => new TotpSessionRotateStoreError({
|
|
3418
|
+
message: "Failed to load rotated session",
|
|
3419
|
+
cause,
|
|
3420
|
+
})))));
|
|
3421
|
+
}), Effect.mapError((cause) => cause instanceof TotpSessionRotateStoreError
|
|
3422
|
+
? cause
|
|
3423
|
+
: new TotpSessionRotateStoreError({
|
|
3424
|
+
message: "Failed to load current session",
|
|
3425
|
+
cause,
|
|
3426
|
+
}))),
|
|
3427
|
+
});
|
|
3428
|
+
};
|
|
3429
|
+
export const makeEffectQbSqliteTotpLoginCommitStore = (atomic, options = {}) => {
|
|
3430
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3431
|
+
return TotpLoginCommitStore.of({
|
|
3432
|
+
commit: (input) => {
|
|
3433
|
+
const { verification, session } = input;
|
|
3434
|
+
if (verification.type !== "auth-flow-state" ||
|
|
3435
|
+
verification.subject !== input.userId ||
|
|
3436
|
+
(session === undefined && input.continuation !== "login-approval") ||
|
|
3437
|
+
(session !== undefined && input.continuation !== undefined) ||
|
|
3438
|
+
(session !== undefined && session.userId !== input.userId)) {
|
|
3439
|
+
return Effect.succeed(false);
|
|
3440
|
+
}
|
|
3441
|
+
const factorExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.totpFactor.table), Query.where(queryEq(tables.totpFactor.select.id, input.factorId)), Query.where(queryEq(tables.totpFactor.select.userId, input.userId)), Query.where(queryIsNotNull(tables.totpFactor.select.confirmedAt)), Query.where(queryIsNull(tables.totpFactor.select.revokedAt)), Query.where(queryOr(queryIsNull(tables.totpFactor.select.lastAcceptedCounter), queryLt(tables.totpFactor.select.lastAcceptedCounter, input.counter)))));
|
|
3442
|
+
const verificationPlan = queryUpdate(tables.verification.table, mutationValues(tables.verification.consume({
|
|
3443
|
+
id: verification.id,
|
|
3444
|
+
type: verification.type,
|
|
3445
|
+
consumedAt: input.consumedAt,
|
|
3446
|
+
}))).pipe(Query.where(queryEq(tables.verification.select.id, verification.id)), Query.where(queryEq(tables.verification.select.type, verification.type)), Query.where(queryEq(tables.verification.select.subject, verification.subject)), Query.where(queryIsNotDistinctFrom(tables.verification.select.secretHash, verification.secretHash ?? null)), Query.where(queryEq(tables.verification.select.createdAt, Number(verification.createdAt))), Query.where(queryEq(tables.verification.select.expiresAt, Number(verification.expiresAt))), Query.where(queryIsNotDistinctFrom(tables.verification.select.metadata, jsonEncode(verification.metadata))), Query.where(queryIsNull(tables.verification.select.consumedAt)), Query.where(queryGt(tables.verification.select.expiresAt, Number(input.consumedAt))), Query.where(factorExists));
|
|
3447
|
+
const factorPlan = queryUpdate(tables.totpFactor.table, mutationValues(tables.totpFactor.acceptCounter({
|
|
3448
|
+
id: input.factorId,
|
|
3449
|
+
counter: input.counter,
|
|
3450
|
+
lastUsedAt: input.lastUsedAt,
|
|
3451
|
+
...(input.factorMetadata === undefined
|
|
3452
|
+
? {}
|
|
3453
|
+
: { metadata: input.factorMetadata }),
|
|
3454
|
+
}))).pipe(Query.where(queryEq(tables.totpFactor.select.id, input.factorId)), Query.where(queryEq(tables.totpFactor.select.userId, input.userId)), Query.where(queryIsNotNull(tables.totpFactor.select.confirmedAt)), Query.where(queryIsNull(tables.totpFactor.select.revokedAt)), Query.where(queryOr(queryIsNull(tables.totpFactor.select.lastAcceptedCounter), queryLt(tables.totpFactor.select.lastAcceptedCounter, input.counter))), Query.where(queryChangedOne()), Query.returning({ id: tables.totpFactor.select.id }));
|
|
3455
|
+
if (session === undefined) {
|
|
3456
|
+
return executeAtomicPlans(atomic, [
|
|
3457
|
+
verificationPlan,
|
|
3458
|
+
factorPlan,
|
|
3459
|
+
]).pipe(Effect.map((results) => results[1].length === 1), Effect.mapError((cause) => new TotpLoginCommitStoreError({
|
|
3460
|
+
message: "Atomic TOTP login commit failed",
|
|
3461
|
+
cause,
|
|
3462
|
+
})));
|
|
3463
|
+
}
|
|
3464
|
+
const sessionExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.id, session.id))));
|
|
3465
|
+
const sessionVerificationPlan = verificationPlan.pipe(Query.where(queryNot(sessionExists)));
|
|
3466
|
+
const sessionPlan = queryInsertWhere(tables.session.table, mutationValues(tables.session.insert(session)), queryChangedOne()).pipe(Query.returning({ id: tables.session.select.id }));
|
|
3467
|
+
return executeAtomicPlans(atomic, [
|
|
3468
|
+
sessionVerificationPlan,
|
|
3469
|
+
factorPlan,
|
|
3470
|
+
sessionPlan,
|
|
3471
|
+
]).pipe(Effect.map((results) => results[2].length === 1), Effect.mapError((cause) => new TotpLoginCommitStoreError({
|
|
3472
|
+
message: "Atomic TOTP login commit failed",
|
|
3473
|
+
cause,
|
|
3474
|
+
})));
|
|
3475
|
+
},
|
|
3476
|
+
});
|
|
3477
|
+
};
|
|
3478
|
+
export const makeEffectQbSqliteRecoveryCodeLoginCommitStore = (atomic, options = {}) => {
|
|
3479
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3480
|
+
return RecoveryCodeLoginCommitStore.of({
|
|
3481
|
+
commit: (input) => {
|
|
3482
|
+
const { verification, session } = input;
|
|
3483
|
+
if (verification.type !== "auth-flow-state" ||
|
|
3484
|
+
verification.subject !== input.userId ||
|
|
3485
|
+
(session === undefined && input.continuation !== "login-approval") ||
|
|
3486
|
+
(session !== undefined && input.continuation !== undefined) ||
|
|
3487
|
+
(session !== undefined && session.userId !== input.userId)) {
|
|
3488
|
+
return Effect.succeed(false);
|
|
3489
|
+
}
|
|
3490
|
+
const codeExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.recoveryCode.table), Query.where(queryEq(tables.recoveryCode.select.id, input.codeId)), Query.where(queryEq(tables.recoveryCode.select.userId, input.userId)), Query.where(queryIsNull(tables.recoveryCode.select.usedAt)), Query.where(queryIsNull(tables.recoveryCode.select.revokedAt))));
|
|
3491
|
+
const verificationPlan = queryUpdate(tables.verification.table, mutationValues(tables.verification.consume({
|
|
3492
|
+
id: verification.id,
|
|
3493
|
+
type: verification.type,
|
|
3494
|
+
consumedAt: input.consumedAt,
|
|
3495
|
+
}))).pipe(Query.where(queryEq(tables.verification.select.id, verification.id)), Query.where(queryEq(tables.verification.select.type, verification.type)), Query.where(queryEq(tables.verification.select.subject, verification.subject)), Query.where(queryIsNotDistinctFrom(tables.verification.select.secretHash, verification.secretHash ?? null)), Query.where(queryEq(tables.verification.select.createdAt, Number(verification.createdAt))), Query.where(queryEq(tables.verification.select.expiresAt, Number(verification.expiresAt))), Query.where(queryIsNotDistinctFrom(tables.verification.select.metadata, jsonEncode(verification.metadata))), Query.where(queryIsNull(tables.verification.select.consumedAt)), Query.where(queryGt(tables.verification.select.expiresAt, Number(input.consumedAt))), Query.where(codeExists));
|
|
3496
|
+
const codePlan = queryUpdate(tables.recoveryCode.table, mutationValues(tables.recoveryCode.markUsed({
|
|
3497
|
+
id: input.codeId,
|
|
3498
|
+
usedAt: input.usedAt,
|
|
3499
|
+
...(input.codeMetadata === undefined
|
|
3500
|
+
? {}
|
|
3501
|
+
: { metadata: input.codeMetadata }),
|
|
3502
|
+
}))).pipe(Query.where(queryEq(tables.recoveryCode.select.id, input.codeId)), Query.where(queryEq(tables.recoveryCode.select.userId, input.userId)), Query.where(queryIsNull(tables.recoveryCode.select.usedAt)), Query.where(queryIsNull(tables.recoveryCode.select.revokedAt)), Query.where(queryChangedOne()), Query.returning({ id: tables.recoveryCode.select.id }));
|
|
3503
|
+
if (session === undefined) {
|
|
3504
|
+
return executeAtomicPlans(atomic, [
|
|
3505
|
+
verificationPlan,
|
|
3506
|
+
codePlan,
|
|
3507
|
+
]).pipe(Effect.map((results) => results[1].length === 1), Effect.mapError((cause) => new RecoveryCodeLoginCommitStoreError({
|
|
3508
|
+
message: "Atomic recovery-code login commit failed",
|
|
3509
|
+
cause,
|
|
3510
|
+
})));
|
|
3511
|
+
}
|
|
3512
|
+
const sessionExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.id, session.id))));
|
|
3513
|
+
const sessionVerificationPlan = verificationPlan.pipe(Query.where(queryNot(sessionExists)));
|
|
3514
|
+
const sessionPlan = queryInsertWhere(tables.session.table, mutationValues(tables.session.insert(session)), queryChangedOne()).pipe(Query.returning({ id: tables.session.select.id }));
|
|
3515
|
+
return executeAtomicPlans(atomic, [
|
|
3516
|
+
sessionVerificationPlan,
|
|
3517
|
+
codePlan,
|
|
3518
|
+
sessionPlan,
|
|
3519
|
+
]).pipe(Effect.map((results) => results[2].length === 1), Effect.mapError((cause) => new RecoveryCodeLoginCommitStoreError({
|
|
3520
|
+
message: "Atomic recovery-code login commit failed",
|
|
3521
|
+
cause,
|
|
3522
|
+
})));
|
|
3523
|
+
},
|
|
3524
|
+
});
|
|
3525
|
+
};
|
|
2423
3526
|
export const makeEffectQbSqliteApiKeyStore = (executor, options = {}) => {
|
|
2424
3527
|
const tables = options.tables ?? authSqliteTables;
|
|
2425
|
-
return ApiKeyStore.
|
|
3528
|
+
return ApiKeyStore.of({
|
|
2426
3529
|
insert: (row) => executePlan(executor, queryInsert(tables.apiKey.table, mutationValues(tables.apiKey.insert(row)))).pipe(Effect.mapError((cause) => apiKeyStoreError("insert", cause)), Effect.asVoid),
|
|
2427
3530
|
findById: (id) => findApiKeyById(executor, tables, id),
|
|
2428
3531
|
findByPrefix: (prefix) => findApiKeyByPrefix(executor, tables, prefix),
|
|
@@ -2444,9 +3547,9 @@ export const makeEffectQbSqliteApiKeyStore = (executor, options = {}) => {
|
|
|
2444
3547
|
})),
|
|
2445
3548
|
});
|
|
2446
3549
|
};
|
|
2447
|
-
export const makeEffectQbSqliteRefreshTokenStore = (executor, options = {}) => {
|
|
3550
|
+
export const makeEffectQbSqliteRefreshTokenStore = (executor, atomic, options = {}) => {
|
|
2448
3551
|
const tables = options.tables ?? authSqliteTables;
|
|
2449
|
-
return RefreshTokenStore.
|
|
3552
|
+
return RefreshTokenStore.of({
|
|
2450
3553
|
insert: (row) => executePlan(executor, queryInsert(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(row)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("insert", cause)), Effect.asVoid),
|
|
2451
3554
|
findById: (id) => findRefreshTokenById(executor, tables, id),
|
|
2452
3555
|
findBySecretHash: (hash) => findRefreshTokenBySecretHash(executor, tables, hash),
|
|
@@ -2464,21 +3567,28 @@ export const makeEffectQbSqliteRefreshTokenStore = (executor, options = {}) => {
|
|
|
2464
3567
|
: base.pipe(Query.where(queryIsNull(tables.refreshToken.select.revokedAt)));
|
|
2465
3568
|
return executePlan(executor, query).pipe(Effect.mapError((cause) => refreshTokenStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeRefreshTokenRow(tables, row))));
|
|
2466
3569
|
},
|
|
2467
|
-
rotate: (input) =>
|
|
2468
|
-
const
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
return Effect.succeed(Option.none());
|
|
2475
|
-
}
|
|
2476
|
-
return executePlan(executor, queryInsert(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(input.replacement)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("rotate", cause)), Effect.as(Option.some({
|
|
2477
|
-
previous: previous.value,
|
|
2478
|
-
replacement: input.replacement,
|
|
2479
|
-
})));
|
|
3570
|
+
rotate: (input) => {
|
|
3571
|
+
const { replacement } = input;
|
|
3572
|
+
const rotatePlan = queryUpdate(tables.refreshToken.table, mutationValues(tables.refreshToken.rotate(input))).pipe(Query.where(queryEq(tables.refreshToken.select.id, input.id)), Query.where(queryIsNull(tables.refreshToken.select.rotatedAt)), Query.where(queryIsNull(tables.refreshToken.select.revokedAt)), Query.returning({
|
|
3573
|
+
...selectRefreshTokenColumns(tables),
|
|
3574
|
+
lastUsedAt: queryWithoutNullRefinement(tables.refreshToken.select.lastUsedAt),
|
|
3575
|
+
rotatedAt: queryWithoutNullRefinement(tables.refreshToken.select.rotatedAt),
|
|
3576
|
+
replacedById: queryWithoutNullRefinement(tables.refreshToken.select.replacedById),
|
|
2480
3577
|
}));
|
|
2481
|
-
|
|
3578
|
+
const replacementPlan = queryInsertWhere(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(replacement)), queryChangedOne()).pipe(Query.returning({ id: tables.refreshToken.select.id }));
|
|
3579
|
+
return executeAtomicPlans(atomic, [
|
|
3580
|
+
rotatePlan,
|
|
3581
|
+
replacementPlan,
|
|
3582
|
+
]).pipe(Effect.mapError((cause) => refreshTokenStoreError("rotate", cause)), Effect.map(([previousRows, replacementRows]) => {
|
|
3583
|
+
const [previous] = previousRows;
|
|
3584
|
+
return previous === undefined || replacementRows.length === 0
|
|
3585
|
+
? Option.none()
|
|
3586
|
+
: Option.some({
|
|
3587
|
+
previous: decodeRefreshTokenRow(tables, previous),
|
|
3588
|
+
replacement,
|
|
3589
|
+
});
|
|
3590
|
+
}));
|
|
3591
|
+
},
|
|
2482
3592
|
markReuseDetected: (input) => executePlan(executor, queryUpdate(tables.refreshToken.table, mutationValues(tables.refreshToken.markReuseDetected(input))).pipe(Query.where(queryEq(tables.refreshToken.select.id, input.id)), Query.returning(selectRefreshTokenColumns(tables)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("reuse", cause)), Effect.map((rows) => rows[0] === undefined
|
|
2483
3593
|
? Option.none()
|
|
2484
3594
|
: Option.some(decodeRefreshTokenRow(tables, rows[0])))),
|
|
@@ -2493,7 +3603,7 @@ export const makeEffectQbSqliteRefreshTokenStore = (executor, options = {}) => {
|
|
|
2493
3603
|
};
|
|
2494
3604
|
export const makeEffectQbSqliteJwtRevocationStore = (executor, options = {}) => {
|
|
2495
3605
|
const tables = options.tables ?? authSqliteTables;
|
|
2496
|
-
return JwtRevocationStore.
|
|
3606
|
+
return JwtRevocationStore.of({
|
|
2497
3607
|
revoke: (record) => findJwtRevocation(executor, tables, record.jwtId, "revoke").pipe(Effect.flatMap((existing) => {
|
|
2498
3608
|
if (Option.isNone(existing)) {
|
|
2499
3609
|
return executePlan(executor, queryInsert(tables.jwtRevocation.table, mutationValues(tables.jwtRevocation.insert(record))));
|
|
@@ -2507,7 +3617,7 @@ export const makeEffectQbSqliteJwtRevocationStore = (executor, options = {}) =>
|
|
|
2507
3617
|
};
|
|
2508
3618
|
export const makeEffectQbSqliteOAuthAccountStore = (executor, options = {}) => {
|
|
2509
3619
|
const tables = options.tables ?? authSqliteTables;
|
|
2510
|
-
return OAuthAccountStore.
|
|
3620
|
+
return OAuthAccountStore.of({
|
|
2511
3621
|
insert: (row) => executePlan(executor, queryInsert(tables.oauthAccount.table, mutationValues(tables.oauthAccount.insert(row)))).pipe(Effect.mapError((cause) => oauthAccountStoreError("insert", cause)), Effect.asVoid),
|
|
2512
3622
|
findById: (id) => findOAuthAccountById(executor, tables, id),
|
|
2513
3623
|
findByProviderAccount: (input) => findOAuthAccountByProviderAccount(executor, tables, input),
|
|
@@ -2531,9 +3641,90 @@ export const makeEffectQbSqliteOAuthAccountStore = (executor, options = {}) => {
|
|
|
2531
3641
|
})),
|
|
2532
3642
|
});
|
|
2533
3643
|
};
|
|
3644
|
+
export const makeEffectQbSqliteOAuthIdentityBridgeStore = (executor, atomic, options = {}) => {
|
|
3645
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3646
|
+
const identities = makeEffectQbSqliteIdentityStore(executor, atomic, options);
|
|
3647
|
+
const accounts = makeEffectQbSqliteOAuthAccountStore(executor, options);
|
|
3648
|
+
const classify = (input, cause) => Effect.gen(function* () {
|
|
3649
|
+
if (Option.isSome(yield* findUserById(executor, tables, input.user.id))) {
|
|
3650
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3651
|
+
reason: "user-id",
|
|
3652
|
+
});
|
|
3653
|
+
}
|
|
3654
|
+
if (input.identity !== undefined) {
|
|
3655
|
+
if (Option.isSome(yield* identities.findById(input.identity.id))) {
|
|
3656
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3657
|
+
reason: "identity-id",
|
|
3658
|
+
});
|
|
3659
|
+
}
|
|
3660
|
+
if (Option.isSome(yield* identities.findByKindAndNormalizedValue({
|
|
3661
|
+
scope: input.identity.scope,
|
|
3662
|
+
kind: input.identity.kind,
|
|
3663
|
+
normalizedValue: input.identity.normalizedValue,
|
|
3664
|
+
}))) {
|
|
3665
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3666
|
+
reason: "identity-value",
|
|
3667
|
+
});
|
|
3668
|
+
}
|
|
3669
|
+
}
|
|
3670
|
+
if (Option.isSome(yield* accounts.findById(input.account.id))) {
|
|
3671
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3672
|
+
reason: "oauth-account-id",
|
|
3673
|
+
});
|
|
3674
|
+
}
|
|
3675
|
+
if (Option.isSome(yield* accounts.findByProviderAccount({
|
|
3676
|
+
providerId: input.account.providerId,
|
|
3677
|
+
providerAccountId: input.account.providerAccountId,
|
|
3678
|
+
includeUnlinked: true,
|
|
3679
|
+
}))) {
|
|
3680
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3681
|
+
reason: "provider-account",
|
|
3682
|
+
});
|
|
3683
|
+
}
|
|
3684
|
+
return yield* new OAuthIdentityBridgeStoreError({
|
|
3685
|
+
message: "Atomic OAuth identity creation failed",
|
|
3686
|
+
cause,
|
|
3687
|
+
});
|
|
3688
|
+
}).pipe(Effect.catch((error) => error instanceof OAuthIdentityBridgeConflictError ||
|
|
3689
|
+
error instanceof OAuthIdentityBridgeStoreError
|
|
3690
|
+
? Effect.fail(error)
|
|
3691
|
+
: Effect.fail(new OAuthIdentityBridgeStoreError({
|
|
3692
|
+
message: "OAuth identity conflict classification failed",
|
|
3693
|
+
cause: error,
|
|
3694
|
+
}))));
|
|
3695
|
+
return OAuthIdentityBridgeStore.of({
|
|
3696
|
+
create: (input) => {
|
|
3697
|
+
if (input.account.userId !== input.user.id ||
|
|
3698
|
+
(input.identity !== undefined &&
|
|
3699
|
+
input.identity.userId !== input.user.id)) {
|
|
3700
|
+
return Effect.fail(new OAuthIdentityBridgeConflictError({ reason: "user-id" }));
|
|
3701
|
+
}
|
|
3702
|
+
const plans = [
|
|
3703
|
+
queryInsert(tables.user.table, mutationValues(tables.user.insert(input.user))),
|
|
3704
|
+
];
|
|
3705
|
+
if (input.identity !== undefined) {
|
|
3706
|
+
plans.push(queryInsert(tables.identity.table, mutationValues(tables.identity.insert(input.identity))));
|
|
3707
|
+
}
|
|
3708
|
+
plans.push(queryInsert(tables.oauthAccount.table, mutationValues(tables.oauthAccount.insert(input.account))));
|
|
3709
|
+
return atomic.execute(plans).pipe(Effect.map(() => ({
|
|
3710
|
+
user: input.user,
|
|
3711
|
+
...(input.identity === undefined
|
|
3712
|
+
? {}
|
|
3713
|
+
: {
|
|
3714
|
+
identity: {
|
|
3715
|
+
...input.identity,
|
|
3716
|
+
isPrimaryLogin: input.identity.isPrimaryLogin ?? false,
|
|
3717
|
+
updatedAt: input.identity.createdAt,
|
|
3718
|
+
},
|
|
3719
|
+
}),
|
|
3720
|
+
account: input.account,
|
|
3721
|
+
})), Effect.catch((error) => classify(input, error)));
|
|
3722
|
+
},
|
|
3723
|
+
});
|
|
3724
|
+
};
|
|
2534
3725
|
export const makeEffectQbSqliteOAuthClientStore = (executor, options = {}) => {
|
|
2535
3726
|
const tables = options.tables ?? authSqliteTables;
|
|
2536
|
-
return OAuthClientStore.
|
|
3727
|
+
return OAuthClientStore.of({
|
|
2537
3728
|
upsert: (record) => executePlan(executor, queryInsert(tables.oauthClient.table, mutationValues(tables.oauthClient.insert(record))).pipe(Query.onConflict("id", {
|
|
2538
3729
|
update: mutationValues(tables.oauthClient.update(record)),
|
|
2539
3730
|
}))).pipe(Effect.mapError((cause) => oauthClientStoreError("upsert", cause)), Effect.asVoid),
|
|
@@ -2550,7 +3741,7 @@ export const makeEffectQbSqliteOAuthClientStore = (executor, options = {}) => {
|
|
|
2550
3741
|
};
|
|
2551
3742
|
export const makeEffectQbSqliteOAuthConsentStore = (executor, options = {}) => {
|
|
2552
3743
|
const tables = options.tables ?? authSqliteTables;
|
|
2553
|
-
return OAuthConsentStore.
|
|
3744
|
+
return OAuthConsentStore.of({
|
|
2554
3745
|
upsert: (record) => executePlan(executor, queryInsert(tables.oauthConsent.table, mutationValues(tables.oauthConsent.insert(record))).pipe(Query.onConflict("id", {
|
|
2555
3746
|
update: mutationValues(tables.oauthConsent.update(record)),
|
|
2556
3747
|
}))).pipe(Effect.mapError((cause) => oauthConsentStoreError("upsert", cause)), Effect.asVoid),
|
|
@@ -2574,12 +3765,14 @@ export const makeEffectQbSqliteOAuthConsentStore = (executor, options = {}) => {
|
|
|
2574
3765
|
};
|
|
2575
3766
|
export const makeEffectQbSqliteOAuthAuthorizationCodeStore = (executor, options = {}) => {
|
|
2576
3767
|
const tables = options.tables ?? authSqliteTables;
|
|
2577
|
-
return OAuthAuthorizationCodeStore.
|
|
3768
|
+
return OAuthAuthorizationCodeStore.of({
|
|
2578
3769
|
upsert: (record) => executePlan(executor, queryInsert(tables.oauthAuthorizationCode.table, mutationValues(tables.oauthAuthorizationCode.insert(record))).pipe(Query.onConflict("code_hash", {
|
|
2579
3770
|
update: mutationValues(tables.oauthAuthorizationCode.update(record)),
|
|
2580
3771
|
}))).pipe(Effect.mapError((cause) => oauthAuthorizationCodeStoreError("upsert", cause)), Effect.asVoid),
|
|
2581
3772
|
findByHash: (input) => findOAuthAuthorizationCodeByHash(executor, tables, input),
|
|
2582
|
-
markConsumed: (input) => executePlan(executor, queryUpdate(tables.oauthAuthorizationCode.table, mutationValues(tables.oauthAuthorizationCode.markConsumed(input))).pipe(Query.where(queryEq(tables.oauthAuthorizationCode.select.codeHash, input.codeHash)), Query.where(queryIsNull(tables.oauthAuthorizationCode.select.consumedAt)), Query.returning({
|
|
3773
|
+
markConsumed: (input) => executePlan(executor, queryUpdate(tables.oauthAuthorizationCode.table, mutationValues(tables.oauthAuthorizationCode.markConsumed(input))).pipe(Query.where(queryEq(tables.oauthAuthorizationCode.select.codeHash, input.codeHash)), Query.where(queryIsNull(tables.oauthAuthorizationCode.select.consumedAt)), Query.returning({
|
|
3774
|
+
codeHash: tables.oauthAuthorizationCode.select.codeHash,
|
|
3775
|
+
}))).pipe(Effect.mapError((cause) => oauthAuthorizationCodeStoreError("mark-consumed", cause)), Effect.flatMap((rows) => {
|
|
2583
3776
|
const [consumed] = rows;
|
|
2584
3777
|
if (consumed !== undefined) {
|
|
2585
3778
|
return findOAuthAuthorizationCodeByHash(executor, tables, { codeHash: consumed.codeHash }, "mark-consumed").pipe(Effect.map((record) => Option.isNone(record)
|
|
@@ -2600,8 +3793,8 @@ export const makeEffectQbSqliteOAuthAuthorizationCodeStore = (executor, options
|
|
|
2600
3793
|
};
|
|
2601
3794
|
export const makeEffectQbSqliteOAuthClientSecretStore = (executor, options = {}) => {
|
|
2602
3795
|
const tables = options.tables ?? authSqliteTables;
|
|
2603
|
-
return OAuthClientSecretStore.
|
|
2604
|
-
upsert: (record) => executePlan(executor, queryInsert(tables.oauthClientSecret.table, mutationValues(tables.oauthClientSecret.insert(record))).pipe(Query.onConflict("prefix", {
|
|
3796
|
+
return OAuthClientSecretStore.of({
|
|
3797
|
+
upsert: (record) => executePlan(executor, queryInsert(tables.oauthClientSecret.table, mutationValues(tables.oauthClientSecret.insert(record))).pipe(Query.onConflict(["client_id", "prefix"], {
|
|
2605
3798
|
update: mutationValues(tables.oauthClientSecret.update(record)),
|
|
2606
3799
|
}))).pipe(Effect.mapError((cause) => oauthClientSecretStoreError("upsert", cause)), Effect.asVoid),
|
|
2607
3800
|
findByClientIdAndPrefix: (input) => findOAuthClientSecretByClientIdAndPrefix(executor, tables, input),
|
|
@@ -2620,7 +3813,7 @@ export const makeEffectQbSqliteOAuthClientSecretStore = (executor, options = {})
|
|
|
2620
3813
|
};
|
|
2621
3814
|
export const makeEffectQbSqliteOAuthProviderModeTokenStore = (executor, options = {}) => {
|
|
2622
3815
|
const tables = options.tables ?? authSqliteTables;
|
|
2623
|
-
return OAuthProviderModeTokenStore.
|
|
3816
|
+
return OAuthProviderModeTokenStore.of({
|
|
2624
3817
|
upsert: (record) => executePlan(executor, queryInsert(tables.oauthProviderModeToken.table, mutationValues(tables.oauthProviderModeToken.insert(record))).pipe(Query.onConflict("token_hash", {
|
|
2625
3818
|
update: mutationValues(tables.oauthProviderModeToken.update(record)),
|
|
2626
3819
|
}))).pipe(Effect.mapError((cause) => oauthProviderModeTokenStoreError("upsert", cause)), Effect.asVoid),
|
|
@@ -2645,7 +3838,7 @@ export const makeEffectQbSqliteOAuthProviderModeTokenStore = (executor, options
|
|
|
2645
3838
|
};
|
|
2646
3839
|
export const makeEffectQbSqliteOAuthProviderTokenVault = (executor, options = {}) => {
|
|
2647
3840
|
const tables = options.tables ?? authSqliteTables;
|
|
2648
|
-
return OAuthProviderTokenVault.
|
|
3841
|
+
return OAuthProviderTokenVault.of({
|
|
2649
3842
|
upsert: (record) => findOAuthProviderTokenVaultRecordByAccountId(executor, tables, { accountId: record.accountId }, "upsert", true).pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
2650
3843
|
? executePlan(executor, queryInsert(tables.oauthProviderTokenVault.table, mutationValues(tables.oauthProviderTokenVault.insert(record))))
|
|
2651
3844
|
: executePlan(executor, queryUpdate(tables.oauthProviderTokenVault.table, mutationValues(tables.oauthProviderTokenVault.update(record))).pipe(Query.where(queryEq(tables.oauthProviderTokenVault.select.accountId, record.accountId))))), Effect.mapError((cause) => cause instanceof OAuthProviderTokenVaultError
|
|
@@ -2670,7 +3863,7 @@ export const makeEffectQbSqliteOAuthProviderTokenVault = (executor, options = {}
|
|
|
2670
3863
|
};
|
|
2671
3864
|
export const makeEffectQbSqliteDomainVerificationStore = (executor, options = {}) => {
|
|
2672
3865
|
const tables = options.tables ?? authSqliteTables;
|
|
2673
|
-
return DomainVerificationStore.
|
|
3866
|
+
return DomainVerificationStore.of({
|
|
2674
3867
|
upsert: (record) => findDomainVerificationByOwnerAndDomain(executor, tables, { ownerId: record.ownerId, domain: record.domain }, "upsert").pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
2675
3868
|
? executePlan(executor, queryInsert(tables.domainVerification.table, mutationValues(tables.domainVerification.insert(record))))
|
|
2676
3869
|
: executePlan(executor, queryUpdate(tables.domainVerification.table, mutationValues(tables.domainVerification.update(record))).pipe(Query.where(queryEq(tables.domainVerification.select.ownerId, record.ownerId)), Query.where(queryEq(tables.domainVerification.select.domain, record.domain))))), Effect.mapError((cause) => cause instanceof DomainVerificationStoreError
|
|
@@ -2682,8 +3875,8 @@ export const makeEffectQbSqliteDomainVerificationStore = (executor, options = {}
|
|
|
2682
3875
|
const base = Query.select(selectDomainVerificationColumns(tables)).pipe(Query.from(tables.domainVerification.table), Query.where(queryEq(tables.domainVerification.select.ownerId, input.ownerId)));
|
|
2683
3876
|
const query = input.includeRevoked === true
|
|
2684
3877
|
? base
|
|
2685
|
-
: base.pipe(Query.where(
|
|
2686
|
-
return executePlan(executor, query).pipe(Effect.mapError((cause) => domainVerificationStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeDomainVerificationRecord(tables, row))));
|
|
3878
|
+
: base.pipe(Query.where(queryNeq(tables.domainVerification.select.status, "revoked")));
|
|
3879
|
+
return executePlan(executor, query.pipe(Query.orderBy(tables.domainVerification.select.createdAt, "asc"), Query.orderBy(tables.domainVerification.select.id, "asc"))).pipe(Effect.mapError((cause) => domainVerificationStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeDomainVerificationRecord(tables, row))));
|
|
2687
3880
|
},
|
|
2688
3881
|
markVerified: (input) => executePlan(executor, queryUpdate(tables.domainVerification.table, mutationValues(tables.domainVerification.markVerified(input))).pipe(Query.where(queryEq(tables.domainVerification.select.id, input.id)), Query.where(queryEq(tables.domainVerification.select.status, "pending")), Query.returning(selectDomainVerificationColumns(tables)))).pipe(Effect.mapError((cause) => domainVerificationStoreError("mark-verified", cause)), Effect.map((rows) => rows[0] === undefined
|
|
2689
3882
|
? Option.none()
|
|
@@ -2698,7 +3891,7 @@ export const makeEffectQbSqliteDomainVerificationStore = (executor, options = {}
|
|
|
2698
3891
|
};
|
|
2699
3892
|
export const makeEffectQbSqliteSecurityTimelineStore = (executor, options = {}) => {
|
|
2700
3893
|
const tables = options.tables ?? authSqliteTables;
|
|
2701
|
-
return SecurityTimelineStore.
|
|
3894
|
+
return SecurityTimelineStore.of({
|
|
2702
3895
|
insert: (event) => executePlan(executor, queryInsert(tables.securityTimeline.table, mutationValues(tables.securityTimeline.insert(event)))).pipe(Effect.mapError((cause) => securityTimelineStoreError("insert", cause)), Effect.asVoid),
|
|
2703
3896
|
listByUser: (input) => listSecurityTimelineByUser(executor, tables, input),
|
|
2704
3897
|
deleteBefore: (input) => deleteSecurityTimelineBefore(executor, tables, input),
|
|
@@ -2706,7 +3899,7 @@ export const makeEffectQbSqliteSecurityTimelineStore = (executor, options = {})
|
|
|
2706
3899
|
};
|
|
2707
3900
|
export const makeEffectQbSqliteWebhookOutboxStore = (executor, options = {}) => {
|
|
2708
3901
|
const tables = options.tables ?? authSqliteTables;
|
|
2709
|
-
return WebhookOutboxStore.
|
|
3902
|
+
return WebhookOutboxStore.of({
|
|
2710
3903
|
enqueue: (input) => Effect.gen(function* () {
|
|
2711
3904
|
const now = input.now ?? (yield* currentUnixMillis);
|
|
2712
3905
|
const record = {
|
|
@@ -2725,7 +3918,8 @@ export const makeEffectQbSqliteWebhookOutboxStore = (executor, options = {}) =>
|
|
|
2725
3918
|
claimDue: (input) => claimDueWebhookOutboxRecords(executor, tables, input),
|
|
2726
3919
|
markDelivered: (input) => Effect.gen(function* () {
|
|
2727
3920
|
const existing = yield* findWebhookOutboxRecordById(executor, tables, input.id, "markDelivered");
|
|
2728
|
-
if (Option.isNone(existing) ||
|
|
3921
|
+
if (Option.isNone(existing) ||
|
|
3922
|
+
existing.value.status === "dead_lettered") {
|
|
2729
3923
|
return;
|
|
2730
3924
|
}
|
|
2731
3925
|
const deliveredAt = input.deliveredAt ?? (yield* currentUnixMillis);
|
|
@@ -2763,11 +3957,11 @@ export const makeEffectQbSqliteWebhookOutboxStore = (executor, options = {}) =>
|
|
|
2763
3957
|
};
|
|
2764
3958
|
export const makeEffectQbSqliteWebhookReplayStore = (executor, options = {}) => {
|
|
2765
3959
|
const tables = options.tables ?? authSqliteTables;
|
|
2766
|
-
return WebhookReplayStore.
|
|
3960
|
+
return WebhookReplayStore.of({
|
|
2767
3961
|
claim: (input) => Effect.gen(function* () {
|
|
2768
3962
|
const now = input.now ?? (yield* currentUnixMillis);
|
|
2769
3963
|
const claim = { ...input, now };
|
|
2770
|
-
const rows = yield* executePlan(executor, queryInsert(tables.webhookReplay.table, mutationValues(tables.webhookReplay.insert(claim))).pipe(
|
|
3964
|
+
const rows = yield* executePlan(executor, queryInsert(tables.webhookReplay.table, mutationValues(tables.webhookReplay.insert(claim))).pipe(SqliteQuery.onConflict("id", {
|
|
2771
3965
|
update: mutationValues(tables.webhookReplay.update(claim)),
|
|
2772
3966
|
where: queryLt(tables.webhookReplay.select.expiresAt, Number(now)),
|
|
2773
3967
|
}), Query.returning(selectWebhookReplayColumns(tables)))).pipe(Effect.mapError((cause) => webhookReplayStoreError("claim", cause)));
|
|
@@ -2776,8 +3970,297 @@ export const makeEffectQbSqliteWebhookReplayStore = (executor, options = {}) =>
|
|
|
2776
3970
|
deleteExpired: (input = {}) => deleteExpiredWebhookReplayRecords(executor, tables, input),
|
|
2777
3971
|
});
|
|
2778
3972
|
};
|
|
2779
|
-
|
|
3973
|
+
const permissionScopeKey = (scope) => [
|
|
3974
|
+
scope?.type ?? "global",
|
|
3975
|
+
scope?.id === undefined ? 0 : 1,
|
|
3976
|
+
scope?.id ?? "",
|
|
3977
|
+
];
|
|
3978
|
+
const comparePermissionText = (left, right) => left === right
|
|
3979
|
+
? 0
|
|
3980
|
+
: left === undefined
|
|
3981
|
+
? -1
|
|
3982
|
+
: right === undefined
|
|
3983
|
+
? 1
|
|
3984
|
+
: left < right
|
|
3985
|
+
? -1
|
|
3986
|
+
: 1;
|
|
3987
|
+
const comparePermissionScopes = (left, right) => {
|
|
3988
|
+
if (left === undefined || right === undefined) {
|
|
3989
|
+
return left === right ? 0 : left === undefined ? -1 : 1;
|
|
3990
|
+
}
|
|
3991
|
+
return (comparePermissionText(left.type, right.type) ||
|
|
3992
|
+
comparePermissionText(left.id, right.id));
|
|
3993
|
+
};
|
|
3994
|
+
const permissionScopesMatchExactly = (left, right) => {
|
|
3995
|
+
const [leftType, leftIdPresent, leftId] = permissionScopeKey(left);
|
|
3996
|
+
const [rightType, rightIdPresent, rightId] = permissionScopeKey(right);
|
|
3997
|
+
return (leftType === rightType &&
|
|
3998
|
+
leftIdPresent === rightIdPresent &&
|
|
3999
|
+
leftId === rightId);
|
|
4000
|
+
};
|
|
4001
|
+
const permissionGrantIsActive = (grant, at) => grant.revokedAt === undefined &&
|
|
4002
|
+
(grant.expiresAt === undefined || Number(grant.expiresAt) > Number(at));
|
|
4003
|
+
const comparePermissionGrantRecords = (left, right) => comparePermissionText(left.subject.type, right.subject.type) ||
|
|
4004
|
+
comparePermissionText(left.subject.id, right.subject.id) ||
|
|
4005
|
+
comparePermissionText(left.permission, right.permission) ||
|
|
4006
|
+
comparePermissionScopes(left.scope, right.scope);
|
|
4007
|
+
const compareRoleGrantRecords = (left, right) => comparePermissionText(left.subject.type, right.subject.type) ||
|
|
4008
|
+
comparePermissionText(left.subject.id, right.subject.id) ||
|
|
4009
|
+
comparePermissionText(left.role, right.role) ||
|
|
4010
|
+
comparePermissionScopes(left.scope, right.scope);
|
|
4011
|
+
const compareRolePermissionRecords = (left, right) => comparePermissionText(left.role, right.role) ||
|
|
4012
|
+
comparePermissionText(left.permission, right.permission) ||
|
|
4013
|
+
comparePermissionText(left.scopeType, right.scopeType);
|
|
4014
|
+
export const makeEffectQbSqlitePermissionStore = (executor, options = {}) => {
|
|
4015
|
+
const tables = options.tables ?? authSqliteTables;
|
|
4016
|
+
const permissionGrantWhere = (input) => {
|
|
4017
|
+
const { scope: _scope, ...withoutScope } = input;
|
|
4018
|
+
const scope = canonicalPermissionScope(input.scope);
|
|
4019
|
+
const canonicalInput = {
|
|
4020
|
+
...withoutScope,
|
|
4021
|
+
...(scope === undefined ? {} : { scope }),
|
|
4022
|
+
};
|
|
4023
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(scope);
|
|
4024
|
+
return queryUpdate(tables.permissionGrant.table, mutationValues(tables.permissionGrant.revoke(canonicalInput, input.revokedAt ?? UnixMillis(Date.now())))).pipe(Query.where(queryEq(tables.permissionGrant.select.subjectType, input.subject.type)), Query.where(queryEq(tables.permissionGrant.select.subjectId, input.subject.id)), Query.where(queryEq(tables.permissionGrant.select.permissionId, input.permission)), Query.where(queryEq(tables.permissionGrant.select.scopeType, scopeType)), Query.where(queryEq(tables.permissionGrant.select.scopeIdPresent, scopeIdPresent)), Query.where(queryEq(tables.permissionGrant.select.scopeId, scopeId)));
|
|
4025
|
+
};
|
|
4026
|
+
const roleGrantWhere = (input) => {
|
|
4027
|
+
const { scope: _scope, ...withoutScope } = input;
|
|
4028
|
+
const scope = canonicalPermissionScope(input.scope);
|
|
4029
|
+
const canonicalInput = {
|
|
4030
|
+
...withoutScope,
|
|
4031
|
+
...(scope === undefined ? {} : { scope }),
|
|
4032
|
+
};
|
|
4033
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(scope);
|
|
4034
|
+
return queryUpdate(tables.roleGrant.table, mutationValues(tables.roleGrant.revoke(canonicalInput, input.revokedAt ?? UnixMillis(Date.now())))).pipe(Query.where(queryEq(tables.roleGrant.select.subjectType, input.subject.type)), Query.where(queryEq(tables.roleGrant.select.subjectId, input.subject.id)), Query.where(queryEq(tables.roleGrant.select.roleId, input.role)), Query.where(queryEq(tables.roleGrant.select.scopeType, scopeType)), Query.where(queryEq(tables.roleGrant.select.scopeIdPresent, scopeIdPresent)), Query.where(queryEq(tables.roleGrant.select.scopeId, scopeId)));
|
|
4035
|
+
};
|
|
4036
|
+
const checkResult = (operation, allowed) => executePlan(executor, Query.select({ allowed: allowed })).pipe(Effect.mapError((cause) => permissionStoreError(operation, cause)), Effect.map((rows) => decodeBoolean(rows[0]?.allowed)));
|
|
4037
|
+
const findPermissionDefinition = (id, operation = "find_permission_definition") => executePlan(executor, Query.select(selectPermissionDefinitionColumns(tables)).pipe(Query.from(tables.permissionDefinition.table), Query.where(queryEq(tables.permissionDefinition.select.id, id)))).pipe(Effect.mapError((cause) => permissionStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
4038
|
+
? Option.none()
|
|
4039
|
+
: Option.some(decodePermissionDefinition(tables, rows[0]))));
|
|
4040
|
+
const findRoleDefinition = (id, operation = "find_role_definition") => executePlan(executor, Query.select(selectRoleDefinitionColumns(tables)).pipe(Query.from(tables.roleDefinition.table), Query.where(queryEq(tables.roleDefinition.select.id, id)))).pipe(Effect.mapError((cause) => permissionStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
4041
|
+
? Option.none()
|
|
4042
|
+
: Option.some(decodeRoleDefinition(tables, rows[0]))));
|
|
4043
|
+
const mutatePermissionDefinition = (operation, input, values) => {
|
|
4044
|
+
const returning = operation === "delete_permission_definition"
|
|
4045
|
+
? {
|
|
4046
|
+
...selectPermissionDefinitionColumns(tables),
|
|
4047
|
+
deletedAt: queryWithoutNullRefinement(tables.permissionDefinition.select.deletedAt),
|
|
4048
|
+
}
|
|
4049
|
+
: selectPermissionDefinitionColumns(tables);
|
|
4050
|
+
return executePlan(executor, queryUpdate(tables.permissionDefinition.table, mutationValues(values)).pipe(Query.where(queryEq(tables.permissionDefinition.select.id, input.id)), Query.where(queryEq(tables.permissionDefinition.select.updatedAt, Number(input.expectedUpdatedAt))), Query.where(queryIsNull(tables.permissionDefinition.select.deletedAt)), Query.returning(returning))).pipe(Effect.mapError((cause) => permissionStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
4051
|
+
? Option.none()
|
|
4052
|
+
: Option.some(decodePermissionDefinition(tables, rows[0]))));
|
|
4053
|
+
};
|
|
4054
|
+
const mutateRoleDefinition = (operation, input, values) => {
|
|
4055
|
+
const returning = operation === "delete_role_definition"
|
|
4056
|
+
? {
|
|
4057
|
+
...selectRoleDefinitionColumns(tables),
|
|
4058
|
+
deletedAt: queryWithoutNullRefinement(tables.roleDefinition.select.deletedAt),
|
|
4059
|
+
}
|
|
4060
|
+
: selectRoleDefinitionColumns(tables);
|
|
4061
|
+
return executePlan(executor, queryUpdate(tables.roleDefinition.table, mutationValues(values)).pipe(Query.where(queryEq(tables.roleDefinition.select.id, input.id)), Query.where(queryEq(tables.roleDefinition.select.updatedAt, Number(input.expectedUpdatedAt))), Query.where(queryIsNull(tables.roleDefinition.select.deletedAt)), Query.returning(returning))).pipe(Effect.mapError((cause) => permissionStoreError(operation, cause)), Effect.map((rows) => rows[0] === undefined
|
|
4062
|
+
? Option.none()
|
|
4063
|
+
: Option.some(decodeRoleDefinition(tables, rows[0]))));
|
|
4064
|
+
};
|
|
4065
|
+
return PermissionStore.of({
|
|
4066
|
+
createPermissionDefinition: (definition) => executePlan(executor, queryInsert(tables.permissionDefinition.table, mutationValues(tables.permissionDefinition.insert(definition))).pipe(SqliteQuery.onConflict(tables.permissionDefinition.conflictTarget), Query.returning(selectPermissionDefinitionColumns(tables)))).pipe(Effect.mapError((cause) => permissionStoreError("create_permission_definition", cause)), Effect.map((rows) => rows.length > 0)),
|
|
4067
|
+
findPermissionDefinition: (id) => findPermissionDefinition(id),
|
|
4068
|
+
listPermissionDefinitions: (input = {}) => {
|
|
4069
|
+
const base = Query.select(selectPermissionDefinitionColumns(tables)).pipe(Query.from(tables.permissionDefinition.table));
|
|
4070
|
+
const withoutDisabled = input.includeDisabled === true
|
|
4071
|
+
? base
|
|
4072
|
+
: base.pipe(Query.where(queryIsNull(tables.permissionDefinition.select.disabledAt)));
|
|
4073
|
+
const withoutDeleted = input.includeDeleted === true
|
|
4074
|
+
? withoutDisabled
|
|
4075
|
+
: withoutDisabled.pipe(Query.where(queryIsNull(tables.permissionDefinition.select.deletedAt)));
|
|
4076
|
+
const after = input.after === undefined
|
|
4077
|
+
? withoutDeleted
|
|
4078
|
+
: withoutDeleted.pipe(Query.where(queryGt(tables.permissionDefinition.select.id, input.after)));
|
|
4079
|
+
const query = after.pipe(Query.orderBy(tables.permissionDefinition.select.id, "asc"), Query.limit(normalizePermissionDefinitionListLimit(input.limit)));
|
|
4080
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_permission_definitions", cause)), Effect.map((rows) => rows.map((row) => decodePermissionDefinition(tables, row))));
|
|
4081
|
+
},
|
|
4082
|
+
updatePermissionDefinition: (input) => mutatePermissionDefinition("update_permission_definition", input, tables.permissionDefinition.update(input)),
|
|
4083
|
+
setPermissionDefinitionDisabled: (input) => mutatePermissionDefinition("set_permission_definition_disabled", input, tables.permissionDefinition.setDisabled(input)),
|
|
4084
|
+
deletePermissionDefinition: (input) => mutatePermissionDefinition("delete_permission_definition", input, tables.permissionDefinition.delete(input)),
|
|
4085
|
+
createRoleDefinition: (definition) => executePlan(executor, queryInsert(tables.roleDefinition.table, mutationValues(tables.roleDefinition.insert(definition))).pipe(SqliteQuery.onConflict(tables.roleDefinition.conflictTarget), Query.returning(selectRoleDefinitionColumns(tables)))).pipe(Effect.mapError((cause) => permissionStoreError("create_role_definition", cause)), Effect.map((rows) => rows.length > 0)),
|
|
4086
|
+
findRoleDefinition: (id) => findRoleDefinition(id),
|
|
4087
|
+
listRoleDefinitions: (input = {}) => {
|
|
4088
|
+
const base = Query.select(selectRoleDefinitionColumns(tables)).pipe(Query.from(tables.roleDefinition.table));
|
|
4089
|
+
const withoutDisabled = input.includeDisabled === true
|
|
4090
|
+
? base
|
|
4091
|
+
: base.pipe(Query.where(queryIsNull(tables.roleDefinition.select.disabledAt)));
|
|
4092
|
+
const query = input.includeDeleted === true
|
|
4093
|
+
? withoutDisabled
|
|
4094
|
+
: withoutDisabled.pipe(Query.where(queryIsNull(tables.roleDefinition.select.deletedAt)));
|
|
4095
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_role_definitions", cause)), Effect.map((rows) => rows
|
|
4096
|
+
.map((row) => decodeRoleDefinition(tables, row))
|
|
4097
|
+
.sort((left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0)));
|
|
4098
|
+
},
|
|
4099
|
+
updateRoleDefinition: (input) => mutateRoleDefinition("update_role_definition", input, tables.roleDefinition.update(input)),
|
|
4100
|
+
setRoleDefinitionDisabled: (input) => mutateRoleDefinition("set_role_definition_disabled", input, tables.roleDefinition.setDisabled(input)),
|
|
4101
|
+
deleteRoleDefinition: (input) => mutateRoleDefinition("delete_role_definition", input, tables.roleDefinition.delete(input)),
|
|
4102
|
+
grantPermission: (input) => Effect.suspend(() => {
|
|
4103
|
+
const { scope: _scope, ...withoutScope } = input;
|
|
4104
|
+
const scope = canonicalPermissionScope(input.scope);
|
|
4105
|
+
const grant = {
|
|
4106
|
+
...withoutScope,
|
|
4107
|
+
...(scope === undefined ? {} : { scope }),
|
|
4108
|
+
};
|
|
4109
|
+
return executePlan(executor, queryInsert(tables.permissionGrant.table, mutationValues(tables.permissionGrant.insert(grant))).pipe(SqliteQuery.onConflict([
|
|
4110
|
+
"subject_type",
|
|
4111
|
+
"subject_id",
|
|
4112
|
+
"permission_id",
|
|
4113
|
+
"scope_type",
|
|
4114
|
+
"scope_id_present",
|
|
4115
|
+
"scope_id",
|
|
4116
|
+
], { update: mutationValues(tables.permissionGrant.update(grant)) }))).pipe(Effect.mapError((cause) => permissionStoreError("grant_permission", cause)), Effect.asVoid);
|
|
4117
|
+
}),
|
|
4118
|
+
revokePermission: (input) => Effect.suspend(() => executePlan(executor, permissionGrantWhere(input)).pipe(Effect.mapError((cause) => permissionStoreError("revoke_permission", cause)), Effect.asVoid)),
|
|
4119
|
+
listPermissionGrants: (input = {}) => Effect.suspend(() => {
|
|
4120
|
+
const at = input.at ?? UnixMillis(Date.now());
|
|
4121
|
+
const activity = input.activity ?? "active";
|
|
4122
|
+
const base = Query.select(selectPermissionGrantColumns(tables)).pipe(Query.from(tables.permissionGrant.table));
|
|
4123
|
+
const bySubjectType = input.subject === undefined
|
|
4124
|
+
? base
|
|
4125
|
+
: base.pipe(Query.where(queryEq(tables.permissionGrant.select.subjectType, input.subject.type)));
|
|
4126
|
+
const bySubject = input.subject === undefined
|
|
4127
|
+
? bySubjectType
|
|
4128
|
+
: bySubjectType.pipe(Query.where(queryEq(tables.permissionGrant.select.subjectId, input.subject.id)));
|
|
4129
|
+
const byPermission = input.permission === undefined
|
|
4130
|
+
? bySubject
|
|
4131
|
+
: bySubject.pipe(Query.where(queryEq(tables.permissionGrant.select.permissionId, input.permission)));
|
|
4132
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(input.scope);
|
|
4133
|
+
const query = input.scope === undefined
|
|
4134
|
+
? byPermission
|
|
4135
|
+
: byPermission.pipe(Query.where(queryEq(tables.permissionGrant.select.scopeType, scopeType)), Query.where(queryEq(tables.permissionGrant.select.scopeIdPresent, scopeIdPresent)), Query.where(queryEq(tables.permissionGrant.select.scopeId, scopeId)));
|
|
4136
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_permission_grants", cause)), Effect.flatMap((rows) => Effect.try({
|
|
4137
|
+
try: () => rows
|
|
4138
|
+
.map((row) => decodePermissionGrant(tables, row))
|
|
4139
|
+
.filter((grant) => (input.subject === undefined ||
|
|
4140
|
+
(grant.subject.type === input.subject.type &&
|
|
4141
|
+
grant.subject.id === input.subject.id)) &&
|
|
4142
|
+
(input.permission === undefined ||
|
|
4143
|
+
grant.permission === input.permission) &&
|
|
4144
|
+
(input.scope === undefined ||
|
|
4145
|
+
permissionScopesMatchExactly(grant.scope, input.scope)) &&
|
|
4146
|
+
(activity === "all" ||
|
|
4147
|
+
permissionGrantIsActive(grant, at) ===
|
|
4148
|
+
(activity === "active")))
|
|
4149
|
+
.sort(comparePermissionGrantRecords),
|
|
4150
|
+
catch: (cause) => permissionStoreError("list_permission_grants", cause),
|
|
4151
|
+
})));
|
|
4152
|
+
}),
|
|
4153
|
+
grantRole: (input) => Effect.suspend(() => {
|
|
4154
|
+
const { scope: _scope, ...withoutScope } = input;
|
|
4155
|
+
const scope = canonicalPermissionScope(input.scope);
|
|
4156
|
+
const grant = {
|
|
4157
|
+
...withoutScope,
|
|
4158
|
+
...(scope === undefined ? {} : { scope }),
|
|
4159
|
+
};
|
|
4160
|
+
return executePlan(executor, queryInsert(tables.roleGrant.table, mutationValues(tables.roleGrant.insert(grant))).pipe(SqliteQuery.onConflict([
|
|
4161
|
+
"subject_type",
|
|
4162
|
+
"subject_id",
|
|
4163
|
+
"role_id",
|
|
4164
|
+
"scope_type",
|
|
4165
|
+
"scope_id_present",
|
|
4166
|
+
"scope_id",
|
|
4167
|
+
], { update: mutationValues(tables.roleGrant.update(grant)) }))).pipe(Effect.mapError((cause) => permissionStoreError("grant_role", cause)), Effect.asVoid);
|
|
4168
|
+
}),
|
|
4169
|
+
revokeRole: (input) => Effect.suspend(() => executePlan(executor, roleGrantWhere(input)).pipe(Effect.mapError((cause) => permissionStoreError("revoke_role", cause)), Effect.asVoid)),
|
|
4170
|
+
listRoleGrants: (input = {}) => Effect.suspend(() => {
|
|
4171
|
+
const at = input.at ?? UnixMillis(Date.now());
|
|
4172
|
+
const activity = input.activity ?? "active";
|
|
4173
|
+
const base = Query.select(selectRoleGrantColumns(tables)).pipe(Query.from(tables.roleGrant.table));
|
|
4174
|
+
const bySubjectType = input.subject === undefined
|
|
4175
|
+
? base
|
|
4176
|
+
: base.pipe(Query.where(queryEq(tables.roleGrant.select.subjectType, input.subject.type)));
|
|
4177
|
+
const bySubject = input.subject === undefined
|
|
4178
|
+
? bySubjectType
|
|
4179
|
+
: bySubjectType.pipe(Query.where(queryEq(tables.roleGrant.select.subjectId, input.subject.id)));
|
|
4180
|
+
const byRole = input.role === undefined
|
|
4181
|
+
? bySubject
|
|
4182
|
+
: bySubject.pipe(Query.where(queryEq(tables.roleGrant.select.roleId, input.role)));
|
|
4183
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(input.scope);
|
|
4184
|
+
const query = input.scope === undefined
|
|
4185
|
+
? byRole
|
|
4186
|
+
: byRole.pipe(Query.where(queryEq(tables.roleGrant.select.scopeType, scopeType)), Query.where(queryEq(tables.roleGrant.select.scopeIdPresent, scopeIdPresent)), Query.where(queryEq(tables.roleGrant.select.scopeId, scopeId)));
|
|
4187
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_role_grants", cause)), Effect.flatMap((rows) => Effect.try({
|
|
4188
|
+
try: () => rows
|
|
4189
|
+
.map((row) => decodeRoleGrant(tables, row))
|
|
4190
|
+
.filter((grant) => (input.subject === undefined ||
|
|
4191
|
+
(grant.subject.type === input.subject.type &&
|
|
4192
|
+
grant.subject.id === input.subject.id)) &&
|
|
4193
|
+
(input.role === undefined || grant.role === input.role) &&
|
|
4194
|
+
(input.scope === undefined ||
|
|
4195
|
+
permissionScopesMatchExactly(grant.scope, input.scope)) &&
|
|
4196
|
+
(activity === "all" ||
|
|
4197
|
+
permissionGrantIsActive(grant, at) ===
|
|
4198
|
+
(activity === "active")))
|
|
4199
|
+
.sort(compareRoleGrantRecords),
|
|
4200
|
+
catch: (cause) => permissionStoreError("list_role_grants", cause),
|
|
4201
|
+
})));
|
|
4202
|
+
}),
|
|
4203
|
+
assignRolePermission: (input) => executePlan(executor, queryInsert(tables.rolePermission.table, mutationValues(tables.rolePermission.insert(input))).pipe(SqliteQuery.onConflict([
|
|
4204
|
+
"role_id",
|
|
4205
|
+
"permission_id",
|
|
4206
|
+
"scope_type_present",
|
|
4207
|
+
"scope_type",
|
|
4208
|
+
]))).pipe(Effect.mapError((cause) => permissionStoreError("assign_role_permission", cause)), Effect.asVoid),
|
|
4209
|
+
removeRolePermission: (input) => executePlan(executor, Query.delete(tables.rolePermission.table).pipe(Query.where(queryEq(tables.rolePermission.select.roleId, input.role)), Query.where(queryEq(tables.rolePermission.select.permissionId, input.permission)), Query.where(queryEq(tables.rolePermission.select.scopeTypePresent, input.scopeType === undefined ? 0 : 1)), Query.where(queryEq(tables.rolePermission.select.scopeType, input.scopeType ?? "")))).pipe(Effect.mapError((cause) => permissionStoreError("remove_role_permission", cause)), Effect.asVoid),
|
|
4210
|
+
listRolePermissions: (input = {}) => {
|
|
4211
|
+
const base = Query.select(selectRolePermissionColumns(tables)).pipe(Query.from(tables.rolePermission.table));
|
|
4212
|
+
const byRole = input.role === undefined
|
|
4213
|
+
? base
|
|
4214
|
+
: base.pipe(Query.where(queryEq(tables.rolePermission.select.roleId, input.role)));
|
|
4215
|
+
const byPermission = input.permission === undefined
|
|
4216
|
+
? byRole
|
|
4217
|
+
: byRole.pipe(Query.where(queryEq(tables.rolePermission.select.permissionId, input.permission)));
|
|
4218
|
+
const byScopePresence = input.scopeType === undefined
|
|
4219
|
+
? byPermission
|
|
4220
|
+
: byPermission.pipe(Query.where(queryEq(tables.rolePermission.select.scopeTypePresent, input.scopeType === null ? 0 : 1)));
|
|
4221
|
+
const query = input.scopeType === undefined || input.scopeType === null
|
|
4222
|
+
? byScopePresence
|
|
4223
|
+
: byScopePresence.pipe(Query.where(queryEq(tables.rolePermission.select.scopeType, input.scopeType)));
|
|
4224
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_role_permissions", cause)), Effect.flatMap((rows) => Effect.try({
|
|
4225
|
+
try: () => rows
|
|
4226
|
+
.map((row) => decodeRolePermission(tables, row))
|
|
4227
|
+
.filter((rolePermission) => (input.role === undefined ||
|
|
4228
|
+
rolePermission.role === input.role) &&
|
|
4229
|
+
(input.permission === undefined ||
|
|
4230
|
+
rolePermission.permission === input.permission) &&
|
|
4231
|
+
(input.scopeType === undefined ||
|
|
4232
|
+
(input.scopeType === null
|
|
4233
|
+
? rolePermission.scopeType === undefined
|
|
4234
|
+
: rolePermission.scopeType === input.scopeType)))
|
|
4235
|
+
.sort(compareRolePermissionRecords),
|
|
4236
|
+
catch: (cause) => permissionStoreError("list_role_permissions", cause),
|
|
4237
|
+
})));
|
|
4238
|
+
},
|
|
4239
|
+
hasPermission: (input) => Effect.suspend(() => {
|
|
4240
|
+
const now = Date.now();
|
|
4241
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(input.scope);
|
|
4242
|
+
const permissionScope = queryOr(queryAnd(queryEq(tables.permissionGrant.select.scopeType, "global"), queryEq(tables.permissionGrant.select.scopeIdPresent, 0)), queryAnd(queryEq(tables.permissionGrant.select.scopeType, scopeType), queryEq(tables.permissionGrant.select.scopeIdPresent, scopeIdPresent), queryEq(tables.permissionGrant.select.scopeId, scopeId)));
|
|
4243
|
+
const direct = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.permissionGrant.table), Query.where(queryEq(tables.permissionGrant.select.subjectType, input.subject.type)), Query.where(queryEq(tables.permissionGrant.select.subjectId, input.subject.id)), Query.where(queryEq(tables.permissionGrant.select.permissionId, input.permission)), Query.where(queryIsNull(tables.permissionGrant.select.revokedAt)), Query.where(queryOr(queryIsNull(tables.permissionGrant.select.expiresAt), queryGt(tables.permissionGrant.select.expiresAt, now))), Query.where(permissionScope)));
|
|
4244
|
+
const roleGrant = aliasTable(tables.roleGrant.table, "role_grant");
|
|
4245
|
+
const roleGrantColumn = (column) => aliasColumn(roleGrant, column);
|
|
4246
|
+
const rolePermission = aliasTable(tables.rolePermission.table, "role_permission");
|
|
4247
|
+
const rolePermissionColumn = (column) => aliasColumn(rolePermission, column);
|
|
4248
|
+
const rolePermissionExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(rolePermission), Query.where(queryEq(rolePermissionColumn(tables.rolePermission.select.roleId), roleGrantColumn(tables.roleGrant.select.roleId))), Query.where(queryEq(rolePermissionColumn(tables.rolePermission.select.permissionId), input.permission)), Query.where(queryOr(queryEq(rolePermissionColumn(tables.rolePermission.select.scopeTypePresent), 0), queryAnd(queryEq(rolePermissionColumn(tables.rolePermission.select.scopeTypePresent), 1), queryEq(rolePermissionColumn(tables.rolePermission.select.scopeType), scopeType))))));
|
|
4249
|
+
const role = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(roleGrant), Query.where(queryEq(roleGrantColumn(tables.roleGrant.select.subjectType), input.subject.type)), Query.where(queryEq(roleGrantColumn(tables.roleGrant.select.subjectId), input.subject.id)), Query.where(queryIsNull(roleGrantColumn(tables.roleGrant.select.revokedAt))), Query.where(queryOr(queryIsNull(roleGrantColumn(tables.roleGrant.select.expiresAt)), queryGt(roleGrantColumn(tables.roleGrant.select.expiresAt), now))), Query.where(queryOr(queryAnd(queryEq(roleGrantColumn(tables.roleGrant.select.scopeType), "global"), queryEq(roleGrantColumn(tables.roleGrant.select.scopeIdPresent), 0)), queryAnd(queryEq(roleGrantColumn(tables.roleGrant.select.scopeType), scopeType), queryEq(roleGrantColumn(tables.roleGrant.select.scopeIdPresent), scopeIdPresent), queryEq(roleGrantColumn(tables.roleGrant.select.scopeId), scopeId)))), Query.where(rolePermissionExists)));
|
|
4250
|
+
return checkResult("has_permission", queryOr(direct, role));
|
|
4251
|
+
}),
|
|
4252
|
+
hasRole: (input) => Effect.suspend(() => {
|
|
4253
|
+
const now = Date.now();
|
|
4254
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(input.scope);
|
|
4255
|
+
const allowed = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.roleGrant.table), Query.where(queryEq(tables.roleGrant.select.subjectType, input.subject.type)), Query.where(queryEq(tables.roleGrant.select.subjectId, input.subject.id)), Query.where(queryEq(tables.roleGrant.select.roleId, input.role)), Query.where(queryIsNull(tables.roleGrant.select.revokedAt)), Query.where(queryOr(queryIsNull(tables.roleGrant.select.expiresAt), queryGt(tables.roleGrant.select.expiresAt, now))), Query.where(queryOr(queryAnd(queryEq(tables.roleGrant.select.scopeType, "global"), queryEq(tables.roleGrant.select.scopeIdPresent, 0)), queryAnd(queryEq(tables.roleGrant.select.scopeType, scopeType), queryEq(tables.roleGrant.select.scopeIdPresent, scopeIdPresent), queryEq(tables.roleGrant.select.scopeId, scopeId))))));
|
|
4256
|
+
return checkResult("has_role", allowed);
|
|
4257
|
+
}),
|
|
4258
|
+
});
|
|
4259
|
+
};
|
|
4260
|
+
export const makeEffectQbSqliteStorage = (executor, atomic, options = {}) => ({
|
|
2780
4261
|
userStore: makeEffectQbSqliteUserStore(executor, options),
|
|
4262
|
+
identityStore: makeEffectQbSqliteIdentityStore(executor, atomic, options),
|
|
4263
|
+
registrationStore: makeEffectQbSqliteRegistrationStore(executor, atomic, options),
|
|
2781
4264
|
credentialStore: makeEffectQbSqliteCredentialStore(executor, options),
|
|
2782
4265
|
sessionStore: makeEffectQbSqliteSessionStore(executor, options),
|
|
2783
4266
|
verificationStore: makeEffectQbSqliteVerificationStore(executor, options),
|
|
@@ -2787,11 +4270,16 @@ export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
|
|
|
2787
4270
|
trustedDeviceStore: makeEffectQbSqliteTrustedDeviceStore(executor, options),
|
|
2788
4271
|
passkeyCredentialStore: makeEffectQbSqlitePasskeyCredentialStore(executor, options),
|
|
2789
4272
|
totpFactorStore: makeEffectQbSqliteTotpFactorStore(executor, options),
|
|
2790
|
-
|
|
4273
|
+
totpSessionRotateStore: makeEffectQbSqliteTotpSessionRotateStore(executor, atomic, options),
|
|
4274
|
+
totpLoginCommitStore: makeEffectQbSqliteTotpLoginCommitStore(atomic, options),
|
|
4275
|
+
recoveryCodeStore: makeEffectQbSqliteRecoveryCodeStore(executor, atomic, options),
|
|
4276
|
+
recoveryCodeSessionRotateStore: makeEffectQbSqliteRecoveryCodeSessionRotateStore(executor, atomic, options),
|
|
4277
|
+
recoveryCodeLoginCommitStore: makeEffectQbSqliteRecoveryCodeLoginCommitStore(atomic, options),
|
|
2791
4278
|
apiKeyStore: makeEffectQbSqliteApiKeyStore(executor, options),
|
|
2792
|
-
refreshTokenStore: makeEffectQbSqliteRefreshTokenStore(executor, options),
|
|
4279
|
+
refreshTokenStore: makeEffectQbSqliteRefreshTokenStore(executor, atomic, options),
|
|
2793
4280
|
jwtRevocationStore: makeEffectQbSqliteJwtRevocationStore(executor, options),
|
|
2794
4281
|
oauthAccountStore: makeEffectQbSqliteOAuthAccountStore(executor, options),
|
|
4282
|
+
oauthIdentityBridgeStore: makeEffectQbSqliteOAuthIdentityBridgeStore(executor, atomic, options),
|
|
2795
4283
|
oauthClientStore: makeEffectQbSqliteOAuthClientStore(executor, options),
|
|
2796
4284
|
oauthConsentStore: makeEffectQbSqliteOAuthConsentStore(executor, options),
|
|
2797
4285
|
oauthAuthorizationCodeStore: makeEffectQbSqliteOAuthAuthorizationCodeStore(executor, options),
|
|
@@ -2802,8 +4290,9 @@ export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
|
|
|
2802
4290
|
securityTimelineStore: makeEffectQbSqliteSecurityTimelineStore(executor, options),
|
|
2803
4291
|
webhookOutboxStore: makeEffectQbSqliteWebhookOutboxStore(executor, options),
|
|
2804
4292
|
webhookReplayStore: makeEffectQbSqliteWebhookReplayStore(executor, options),
|
|
4293
|
+
permissionStore: makeEffectQbSqlitePermissionStore(executor, options),
|
|
2805
4294
|
});
|
|
2806
|
-
export const EffectQbSqliteAuthStorageLive = (executor, options = {}) => Layer.mergeAll(Layer.succeed(UserStore)(makeEffectQbSqliteUserStore(executor, options)), Layer.succeed(CredentialStore)(makeEffectQbSqliteCredentialStore(executor, options)), Layer.succeed(SessionStore)(makeEffectQbSqliteSessionStore(executor, options)), Layer.succeed(VerificationStore)(makeEffectQbSqliteVerificationStore(executor, options)), Layer.succeed(AuditLogStore)(makeEffectQbSqliteAuditLogStore(executor, options)), Layer.succeed(LoginApprovalReviewStore)(makeEffectQbSqliteLoginApprovalReviewStore(executor, options)), Layer.succeed(LoginRiskHistory)(makeEffectQbSqliteLoginRiskHistoryStore(executor, options)), Layer.succeed(TrustedDeviceStore)(makeEffectQbSqliteTrustedDeviceStore(executor, options)), Layer.succeed(PasskeyCredentialStore)(makeEffectQbSqlitePasskeyCredentialStore(executor, options)), Layer.succeed(TotpFactorStore)(makeEffectQbSqliteTotpFactorStore(executor, options)), Layer.succeed(RecoveryCodeStore)(makeEffectQbSqliteRecoveryCodeStore(executor, options)), Layer.succeed(ApiKeyStore)(makeEffectQbSqliteApiKeyStore(executor, options)), Layer.succeed(RefreshTokenStore)(makeEffectQbSqliteRefreshTokenStore(executor, options)), Layer.succeed(JwtRevocationStore)(makeEffectQbSqliteJwtRevocationStore(executor, options)), Layer.succeed(OAuthAccountStore)(makeEffectQbSqliteOAuthAccountStore(executor, options)), Layer.succeed(OAuthClientStore)(makeEffectQbSqliteOAuthClientStore(executor, options)), Layer.succeed(OAuthConsentStore)(makeEffectQbSqliteOAuthConsentStore(executor, options)), Layer.succeed(OAuthAuthorizationCodeStore)(makeEffectQbSqliteOAuthAuthorizationCodeStore(executor, options)), Layer.succeed(OAuthClientSecretStore)(makeEffectQbSqliteOAuthClientSecretStore(executor, options)), Layer.succeed(OAuthProviderModeTokenStore)(makeEffectQbSqliteOAuthProviderModeTokenStore(executor, options)), Layer.succeed(OAuthProviderTokenVault)(makeEffectQbSqliteOAuthProviderTokenVault(executor, options)), Layer.succeed(DomainVerificationStore)(makeEffectQbSqliteDomainVerificationStore(executor, options)), Layer.succeed(SecurityTimelineStore)(makeEffectQbSqliteSecurityTimelineStore(executor, options)), Layer.succeed(WebhookOutboxStore)(makeEffectQbSqliteWebhookOutboxStore(executor, options)), Layer.succeed(WebhookReplayStore)(makeEffectQbSqliteWebhookReplayStore(executor, options)));
|
|
4295
|
+
export const EffectQbSqliteAuthStorageLive = (executor, atomic, options = {}) => Layer.mergeAll(Layer.succeed(UserStore)(makeEffectQbSqliteUserStore(executor, options)), Layer.succeed(IdentityStore)(makeEffectQbSqliteIdentityStore(executor, atomic, options)), Layer.succeed(RegistrationStore)(makeEffectQbSqliteRegistrationStore(executor, atomic, options)), Layer.succeed(CredentialStore)(makeEffectQbSqliteCredentialStore(executor, options)), Layer.succeed(SessionStore)(makeEffectQbSqliteSessionStore(executor, options)), Layer.succeed(VerificationStore)(makeEffectQbSqliteVerificationStore(executor, options)), Layer.succeed(AuditLogStore)(makeEffectQbSqliteAuditLogStore(executor, options)), Layer.succeed(LoginApprovalReviewStore)(makeEffectQbSqliteLoginApprovalReviewStore(executor, options)), Layer.succeed(LoginRiskHistory)(makeEffectQbSqliteLoginRiskHistoryStore(executor, options)), Layer.succeed(TrustedDeviceStore)(makeEffectQbSqliteTrustedDeviceStore(executor, options)), Layer.succeed(PasskeyCredentialStore)(makeEffectQbSqlitePasskeyCredentialStore(executor, options)), Layer.succeed(TotpFactorStore)(makeEffectQbSqliteTotpFactorStore(executor, options)), Layer.succeed(TotpSessionRotateStore)(makeEffectQbSqliteTotpSessionRotateStore(executor, atomic, options)), Layer.succeed(TotpLoginCommitStore)(makeEffectQbSqliteTotpLoginCommitStore(atomic, options)), Layer.succeed(RecoveryCodeStore)(makeEffectQbSqliteRecoveryCodeStore(executor, atomic, options)), Layer.succeed(RecoveryCodeSessionRotateStore)(makeEffectQbSqliteRecoveryCodeSessionRotateStore(executor, atomic, options)), Layer.succeed(RecoveryCodeLoginCommitStore)(makeEffectQbSqliteRecoveryCodeLoginCommitStore(atomic, options)), Layer.succeed(ApiKeyStore)(makeEffectQbSqliteApiKeyStore(executor, options)), Layer.succeed(RefreshTokenStore)(makeEffectQbSqliteRefreshTokenStore(executor, atomic, options)), Layer.succeed(JwtRevocationStore)(makeEffectQbSqliteJwtRevocationStore(executor, options)), Layer.succeed(OAuthAccountStore)(makeEffectQbSqliteOAuthAccountStore(executor, options)), Layer.succeed(OAuthIdentityBridgeStore)(makeEffectQbSqliteOAuthIdentityBridgeStore(executor, atomic, options)), Layer.succeed(OAuthClientStore)(makeEffectQbSqliteOAuthClientStore(executor, options)), Layer.succeed(OAuthConsentStore)(makeEffectQbSqliteOAuthConsentStore(executor, options)), Layer.succeed(OAuthAuthorizationCodeStore)(makeEffectQbSqliteOAuthAuthorizationCodeStore(executor, options)), Layer.succeed(OAuthClientSecretStore)(makeEffectQbSqliteOAuthClientSecretStore(executor, options)), Layer.succeed(OAuthProviderModeTokenStore)(makeEffectQbSqliteOAuthProviderModeTokenStore(executor, options)), Layer.succeed(OAuthProviderTokenVault)(makeEffectQbSqliteOAuthProviderTokenVault(executor, options)), Layer.succeed(DomainVerificationStore)(makeEffectQbSqliteDomainVerificationStore(executor, options)), Layer.succeed(SecurityTimelineStore)(makeEffectQbSqliteSecurityTimelineStore(executor, options)), Layer.succeed(WebhookOutboxStore)(makeEffectQbSqliteWebhookOutboxStore(executor, options)), Layer.succeed(WebhookReplayStore)(makeEffectQbSqliteWebhookReplayStore(executor, options)), Layer.succeed(PermissionStore)(makeEffectQbSqlitePermissionStore(executor, options)));
|
|
2807
4296
|
export const makeD1EffectQbSqliteExecutor = (database) => Executor.make({
|
|
2808
4297
|
driver: Executor.driver((query) => Effect.tryPromise({
|
|
2809
4298
|
try: () => database
|
|
@@ -2820,5 +4309,110 @@ export const makeD1EffectQbSqliteExecutor = (database) => Executor.make({
|
|
|
2820
4309
|
return Effect.succeed([...(result.results ?? [])]);
|
|
2821
4310
|
}))),
|
|
2822
4311
|
});
|
|
2823
|
-
export const
|
|
4312
|
+
export const makeD1EffectQbSqliteAtomicPlanExecutor = (database) => {
|
|
4313
|
+
const execute = (plans) => {
|
|
4314
|
+
if (plans.length === 0) {
|
|
4315
|
+
return Effect.succeed([]);
|
|
4316
|
+
}
|
|
4317
|
+
return Effect.try({
|
|
4318
|
+
try: () => plans.map((plan, statement) => {
|
|
4319
|
+
try {
|
|
4320
|
+
return {
|
|
4321
|
+
plan,
|
|
4322
|
+
rendered: Renderer.sqlite.render(plan),
|
|
4323
|
+
};
|
|
4324
|
+
}
|
|
4325
|
+
catch (error) {
|
|
4326
|
+
throw new AtomicPlanError({
|
|
4327
|
+
statement,
|
|
4328
|
+
phase: "render",
|
|
4329
|
+
commitState: "not-committed",
|
|
4330
|
+
cause: error,
|
|
4331
|
+
});
|
|
4332
|
+
}
|
|
4333
|
+
}),
|
|
4334
|
+
catch: (cause) => cause instanceof AtomicPlanError
|
|
4335
|
+
? cause
|
|
4336
|
+
: new AtomicPlanError({
|
|
4337
|
+
phase: "render",
|
|
4338
|
+
commitState: "not-committed",
|
|
4339
|
+
cause,
|
|
4340
|
+
}),
|
|
4341
|
+
}).pipe(Effect.flatMap((renderedPlans) => Effect.tryPromise({
|
|
4342
|
+
try: () => {
|
|
4343
|
+
const statements = renderedPlans.map(({ rendered }, statement) => {
|
|
4344
|
+
try {
|
|
4345
|
+
return database.prepare(rendered.sql).bind(...rendered.params);
|
|
4346
|
+
}
|
|
4347
|
+
catch (error) {
|
|
4348
|
+
throw new AtomicPlanError({
|
|
4349
|
+
statement,
|
|
4350
|
+
phase: "prepare",
|
|
4351
|
+
commitState: "not-committed",
|
|
4352
|
+
cause: error,
|
|
4353
|
+
});
|
|
4354
|
+
}
|
|
4355
|
+
});
|
|
4356
|
+
return database.batch(statements);
|
|
4357
|
+
},
|
|
4358
|
+
catch: (cause) => cause instanceof AtomicPlanError
|
|
4359
|
+
? cause
|
|
4360
|
+
: new AtomicPlanError({
|
|
4361
|
+
phase: "execute",
|
|
4362
|
+
commitState: "unknown",
|
|
4363
|
+
cause,
|
|
4364
|
+
}),
|
|
4365
|
+
}).pipe(Effect.flatMap((results) => {
|
|
4366
|
+
const failed = results.findIndex((result) => result.success === false || result.error !== undefined);
|
|
4367
|
+
if (failed !== -1) {
|
|
4368
|
+
return Effect.fail(new AtomicPlanError({
|
|
4369
|
+
statement: failed,
|
|
4370
|
+
phase: "execute",
|
|
4371
|
+
commitState: "not-committed",
|
|
4372
|
+
cause: results[failed]?.error,
|
|
4373
|
+
}));
|
|
4374
|
+
}
|
|
4375
|
+
if (results.length !== renderedPlans.length) {
|
|
4376
|
+
return Effect.fail(new AtomicPlanError({
|
|
4377
|
+
phase: "execute",
|
|
4378
|
+
commitState: "unknown",
|
|
4379
|
+
cause: new Error(`D1 batch returned ${results.length} results for ${renderedPlans.length} statements`),
|
|
4380
|
+
}));
|
|
4381
|
+
}
|
|
4382
|
+
return Effect.try({
|
|
4383
|
+
try: () => results.map((result, statement) => {
|
|
4384
|
+
const entry = renderedPlans[statement];
|
|
4385
|
+
if (entry === undefined) {
|
|
4386
|
+
throw new AtomicPlanError({
|
|
4387
|
+
statement,
|
|
4388
|
+
phase: "decode",
|
|
4389
|
+
commitState: "committed",
|
|
4390
|
+
cause: new Error("Missing rendered atomic plan"),
|
|
4391
|
+
});
|
|
4392
|
+
}
|
|
4393
|
+
try {
|
|
4394
|
+
return EffectQbExecutor.decodeRows(entry.rendered, entry.plan, [...(result.results ?? [])], { driverMode: "raw" });
|
|
4395
|
+
}
|
|
4396
|
+
catch (error) {
|
|
4397
|
+
throw new AtomicPlanError({
|
|
4398
|
+
statement,
|
|
4399
|
+
phase: "decode",
|
|
4400
|
+
commitState: "committed",
|
|
4401
|
+
cause: error,
|
|
4402
|
+
});
|
|
4403
|
+
}
|
|
4404
|
+
}),
|
|
4405
|
+
catch: (cause) => cause instanceof AtomicPlanError
|
|
4406
|
+
? cause
|
|
4407
|
+
: new AtomicPlanError({
|
|
4408
|
+
phase: "decode",
|
|
4409
|
+
commitState: "committed",
|
|
4410
|
+
cause,
|
|
4411
|
+
}),
|
|
4412
|
+
});
|
|
4413
|
+
}))));
|
|
4414
|
+
};
|
|
4415
|
+
return { dialect: "sqlite", execute };
|
|
4416
|
+
};
|
|
4417
|
+
export const D1EffectQbSqliteAuthStorageLive = (database, options = {}) => EffectQbSqliteAuthStorageLive(makeD1EffectQbSqliteExecutor(database), makeD1EffectQbSqliteAtomicPlanExecutor(database), options);
|
|
2824
4418
|
//# sourceMappingURL=EffectQbSqliteStorage.js.map
|