@effect-auth/core 0.1.0-alpha.16 → 0.1.0-alpha.19
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 +307 -133
- 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 +0 -12
- 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/AuthFlow.d.ts +128 -38
- package/dist/AuthFlow.d.ts.map +1 -1
- package/dist/AuthFlow.js +611 -263
- package/dist/AuthFlow.js.map +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 +1 -4
- 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/DevEmail.d.ts +44 -0
- package/dist/DevEmail.d.ts.map +1 -0
- package/dist/DevEmail.js +115 -0
- package/dist/DevEmail.js.map +1 -0
- package/dist/DevelopmentSeed.d.ts +47 -0
- package/dist/DevelopmentSeed.d.ts.map +1 -0
- package/dist/DevelopmentSeed.js +337 -0
- package/dist/DevelopmentSeed.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 +4 -4
- package/dist/DrizzleD1SqliteStorage.d.ts.map +1 -1
- package/dist/DrizzleD1SqliteStorage.js +5 -5
- package/dist/DrizzleD1SqliteStorage.js.map +1 -1
- package/dist/DrizzleEffectSqliteStorage.d.ts +13 -4
- package/dist/DrizzleEffectSqliteStorage.d.ts.map +1 -1
- package/dist/DrizzleEffectSqliteStorage.js +71 -4
- package/dist/DrizzleEffectSqliteStorage.js.map +1 -1
- 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/DrizzlePostgresStorage.d.ts +26 -0
- package/dist/DrizzlePostgresStorage.d.ts.map +1 -0
- package/dist/DrizzlePostgresStorage.js +14 -0
- package/dist/DrizzlePostgresStorage.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 +178 -0
- package/dist/EffectQbPostgresStorage.d.ts.map +1 -0
- package/dist/EffectQbPostgresStorage.js +4637 -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 +221 -23
- package/dist/EffectQbSqliteStorage.d.ts.map +1 -1
- package/dist/EffectQbSqliteStorage.js +2064 -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 +939 -884
- package/dist/HttpApi/Api.d.ts.map +1 -1
- package/dist/HttpApi/Api.js +2254 -940
- 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 +661 -301
- package/dist/HttpApi/Endpoints.d.ts.map +1 -1
- package/dist/HttpApi/Endpoints.js +207 -120
- package/dist/HttpApi/Endpoints.js.map +1 -1
- package/dist/HttpApi/Errors.d.ts +37 -10
- package/dist/HttpApi/Errors.d.ts.map +1 -1
- package/dist/HttpApi/Errors.js +42 -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 +26 -0
- package/dist/HttpApi/Middleware.d.ts.map +1 -0
- package/dist/HttpApi/Middleware.js +13 -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/OAuthDeviceAuthorization.d.ts +106 -0
- package/dist/HttpApi/OAuthDeviceAuthorization.d.ts.map +1 -0
- package/dist/HttpApi/OAuthDeviceAuthorization.js +415 -0
- package/dist/HttpApi/OAuthDeviceAuthorization.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/OAuthDeviceAuthorization.d.ts +23 -0
- package/dist/HttpApi/Operations/OAuthDeviceAuthorization.d.ts.map +1 -0
- package/dist/HttpApi/Operations/OAuthDeviceAuthorization.js +7 -0
- package/dist/HttpApi/Operations/OAuthDeviceAuthorization.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 +26 -0
- package/dist/HttpApi/Operations/OAuthToken.d.ts.map +1 -0
- package/dist/HttpApi/Operations/OAuthToken.js +9 -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 +28 -0
- package/dist/HttpApi/Operations/index.d.ts.map +1 -0
- package/dist/HttpApi/Operations/index.js +28 -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 +399 -84
- package/dist/HttpApi/Schemas.d.ts.map +1 -1
- package/dist/HttpApi/Schemas.js +225 -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 +11 -3
- package/dist/HttpApi/index.d.ts.map +1 -1
- package/dist/HttpApi/index.js +10 -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 +8 -29
- package/dist/Jwt.d.ts.map +1 -1
- package/dist/Jwt.js +67 -56
- package/dist/Jwt.js.map +1 -1
- package/dist/LoginApproval.d.ts +15 -30
- 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 +7 -11
- 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/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 +19 -12
- 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 +138 -96
- package/dist/OAuth.d.ts.map +1 -1
- package/dist/OAuth.js +522 -221
- package/dist/OAuth.js.map +1 -1
- package/dist/OAuthDeviceAuthorization.d.ts +378 -0
- package/dist/OAuthDeviceAuthorization.d.ts.map +1 -0
- package/dist/OAuthDeviceAuthorization.js +860 -0
- package/dist/OAuthDeviceAuthorization.js.map +1 -0
- package/dist/OAuthDeviceClient.d.ts +76 -0
- package/dist/OAuthDeviceClient.d.ts.map +1 -0
- package/dist/OAuthDeviceClient.js +362 -0
- package/dist/OAuthDeviceClient.js.map +1 -0
- package/dist/Passkey.d.ts +15 -17
- 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 +295 -81
- 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 +16 -0
- package/dist/PostgresStorageMigrations.d.ts.map +1 -0
- package/dist/PostgresStorageMigrations.js +674 -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 +83 -11
- 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 +0 -9
- 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 +6 -1
- package/dist/Retention.d.ts.map +1 -1
- package/dist/Retention.js +49 -12
- 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/SqliteStorageSchemaData.d.ts +2 -0
- package/dist/SqliteStorageSchemaData.d.ts.map +1 -0
- package/dist/SqliteStorageSchemaData.js +508 -0
- package/dist/SqliteStorageSchemaData.js.map +1 -0
- 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 +7 -1
- package/dist/StorageMigrations.d.ts.map +1 -1
- package/dist/StorageMigrations.js +256 -0
- package/dist/StorageMigrations.js.map +1 -1
- package/dist/StorageSchemaGenerator.d.ts +544 -0
- package/dist/StorageSchemaGenerator.d.ts.map +1 -0
- package/dist/StorageSchemaGenerator.js +1418 -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.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 +192 -34
- 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.map +1 -1
- package/dist/alchemy/cloudflare/Email.js +9 -4
- package/dist/alchemy/cloudflare/Email.js.map +1 -1
- package/dist/cloudflare/Email.js +1 -1
- package/dist/cloudflare/Email.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/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/0029_auth_oauth_device_authorization.sql +28 -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/0010_auth_oauth_device_authorization.sql +50 -0
- package/migrations/postgres/delegated-access/0001_auth_delegated_access.sql +112 -0
- package/package.json +309 -2
|
@@ -1,34 +1,60 @@
|
|
|
1
1
|
import { Data, Effect, Layer, Option, Redacted } from "effect";
|
|
2
|
-
import { Column, Query, Scalar, Table } from "effect-qb";
|
|
3
|
-
import { Executor, Query as SqliteQuery } from "effect-qb/sqlite";
|
|
4
|
-
import { AuditLogStore, AuditLogStoreError, auditEventActorUserId, auditEventUserId, filterAuditLogEvents, } from "./AuditLog.js";
|
|
2
|
+
import { Column, Executor as EffectQbExecutor, Query, Scalar, Table, } from "effect-qb";
|
|
3
|
+
import { Executor, Query as SqliteQuery, Renderer } from "effect-qb/sqlite";
|
|
5
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";
|
|
6
8
|
import { DomainName, DomainVerificationId, DomainVerificationProofToken, DomainVerificationStore, DomainVerificationStoreError, } from "./DomainVerification.js";
|
|
7
|
-
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";
|
|
8
11
|
import { currentUnixMillis } from "./Internal.js";
|
|
9
12
|
import { JwtError, JwtRevocationStore } from "./Jwt.js";
|
|
10
13
|
import { LoginApprovalReviewError, LoginApprovalReviewStore, } from "./LoginApproval.js";
|
|
11
14
|
import { LoginRiskContext, LoginRiskHistory, LoginRiskHistoryError, LoginRiskSignal, maxLoginRiskLevel, } from "./LoginRisk.js";
|
|
12
|
-
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";
|
|
16
|
+
import { OAuthDeviceAuthorizationId, OAuthDeviceAuthorizationStore, OAuthDeviceAuthorizationStoreError, OAuthDeviceCodeTokenCommitStore, OAuthDeviceCodeTokenCommitStoreError, } from "./OAuthDeviceAuthorization.js";
|
|
13
17
|
import { PasskeyCredentialId, PasskeyCredentialStore, PasskeyCredentialStoreError, } from "./Passkey.js";
|
|
14
|
-
import {
|
|
18
|
+
import { canonicalPermissionScope, normalizePermissionDefinitionListLimit, PermissionStore, PermissionStoreError, } from "./Permission.js";
|
|
19
|
+
import { RecoveryCodeHash, RecoveryCodeLoginCommitStore, RecoveryCodeLoginCommitStoreError, RecoveryCodeStore, RecoveryCodeStoreError, RecoveryCodeSessionRotateStore, RecoveryCodeSessionRotateStoreError, } from "./RecoveryCode.js";
|
|
15
20
|
import { RefreshTokenFamilyId, RefreshTokenId, RefreshTokenSecretHash, RefreshTokenStore, RefreshTokenStoreError, } from "./RefreshToken.js";
|
|
16
21
|
import { filterSecurityTimelineEvents, SecurityTimelineEventId, SecurityTimelineStore, SecurityTimelineStoreError, } from "./SecurityTimeline.js";
|
|
17
|
-
import { CredentialStore, SessionStore, StorageError, UserStore, VerificationStore, } from "./Storage.js";
|
|
18
|
-
import { TotpFactorStore, TotpFactorStoreError, TotpSecret, } from "./Totp.js";
|
|
22
|
+
import { CredentialStore, IdentityConflictError, IdentityStore, RegistrationConflictError, RegistrationFailureError, RegistrationStore, SessionStore, StorageError, UserStore, VerificationStore, } from "./Storage.js";
|
|
23
|
+
import { TotpFactorStore, TotpFactorStoreError, TotpLoginCommitStore, TotpLoginCommitStoreError, TotpSessionRotateStore, TotpSessionRotateStoreError, TotpSecret, } from "./Totp.js";
|
|
19
24
|
import { filterTrustedDevicesForUser, TrustedDeviceError, TrustedDeviceStore, } from "./TrustedDevice.js";
|
|
20
25
|
import { filterDueWebhookOutboxRecords, WebhookOutboxItemId, WebhookOutboxStore, WebhookOutboxStoreError, WebhookReplayStore, WebhookReplayStoreError, } from "./Webhook.js";
|
|
26
|
+
const boundColumnName = (expression) => {
|
|
27
|
+
const state = expression[Symbol.for("effect-qb/BoundColumn")];
|
|
28
|
+
if (typeof state?.columnName !== "string") {
|
|
29
|
+
throw new TypeError("Atomic SQLite storage requires bound table columns");
|
|
30
|
+
}
|
|
31
|
+
return state.columnName;
|
|
32
|
+
};
|
|
21
33
|
export class D1EffectQbError extends Data.TaggedError("D1EffectQbError") {
|
|
22
34
|
}
|
|
23
35
|
const makeAuthUserTable = (name) => Table.make(name, {
|
|
24
36
|
id: Column.text().pipe(Column.primaryKey),
|
|
25
|
-
email: Column.text(),
|
|
26
|
-
email_verified: Column.int(),
|
|
27
37
|
created_at: Column.int(),
|
|
28
38
|
updated_at: Column.int(),
|
|
29
39
|
disabled_at: Column.int().pipe(Column.nullable),
|
|
30
40
|
metadata: Column.text().pipe(Column.nullable),
|
|
31
41
|
});
|
|
42
|
+
const makeAuthUserIdentityTable = (name) => Table.make(name, {
|
|
43
|
+
id: Column.text().pipe(Column.primaryKey),
|
|
44
|
+
user_id: Column.text(),
|
|
45
|
+
scope_type: Column.text(),
|
|
46
|
+
scope_id: Column.text(),
|
|
47
|
+
kind: Column.text(),
|
|
48
|
+
value: Column.text(),
|
|
49
|
+
normalized_value: Column.text(),
|
|
50
|
+
verified_at: Column.int().pipe(Column.nullable),
|
|
51
|
+
is_primary_login: Column.int(),
|
|
52
|
+
created_at: Column.int(),
|
|
53
|
+
updated_at: Column.int(),
|
|
54
|
+
revoked_at: Column.int().pipe(Column.nullable),
|
|
55
|
+
replaced_by_id: Column.text().pipe(Column.nullable),
|
|
56
|
+
metadata: Column.text().pipe(Column.nullable),
|
|
57
|
+
});
|
|
32
58
|
const makeAuthCredentialTable = (name) => Table.make(name, {
|
|
33
59
|
id: Column.text().pipe(Column.primaryKey),
|
|
34
60
|
user_id: Column.text(),
|
|
@@ -46,6 +72,7 @@ const makeAuthSessionTable = (name) => Table.make(name, {
|
|
|
46
72
|
created_at: Column.int(),
|
|
47
73
|
expires_at: Column.int(),
|
|
48
74
|
auth_time: Column.int(),
|
|
75
|
+
authentication_events: Column.text(),
|
|
49
76
|
aal: Column.text(),
|
|
50
77
|
amr: Column.text(),
|
|
51
78
|
mfa_verified_at: Column.int().pipe(Column.nullable),
|
|
@@ -65,6 +92,7 @@ const makeAuthVerificationTable = (name) => Table.make(name, {
|
|
|
65
92
|
consumed_at: Column.int().pipe(Column.nullable),
|
|
66
93
|
});
|
|
67
94
|
const makeAuthAuditLogTable = (name) => Table.make(name, {
|
|
95
|
+
storage_id: Column.int().pipe(Column.primaryKey),
|
|
68
96
|
id: Column.text().pipe(Column.nullable),
|
|
69
97
|
type: Column.text(),
|
|
70
98
|
user_id: Column.text().pipe(Column.nullable),
|
|
@@ -142,6 +170,7 @@ const makeAuthTotpFactorTable = (name) => Table.make(name, {
|
|
|
142
170
|
created_at: Column.int(),
|
|
143
171
|
confirmed_at: Column.int().pipe(Column.nullable),
|
|
144
172
|
last_used_at: Column.int().pipe(Column.nullable),
|
|
173
|
+
last_accepted_counter: Column.int().pipe(Column.nullable),
|
|
145
174
|
revoked_at: Column.int().pipe(Column.nullable),
|
|
146
175
|
metadata: Column.text().pipe(Column.nullable),
|
|
147
176
|
});
|
|
@@ -249,8 +278,27 @@ const makeAuthOAuthAuthorizationCodeTable = (name) => Table.make(name, {
|
|
|
249
278
|
consumed_at: Column.int().pipe(Column.nullable),
|
|
250
279
|
metadata: Column.text().pipe(Column.nullable),
|
|
251
280
|
});
|
|
281
|
+
const makeAuthOAuthDeviceAuthorizationTable = (name) => Table.make(name, {
|
|
282
|
+
id: Column.text().pipe(Column.primaryKey),
|
|
283
|
+
device_code_hash: Column.text().pipe(Column.unique),
|
|
284
|
+
user_code_hash: Column.text().pipe(Column.unique),
|
|
285
|
+
client_id: Column.text(),
|
|
286
|
+
requested_scopes: Column.text(),
|
|
287
|
+
granted_scopes: Column.text().pipe(Column.nullable),
|
|
288
|
+
subject: Column.text().pipe(Column.nullable),
|
|
289
|
+
status: Column.text(),
|
|
290
|
+
issued_at: Column.int(),
|
|
291
|
+
expires_at: Column.int(),
|
|
292
|
+
poll_interval_seconds: Column.int(),
|
|
293
|
+
next_poll_at: Column.int(),
|
|
294
|
+
last_polled_at: Column.int().pipe(Column.nullable),
|
|
295
|
+
approved_at: Column.int().pipe(Column.nullable),
|
|
296
|
+
denied_at: Column.int().pipe(Column.nullable),
|
|
297
|
+
consumed_at: Column.int().pipe(Column.nullable),
|
|
298
|
+
metadata: Column.text().pipe(Column.nullable),
|
|
299
|
+
});
|
|
252
300
|
const makeAuthOAuthClientSecretTable = (name) => Table.make(name, {
|
|
253
|
-
prefix: Column.text()
|
|
301
|
+
prefix: Column.text(),
|
|
254
302
|
client_id: Column.text(),
|
|
255
303
|
secret_hash: Column.text(),
|
|
256
304
|
authentication_methods: Column.text(),
|
|
@@ -320,6 +368,52 @@ const makeAuthWebhookReplayTable = (name) => Table.make(name, {
|
|
|
320
368
|
expires_at: Column.int(),
|
|
321
369
|
created_at: Column.int(),
|
|
322
370
|
});
|
|
371
|
+
const makeAuthPermissionDefinitionTable = (name) => Table.make(name, {
|
|
372
|
+
id: Column.text().pipe(Column.primaryKey),
|
|
373
|
+
description: Column.text().pipe(Column.nullable),
|
|
374
|
+
scope_type_present: Column.int(),
|
|
375
|
+
scope_type: Column.text(),
|
|
376
|
+
created_at: Column.int(),
|
|
377
|
+
updated_at: Column.int(),
|
|
378
|
+
disabled_at: Column.int().pipe(Column.nullable),
|
|
379
|
+
deleted_at: Column.int().pipe(Column.nullable),
|
|
380
|
+
});
|
|
381
|
+
const makeAuthRoleDefinitionTable = (name) => Table.make(name, {
|
|
382
|
+
id: Column.text().pipe(Column.primaryKey),
|
|
383
|
+
description: Column.text().pipe(Column.nullable),
|
|
384
|
+
created_at: Column.int(),
|
|
385
|
+
updated_at: Column.int(),
|
|
386
|
+
disabled_at: Column.int().pipe(Column.nullable),
|
|
387
|
+
deleted_at: Column.int().pipe(Column.nullable),
|
|
388
|
+
});
|
|
389
|
+
const makeAuthPermissionGrantTable = (name) => Table.make(name, {
|
|
390
|
+
subject_type: Column.text(),
|
|
391
|
+
subject_id: Column.text(),
|
|
392
|
+
permission_id: Column.text(),
|
|
393
|
+
scope_type: Column.text(),
|
|
394
|
+
scope_id_present: Column.int(),
|
|
395
|
+
scope_id: Column.text(),
|
|
396
|
+
expires_at: Column.int().pipe(Column.nullable),
|
|
397
|
+
metadata: Column.text().pipe(Column.nullable),
|
|
398
|
+
revoked_at: Column.int().pipe(Column.nullable),
|
|
399
|
+
});
|
|
400
|
+
const makeAuthRoleGrantTable = (name) => Table.make(name, {
|
|
401
|
+
subject_type: Column.text(),
|
|
402
|
+
subject_id: Column.text(),
|
|
403
|
+
role_id: Column.text(),
|
|
404
|
+
scope_type: Column.text(),
|
|
405
|
+
scope_id_present: Column.int(),
|
|
406
|
+
scope_id: Column.text(),
|
|
407
|
+
expires_at: Column.int().pipe(Column.nullable),
|
|
408
|
+
metadata: Column.text().pipe(Column.nullable),
|
|
409
|
+
revoked_at: Column.int().pipe(Column.nullable),
|
|
410
|
+
});
|
|
411
|
+
const makeAuthRolePermissionTable = (name) => Table.make(name, {
|
|
412
|
+
role_id: Column.text(),
|
|
413
|
+
permission_id: Column.text(),
|
|
414
|
+
scope_type_present: Column.int(),
|
|
415
|
+
scope_type: Column.text(),
|
|
416
|
+
});
|
|
323
417
|
const authSqliteTablesTypeId = Symbol.for("@effect-auth/core/EffectQbSqliteStorage/AuthSqliteTables");
|
|
324
418
|
const jsonEncode = (value) => value === undefined ? null : JSON.stringify(value);
|
|
325
419
|
const jsonDecode = (value) => {
|
|
@@ -346,14 +440,60 @@ const decodeBoolean = (value) => {
|
|
|
346
440
|
};
|
|
347
441
|
const mutationValues = (value) => value;
|
|
348
442
|
const queryInsert = (table, values) => Query.insert(table, values);
|
|
443
|
+
const queryInsertMany = (table, values) => Query.from(Query.values(values))(Query.insert(table));
|
|
349
444
|
const queryUpdate = (table, values) => Query.update(table, values);
|
|
350
445
|
const queryEq = (left, right) => Query.eq(left, right);
|
|
446
|
+
const queryNeq = (left, right) => Query.neq(left, right);
|
|
351
447
|
const queryGt = (left, right) => Query.gt(left, right);
|
|
352
448
|
const queryLt = (left, right) => Query.lt(left, right);
|
|
353
449
|
const queryLte = (left, right) => Query.lte(left, right);
|
|
354
450
|
const queryIsNull = (value) => Query.isNull(value);
|
|
451
|
+
const queryIsNotNull = (value) => Query.isNotNull(value);
|
|
452
|
+
const queryIsNotDistinctFrom = (left, right) => Query.isNotDistinctFrom(left, right);
|
|
453
|
+
const queryAnd = (...values) => Query.and(...values);
|
|
454
|
+
const queryOr = (...values) => Query.or(...values);
|
|
455
|
+
const queryNot = (value) => Query.not(value);
|
|
456
|
+
const queryExists = (plan) => Query.exists(plan);
|
|
457
|
+
const queryScalar = (plan) => Query.scalar(plan);
|
|
458
|
+
const queryChanges = () => Query.call("changes");
|
|
459
|
+
const queryChangedOne = () => queryEq(queryChanges(), 1);
|
|
460
|
+
const queryWithoutNullRefinement = (value) => Query.cast(value, value[Scalar.TypeId].dbType);
|
|
355
461
|
const queryIn = (value, first, ...rest) => Query.in(value, first, ...rest);
|
|
462
|
+
const sqliteCastLike = (value, column) => Query.cast(value, column[Scalar.TypeId].dbType);
|
|
463
|
+
const sqliteIncrementByFive = (value) => Query.call("length", Query.concat(Query.call("printf", "%.*c", value, "x"), "xxxxx"));
|
|
464
|
+
const sqliteUnixMillisAfterSeconds = (now, seconds, textColumn, millisColumn) => {
|
|
465
|
+
const secondsText = sqliteCastLike(seconds, textColumn);
|
|
466
|
+
const modifier = Query.concat("+", secondsText, " seconds");
|
|
467
|
+
const epochSeconds = Query.call("strftime", "%s", new Date(Number(now)).toISOString(), modifier);
|
|
468
|
+
const fractionalSeconds = Query.call("strftime", "%f", new Date(Number(now)).toISOString(), modifier);
|
|
469
|
+
const milliseconds = Query.call("substr", fractionalSeconds, -3);
|
|
470
|
+
return sqliteCastLike(Query.concat(epochSeconds, milliseconds), millisColumn);
|
|
471
|
+
};
|
|
472
|
+
const aliasTable = (table, alias) => Table.alias(table, alias);
|
|
473
|
+
const aliasColumn = (table, column) => table[boundColumnName(column)];
|
|
474
|
+
const queryAstTypeId = Symbol.for("effect-qb/QueryAst");
|
|
475
|
+
const targetMutationExpression = (table, columnName, value) => {
|
|
476
|
+
if (value === null) {
|
|
477
|
+
return Query.literal(null);
|
|
478
|
+
}
|
|
479
|
+
const plan = queryUpdate(table, { [columnName]: value });
|
|
480
|
+
const ast = plan[queryAstTypeId];
|
|
481
|
+
const assignment = ast?.set?.find((candidate) => candidate.columnName === columnName);
|
|
482
|
+
if (assignment === undefined) {
|
|
483
|
+
throw new TypeError(`Invalid SQLite mutation column: ${columnName}`);
|
|
484
|
+
}
|
|
485
|
+
return assignment.value;
|
|
486
|
+
};
|
|
487
|
+
const queryInsertWhere = (table, values, predicate) => {
|
|
488
|
+
const selection = Object.fromEntries(Object.entries(values).map(([key, value]) => [
|
|
489
|
+
key,
|
|
490
|
+
targetMutationExpression(table, key, value),
|
|
491
|
+
]));
|
|
492
|
+
const select = Query.where(predicate)(Query.select(selection));
|
|
493
|
+
return Query.from(select)(Query.insert(table));
|
|
494
|
+
};
|
|
356
495
|
const executePlan = (executor, plan) => executor.execute(plan);
|
|
496
|
+
const executeAtomicPlans = (atomic, plans) => atomic.execute(plans);
|
|
357
497
|
const omitUndefined = (record) => Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
|
|
358
498
|
const sessionMetadataEnvelopeKey = "__effectAuthSession";
|
|
359
499
|
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -426,13 +566,31 @@ const encodeSessionMetadata = (row) => {
|
|
|
426
566
|
};
|
|
427
567
|
const decodeDefaultUserRow = (row) => omitUndefined({
|
|
428
568
|
id: UserId(row.id),
|
|
429
|
-
email: Email(row.email),
|
|
430
|
-
emailVerified: decodeBoolean(row.emailVerified),
|
|
431
569
|
createdAt: UnixMillis(Number(row.createdAt)),
|
|
432
570
|
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
433
571
|
disabledAt: optionalUnixMillis(row.disabledAt),
|
|
434
572
|
metadata: jsonDecode(row.metadata),
|
|
435
573
|
});
|
|
574
|
+
const decodeDefaultUserIdentityRow = (row) => omitUndefined({
|
|
575
|
+
id: IdentityId(row.id),
|
|
576
|
+
userId: UserId(row.userId),
|
|
577
|
+
scope: row.scopeType === "global"
|
|
578
|
+
? { type: "global" }
|
|
579
|
+
: { type: "tenant", tenantId: row.scopeId },
|
|
580
|
+
kind: row.kind,
|
|
581
|
+
value: row.value,
|
|
582
|
+
normalizedValue: row.normalizedValue,
|
|
583
|
+
verifiedAt: optionalUnixMillis(row.verifiedAt),
|
|
584
|
+
isPrimaryLogin: decodeBoolean(row.isPrimaryLogin),
|
|
585
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
586
|
+
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
587
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
588
|
+
replacedById: row.replacedById === null ? undefined : IdentityId(row.replacedById),
|
|
589
|
+
metadata: jsonDecode(row.metadata),
|
|
590
|
+
});
|
|
591
|
+
const decodeUserIdentityRow = (tables, row) => tables.identity.decode === undefined
|
|
592
|
+
? decodeDefaultUserIdentityRow(row)
|
|
593
|
+
: tables.identity.decode(row);
|
|
436
594
|
const decodeUserRow = (tables, row) => tables.user.decode === undefined
|
|
437
595
|
? decodeDefaultUserRow(row)
|
|
438
596
|
: tables.user.decode(row);
|
|
@@ -450,6 +608,34 @@ const decodePasswordCredentialRow = (tables, row) => tables.credential.decodePas
|
|
|
450
608
|
: tables.credential.decodePassword(row);
|
|
451
609
|
const decodeDefaultSessionRow = (row) => {
|
|
452
610
|
const sessionMetadata = decodeSessionMetadata(row.metadata);
|
|
611
|
+
const authenticationEvents = parseAuthenticationEvents(jsonDecode(row.authenticationEvents));
|
|
612
|
+
if (authenticationEvents.some((event) => event.type === "custom")) {
|
|
613
|
+
return omitUndefined({
|
|
614
|
+
id: SessionId(row.id),
|
|
615
|
+
userId: UserId(row.userId),
|
|
616
|
+
secretHash: row.secretHash,
|
|
617
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
618
|
+
expiresAt: UnixMillis(Number(row.expiresAt)),
|
|
619
|
+
authTime: UnixMillis(Number(row.authTime)),
|
|
620
|
+
authenticationEvents,
|
|
621
|
+
aal: row.aal,
|
|
622
|
+
amr: decodeAmr(row.amr),
|
|
623
|
+
mfaVerifiedAt: optionalUnixMillis(row.mfaVerifiedAt),
|
|
624
|
+
claims: sessionMetadata.claims,
|
|
625
|
+
metadata: sessionMetadata.metadata,
|
|
626
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
627
|
+
lastSeenAt: optionalUnixMillis(row.lastSeenAt),
|
|
628
|
+
rotatedAt: optionalUnixMillis(row.rotatedAt),
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
const assurance = deriveAssurance(authenticationEvents);
|
|
632
|
+
const mfaVerifiedAt = deriveMfaVerifiedAt(authenticationEvents);
|
|
633
|
+
if (row.aal !== assurance.level ||
|
|
634
|
+
JSON.stringify(decodeAmr(row.amr)) !== JSON.stringify(assurance.amr) ||
|
|
635
|
+
optionalUnixMillis(row.mfaVerifiedAt) !== mfaVerifiedAt ||
|
|
636
|
+
Number(row.authTime) !== Number(assurance.authenticatedAt)) {
|
|
637
|
+
throw new Error("Session assurance summary does not match authentication events");
|
|
638
|
+
}
|
|
453
639
|
return omitUndefined({
|
|
454
640
|
id: SessionId(row.id),
|
|
455
641
|
userId: UserId(row.userId),
|
|
@@ -457,6 +643,7 @@ const decodeDefaultSessionRow = (row) => {
|
|
|
457
643
|
createdAt: UnixMillis(Number(row.createdAt)),
|
|
458
644
|
expiresAt: UnixMillis(Number(row.expiresAt)),
|
|
459
645
|
authTime: UnixMillis(Number(row.authTime)),
|
|
646
|
+
authenticationEvents,
|
|
460
647
|
aal: row.aal,
|
|
461
648
|
amr: decodeAmr(row.amr),
|
|
462
649
|
mfaVerifiedAt: optionalUnixMillis(row.mfaVerifiedAt),
|
|
@@ -467,9 +654,13 @@ const decodeDefaultSessionRow = (row) => {
|
|
|
467
654
|
rotatedAt: optionalUnixMillis(row.rotatedAt),
|
|
468
655
|
});
|
|
469
656
|
};
|
|
470
|
-
const decodeSessionRow = (tables, row) =>
|
|
471
|
-
|
|
472
|
-
|
|
657
|
+
const decodeSessionRow = (tables, row) => {
|
|
658
|
+
const decoded = tables.session.decode === undefined
|
|
659
|
+
? decodeDefaultSessionRow(row)
|
|
660
|
+
: tables.session.decode(row);
|
|
661
|
+
assertSessionAssurance(decoded);
|
|
662
|
+
return decoded;
|
|
663
|
+
};
|
|
473
664
|
const decodeDefaultVerificationRow = (row) => omitUndefined({
|
|
474
665
|
id: ChallengeId(row.id),
|
|
475
666
|
type: row.type,
|
|
@@ -483,7 +674,7 @@ const decodeDefaultVerificationRow = (row) => omitUndefined({
|
|
|
483
674
|
const decodeVerificationRow = (tables, row) => tables.verification.decode === undefined
|
|
484
675
|
? decodeDefaultVerificationRow(row)
|
|
485
676
|
: tables.verification.decode(row);
|
|
486
|
-
const decodeDefaultAuditLogEvent = (row) => jsonDecode(row.event);
|
|
677
|
+
const decodeDefaultAuditLogEvent = (row) => decodeAuditEvent(jsonDecode(row.event));
|
|
487
678
|
const decodeAuditLogEvent = (tables, row) => tables.auditLog.decode === undefined
|
|
488
679
|
? decodeDefaultAuditLogEvent(row)
|
|
489
680
|
: tables.auditLog.decode(row);
|
|
@@ -523,7 +714,9 @@ const decodeDefaultLoginRiskHistoryRecord = (row) => omitUndefined({
|
|
|
523
714
|
locationKey: row.locationKey ?? undefined,
|
|
524
715
|
country: row.country ?? undefined,
|
|
525
716
|
region: row.region ?? undefined,
|
|
526
|
-
latitude: row.latitudeMicro === null
|
|
717
|
+
latitude: row.latitudeMicro === null
|
|
718
|
+
? undefined
|
|
719
|
+
: Number(row.latitudeMicro) / 1_000_000,
|
|
527
720
|
longitude: row.longitudeMicro === null
|
|
528
721
|
? undefined
|
|
529
722
|
: Number(row.longitudeMicro) / 1_000_000,
|
|
@@ -546,7 +739,9 @@ const decodeTrustedDeviceRow = (tables, row) => tables.trustedDevice.decode ===
|
|
|
546
739
|
: tables.trustedDevice.decode(row);
|
|
547
740
|
const decodePasskeyTransports = (value) => {
|
|
548
741
|
const decoded = jsonDecode(value);
|
|
549
|
-
return Array.isArray(decoded)
|
|
742
|
+
return Array.isArray(decoded)
|
|
743
|
+
? decoded
|
|
744
|
+
: undefined;
|
|
550
745
|
};
|
|
551
746
|
const decodeDefaultPasskeyCredentialRow = (row) => omitUndefined({
|
|
552
747
|
id: CredentialId(row.id),
|
|
@@ -576,6 +771,9 @@ const decodeDefaultTotpFactorRow = (row) => omitUndefined({
|
|
|
576
771
|
createdAt: UnixMillis(Number(row.createdAt)),
|
|
577
772
|
confirmedAt: optionalUnixMillis(row.confirmedAt),
|
|
578
773
|
lastUsedAt: optionalUnixMillis(row.lastUsedAt),
|
|
774
|
+
lastAcceptedCounter: row.lastAcceptedCounter === null
|
|
775
|
+
? undefined
|
|
776
|
+
: Number(row.lastAcceptedCounter),
|
|
579
777
|
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
580
778
|
metadata: jsonDecode(row.metadata),
|
|
581
779
|
});
|
|
@@ -712,7 +910,8 @@ const decodeOAuthConsentRow = (tables, row) => tables.oauthConsent.decode === un
|
|
|
712
910
|
: tables.oauthConsent.decode(row);
|
|
713
911
|
const oauthConsentCoversScopes = (record, scopes = []) => scopes.every((scope) => record.scopes.includes(scope));
|
|
714
912
|
const isOAuthConsentActive = (record, input) => record.revokedAt === undefined &&
|
|
715
|
-
(record.expiresAt === undefined ||
|
|
913
|
+
(record.expiresAt === undefined ||
|
|
914
|
+
Number(record.expiresAt) > Number(input.now)) &&
|
|
716
915
|
oauthConsentCoversScopes(record, input.scopes);
|
|
717
916
|
const oauthClientRedirectUriValidationResult = (client, input) => {
|
|
718
917
|
if (client === undefined) {
|
|
@@ -740,7 +939,9 @@ const decodeDefaultOAuthAuthorizationCodeRow = (row) => omitUndefined({
|
|
|
740
939
|
subject: row.subject,
|
|
741
940
|
redirectUri: row.redirectUri,
|
|
742
941
|
scopes: decodeStringArray(row.scopes),
|
|
743
|
-
codeChallenge: row.codeChallenge === null
|
|
942
|
+
codeChallenge: row.codeChallenge === null
|
|
943
|
+
? undefined
|
|
944
|
+
: OAuthCodeChallenge(row.codeChallenge),
|
|
744
945
|
codeChallengeMethod: row.codeChallengeMethod === null
|
|
745
946
|
? undefined
|
|
746
947
|
: row.codeChallengeMethod,
|
|
@@ -752,6 +953,42 @@ const decodeDefaultOAuthAuthorizationCodeRow = (row) => omitUndefined({
|
|
|
752
953
|
const decodeOAuthAuthorizationCodeRow = (tables, row) => tables.oauthAuthorizationCode.decode === undefined
|
|
753
954
|
? decodeDefaultOAuthAuthorizationCodeRow(row)
|
|
754
955
|
: tables.oauthAuthorizationCode.decode(row);
|
|
956
|
+
const decodeDefaultOAuthDeviceAuthorizationRow = (row) => {
|
|
957
|
+
const base = omitUndefined({
|
|
958
|
+
id: OAuthDeviceAuthorizationId(row.id),
|
|
959
|
+
deviceCodeHash: row.deviceCodeHash,
|
|
960
|
+
userCodeHash: row.userCodeHash,
|
|
961
|
+
clientId: OAuthClientId(row.clientId),
|
|
962
|
+
requestedScopes: decodeStringArray(row.requestedScopes),
|
|
963
|
+
issuedAt: UnixMillis(Number(row.issuedAt)),
|
|
964
|
+
expiresAt: UnixMillis(Number(row.expiresAt)),
|
|
965
|
+
pollIntervalSeconds: Number(row.pollIntervalSeconds),
|
|
966
|
+
nextPollAt: UnixMillis(Number(row.nextPollAt)),
|
|
967
|
+
lastPolledAt: optionalUnixMillis(row.lastPolledAt),
|
|
968
|
+
metadata: jsonDecode(row.metadata),
|
|
969
|
+
});
|
|
970
|
+
if (row.status === "approved") {
|
|
971
|
+
return {
|
|
972
|
+
...base,
|
|
973
|
+
status: "approved",
|
|
974
|
+
subject: row.subject ?? "",
|
|
975
|
+
grantedScopes: decodeStringArray(row.grantedScopes),
|
|
976
|
+
approvedAt: UnixMillis(Number(row.approvedAt)),
|
|
977
|
+
...omitUndefined({ consumedAt: optionalUnixMillis(row.consumedAt) }),
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
if (row.status === "denied") {
|
|
981
|
+
return {
|
|
982
|
+
...base,
|
|
983
|
+
status: "denied",
|
|
984
|
+
deniedAt: UnixMillis(Number(row.deniedAt)),
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
return { ...base, status: "pending" };
|
|
988
|
+
};
|
|
989
|
+
const decodeOAuthDeviceAuthorizationRow = (table, row) => table.decode === undefined
|
|
990
|
+
? decodeDefaultOAuthDeviceAuthorizationRow(row)
|
|
991
|
+
: table.decode(row);
|
|
755
992
|
const decodeDefaultOAuthClientSecretRow = (row) => omitUndefined({
|
|
756
993
|
prefix: OAuthClientSecretPrefix(row.prefix),
|
|
757
994
|
clientId: OAuthClientId(row.clientId),
|
|
@@ -840,6 +1077,82 @@ const decodeDefaultWebhookOutboxRecord = (row) => omitUndefined({
|
|
|
840
1077
|
const decodeWebhookOutboxRecord = (tables, row) => tables.webhookOutbox.decode === undefined
|
|
841
1078
|
? decodeDefaultWebhookOutboxRecord(row)
|
|
842
1079
|
: tables.webhookOutbox.decode(row);
|
|
1080
|
+
const decodeDefaultPermissionDefinition = (row) => omitUndefined({
|
|
1081
|
+
id: PermissionId(row.id),
|
|
1082
|
+
description: row.description ?? undefined,
|
|
1083
|
+
scopeType: decodeBoolean(row.scopeTypePresent) ? row.scopeType : undefined,
|
|
1084
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
1085
|
+
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
1086
|
+
disabledAt: optionalUnixMillis(row.disabledAt),
|
|
1087
|
+
deletedAt: optionalUnixMillis(row.deletedAt),
|
|
1088
|
+
});
|
|
1089
|
+
const decodePermissionDefinition = (tables, row) => tables.permissionDefinition.decode === undefined
|
|
1090
|
+
? decodeDefaultPermissionDefinition(row)
|
|
1091
|
+
: tables.permissionDefinition.decode(row);
|
|
1092
|
+
const decodeDefaultRoleDefinition = (row) => omitUndefined({
|
|
1093
|
+
id: RoleId(row.id),
|
|
1094
|
+
description: row.description ?? undefined,
|
|
1095
|
+
createdAt: UnixMillis(Number(row.createdAt)),
|
|
1096
|
+
updatedAt: UnixMillis(Number(row.updatedAt)),
|
|
1097
|
+
disabledAt: optionalUnixMillis(row.disabledAt),
|
|
1098
|
+
deletedAt: optionalUnixMillis(row.deletedAt),
|
|
1099
|
+
});
|
|
1100
|
+
const decodeRoleDefinition = (tables, row) => tables.roleDefinition.decode === undefined
|
|
1101
|
+
? decodeDefaultRoleDefinition(row)
|
|
1102
|
+
: tables.roleDefinition.decode(row);
|
|
1103
|
+
const decodePermissionScope = (scopeType, scopeIdPresent, scopeId) => {
|
|
1104
|
+
const hasId = decodeBoolean(scopeIdPresent);
|
|
1105
|
+
if (scopeType === "global" && !hasId) {
|
|
1106
|
+
return undefined;
|
|
1107
|
+
}
|
|
1108
|
+
return hasId ? { type: scopeType, id: scopeId } : { type: scopeType };
|
|
1109
|
+
};
|
|
1110
|
+
const decodeDefaultPermissionGrant = (row) => omitUndefined({
|
|
1111
|
+
subject: { type: row.subjectType, id: row.subjectId },
|
|
1112
|
+
permission: PermissionId(row.permissionId),
|
|
1113
|
+
scope: decodePermissionScope(row.scopeType, row.scopeIdPresent, row.scopeId),
|
|
1114
|
+
expiresAt: optionalUnixMillis(row.expiresAt),
|
|
1115
|
+
metadata: jsonDecode(row.metadata),
|
|
1116
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
1117
|
+
});
|
|
1118
|
+
const decodePermissionGrant = (tables, row) => {
|
|
1119
|
+
const decoded = tables.permissionGrant.decode === undefined
|
|
1120
|
+
? decodeDefaultPermissionGrant(row)
|
|
1121
|
+
: tables.permissionGrant.decode(row);
|
|
1122
|
+
const { scope: _scope, ...withoutScope } = decoded;
|
|
1123
|
+
const scope = canonicalPermissionScope(decoded.scope);
|
|
1124
|
+
return {
|
|
1125
|
+
...withoutScope,
|
|
1126
|
+
...(scope === undefined ? {} : { scope }),
|
|
1127
|
+
};
|
|
1128
|
+
};
|
|
1129
|
+
const decodeDefaultRoleGrant = (row) => omitUndefined({
|
|
1130
|
+
subject: { type: row.subjectType, id: row.subjectId },
|
|
1131
|
+
role: RoleId(row.roleId),
|
|
1132
|
+
scope: decodePermissionScope(row.scopeType, row.scopeIdPresent, row.scopeId),
|
|
1133
|
+
expiresAt: optionalUnixMillis(row.expiresAt),
|
|
1134
|
+
metadata: jsonDecode(row.metadata),
|
|
1135
|
+
revokedAt: optionalUnixMillis(row.revokedAt),
|
|
1136
|
+
});
|
|
1137
|
+
const decodeRoleGrant = (tables, row) => {
|
|
1138
|
+
const decoded = tables.roleGrant.decode === undefined
|
|
1139
|
+
? decodeDefaultRoleGrant(row)
|
|
1140
|
+
: tables.roleGrant.decode(row);
|
|
1141
|
+
const { scope: _scope, ...withoutScope } = decoded;
|
|
1142
|
+
const scope = canonicalPermissionScope(decoded.scope);
|
|
1143
|
+
return {
|
|
1144
|
+
...withoutScope,
|
|
1145
|
+
...(scope === undefined ? {} : { scope }),
|
|
1146
|
+
};
|
|
1147
|
+
};
|
|
1148
|
+
const decodeDefaultRolePermission = (row) => omitUndefined({
|
|
1149
|
+
role: RoleId(row.roleId),
|
|
1150
|
+
permission: PermissionId(row.permissionId),
|
|
1151
|
+
scopeType: decodeBoolean(row.scopeTypePresent) ? row.scopeType : undefined,
|
|
1152
|
+
});
|
|
1153
|
+
const decodeRolePermission = (tables, row) => tables.rolePermission.decode === undefined
|
|
1154
|
+
? decodeDefaultRolePermission(row)
|
|
1155
|
+
: tables.rolePermission.decode(row);
|
|
843
1156
|
const providerTokenVaultTokens = (record) => omitUndefined({
|
|
844
1157
|
accessTokenCiphertext: record.accessTokenCiphertext,
|
|
845
1158
|
refreshTokenCiphertext: record.refreshTokenCiphertext,
|
|
@@ -850,13 +1163,50 @@ const providerTokenVaultTokens = (record) => omitUndefined({
|
|
|
850
1163
|
});
|
|
851
1164
|
const userInsert = (row) => ({
|
|
852
1165
|
id: row.id,
|
|
853
|
-
email: row.email,
|
|
854
|
-
email_verified: row.emailVerified ? 1 : 0,
|
|
855
1166
|
created_at: Number(row.createdAt),
|
|
856
1167
|
updated_at: Number(row.updatedAt),
|
|
857
1168
|
disabled_at: row.disabledAt === undefined ? null : Number(row.disabledAt),
|
|
858
1169
|
metadata: jsonEncode(row.metadata),
|
|
859
1170
|
});
|
|
1171
|
+
const identityScopeId = (scope) => scope.type === "global" ? "global" : scope.tenantId;
|
|
1172
|
+
const userIdentityInsert = (row) => ({
|
|
1173
|
+
id: row.id,
|
|
1174
|
+
user_id: row.userId,
|
|
1175
|
+
scope_type: row.scope.type,
|
|
1176
|
+
scope_id: identityScopeId(row.scope),
|
|
1177
|
+
kind: row.kind,
|
|
1178
|
+
value: row.value,
|
|
1179
|
+
normalized_value: row.normalizedValue,
|
|
1180
|
+
verified_at: row.verifiedAt === undefined ? null : Number(row.verifiedAt),
|
|
1181
|
+
is_primary_login: row.isPrimaryLogin === true ? 1 : 0,
|
|
1182
|
+
created_at: Number(row.createdAt),
|
|
1183
|
+
updated_at: Number(row.createdAt),
|
|
1184
|
+
revoked_at: null,
|
|
1185
|
+
replaced_by_id: null,
|
|
1186
|
+
metadata: jsonEncode(row.metadata),
|
|
1187
|
+
});
|
|
1188
|
+
const userIdentityVerify = (input) => ({
|
|
1189
|
+
verified_at: Number(input.verifiedAt),
|
|
1190
|
+
updated_at: Number(input.verifiedAt),
|
|
1191
|
+
});
|
|
1192
|
+
const userIdentitySetPrimary = (input, primary) => ({
|
|
1193
|
+
is_primary_login: primary ? 1 : 0,
|
|
1194
|
+
updated_at: Number(input.updatedAt),
|
|
1195
|
+
});
|
|
1196
|
+
const userIdentityReplace = (input) => ({
|
|
1197
|
+
is_primary_login: 0,
|
|
1198
|
+
revoked_at: Number(input.replacedAt),
|
|
1199
|
+
replaced_by_id: input.replacement.id,
|
|
1200
|
+
updated_at: Number(input.replacedAt),
|
|
1201
|
+
});
|
|
1202
|
+
const userIdentityRevoke = (input, _row, metadata) => ({
|
|
1203
|
+
is_primary_login: 0,
|
|
1204
|
+
revoked_at: Number(input.revokedAt),
|
|
1205
|
+
updated_at: Number(input.revokedAt),
|
|
1206
|
+
metadata: input.reason === undefined
|
|
1207
|
+
? metadata
|
|
1208
|
+
: Query.call("json_set", Query.coalesce(metadata, "{}"), "$.revokeReason", input.reason),
|
|
1209
|
+
});
|
|
860
1210
|
const passwordCredentialInsert = (row) => ({
|
|
861
1211
|
id: row.id,
|
|
862
1212
|
user_id: row.userId,
|
|
@@ -871,21 +1221,40 @@ const passwordCredentialUpdatePassword = (input) => ({
|
|
|
871
1221
|
password_hash: input.passwordHash,
|
|
872
1222
|
updated_at: Number(input.updatedAt),
|
|
873
1223
|
});
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
}
|
|
1224
|
+
const assertSessionAssurance = (row) => {
|
|
1225
|
+
const authenticationEvents = parseAuthenticationEvents(row.authenticationEvents);
|
|
1226
|
+
// Sessions owns registry-aware validation for custom evidence.
|
|
1227
|
+
if (authenticationEvents.some((event) => event.type === "custom")) {
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
const assurance = deriveAssurance(authenticationEvents);
|
|
1231
|
+
if (assurance.authenticatedAt === undefined ||
|
|
1232
|
+
row.aal !== assurance.level ||
|
|
1233
|
+
JSON.stringify(row.amr) !== JSON.stringify(assurance.amr) ||
|
|
1234
|
+
row.mfaVerifiedAt !== deriveMfaVerifiedAt(authenticationEvents) ||
|
|
1235
|
+
Number(row.authTime) !== Number(assurance.authenticatedAt)) {
|
|
1236
|
+
throw new Error("Session assurance summary does not match authentication events");
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1239
|
+
const sessionInsert = (row) => {
|
|
1240
|
+
assertSessionAssurance(row);
|
|
1241
|
+
return {
|
|
1242
|
+
id: row.id,
|
|
1243
|
+
user_id: row.userId,
|
|
1244
|
+
secret_hash: row.secretHash,
|
|
1245
|
+
created_at: Number(row.createdAt),
|
|
1246
|
+
expires_at: Number(row.expiresAt),
|
|
1247
|
+
auth_time: Number(row.authTime),
|
|
1248
|
+
authentication_events: JSON.stringify(row.authenticationEvents),
|
|
1249
|
+
aal: row.aal,
|
|
1250
|
+
amr: JSON.stringify(row.amr),
|
|
1251
|
+
mfa_verified_at: row.mfaVerifiedAt === undefined ? null : Number(row.mfaVerifiedAt),
|
|
1252
|
+
metadata: encodeSessionMetadata(row),
|
|
1253
|
+
revoked_at: null,
|
|
1254
|
+
last_seen_at: null,
|
|
1255
|
+
rotated_at: null,
|
|
1256
|
+
};
|
|
1257
|
+
};
|
|
889
1258
|
const verificationInsert = (row) => ({
|
|
890
1259
|
id: row.id,
|
|
891
1260
|
type: row.type,
|
|
@@ -946,10 +1315,6 @@ const loginRiskHistoryInsert = (row) => ({
|
|
|
946
1315
|
risk_level: row.riskLevel ?? null,
|
|
947
1316
|
created_at: Number(row.createdAt),
|
|
948
1317
|
});
|
|
949
|
-
const userMarkEmailVerifiedUpdate = (input) => ({
|
|
950
|
-
email_verified: 1,
|
|
951
|
-
updated_at: Number(input.updatedAt),
|
|
952
|
-
});
|
|
953
1318
|
const sessionRefreshUpdate = (input) => omitUndefined({
|
|
954
1319
|
expires_at: Number(input.expiresAt),
|
|
955
1320
|
last_seen_at: input.lastSeenAt === undefined ? undefined : Number(input.lastSeenAt),
|
|
@@ -960,14 +1325,28 @@ const sessionRotateUpdate = (input) => omitUndefined({
|
|
|
960
1325
|
expires_at: input.expiresAt === undefined ? undefined : Number(input.expiresAt),
|
|
961
1326
|
});
|
|
962
1327
|
const sessionUpdateClaimsUpdate = (row, input) => omitUndefined({
|
|
963
|
-
aal: input.aal,
|
|
964
|
-
amr: input.amr === undefined ? undefined : JSON.stringify(input.amr),
|
|
965
|
-
mfa_verified_at: input.mfaVerifiedAt === undefined ? undefined : Number(input.mfaVerifiedAt),
|
|
966
1328
|
metadata: encodeSessionMetadata({
|
|
967
1329
|
...row,
|
|
968
1330
|
claims: input.claims ?? row.claims,
|
|
969
1331
|
}),
|
|
970
1332
|
});
|
|
1333
|
+
const sessionAppendEvidenceUpdate = (input) => {
|
|
1334
|
+
assertSessionAssurance(input);
|
|
1335
|
+
return {
|
|
1336
|
+
authentication_events: JSON.stringify(input.authenticationEvents),
|
|
1337
|
+
auth_time: Number(input.authTime),
|
|
1338
|
+
aal: input.aal,
|
|
1339
|
+
amr: JSON.stringify(input.amr),
|
|
1340
|
+
mfa_verified_at: input.mfaVerifiedAt === undefined ? null : Number(input.mfaVerifiedAt),
|
|
1341
|
+
};
|
|
1342
|
+
};
|
|
1343
|
+
const sessionAssureAndRotateUpdate = (row, input) => ({
|
|
1344
|
+
...sessionAppendEvidenceUpdate(input),
|
|
1345
|
+
...sessionRotateUpdate(input),
|
|
1346
|
+
...(input.claims === undefined
|
|
1347
|
+
? {}
|
|
1348
|
+
: { metadata: encodeSessionMetadata({ ...row, claims: input.claims }) }),
|
|
1349
|
+
});
|
|
971
1350
|
const sessionRevokeUpdate = (input) => ({
|
|
972
1351
|
revoked_at: Number(input.revokedAt),
|
|
973
1352
|
});
|
|
@@ -1032,6 +1411,7 @@ const totpFactorInsert = (row) => ({
|
|
|
1032
1411
|
created_at: Number(row.createdAt),
|
|
1033
1412
|
confirmed_at: row.confirmedAt === undefined ? null : Number(row.confirmedAt),
|
|
1034
1413
|
last_used_at: row.lastUsedAt === undefined ? null : Number(row.lastUsedAt),
|
|
1414
|
+
last_accepted_counter: row.lastAcceptedCounter ?? null,
|
|
1035
1415
|
revoked_at: row.revokedAt === undefined ? null : Number(row.revokedAt),
|
|
1036
1416
|
metadata: jsonEncode(row.metadata),
|
|
1037
1417
|
});
|
|
@@ -1039,8 +1419,9 @@ const totpFactorConfirm = (input) => omitUndefined({
|
|
|
1039
1419
|
confirmed_at: Number(input.confirmedAt),
|
|
1040
1420
|
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
1041
1421
|
});
|
|
1042
|
-
const
|
|
1422
|
+
const totpFactorAcceptCounter = (input) => omitUndefined({
|
|
1043
1423
|
last_used_at: Number(input.lastUsedAt),
|
|
1424
|
+
last_accepted_counter: input.counter,
|
|
1044
1425
|
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
1045
1426
|
});
|
|
1046
1427
|
const totpFactorRevoke = (input, row) => ({
|
|
@@ -1060,6 +1441,14 @@ const recoveryCodeMarkUsed = (input) => omitUndefined({
|
|
|
1060
1441
|
used_at: Number(input.usedAt),
|
|
1061
1442
|
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
1062
1443
|
});
|
|
1444
|
+
const recoveryCodeReplaceActive = (input, metadata) => ({
|
|
1445
|
+
revoked_at: Number(input.revokedAt),
|
|
1446
|
+
...(input.revokeReason === undefined
|
|
1447
|
+
? {}
|
|
1448
|
+
: {
|
|
1449
|
+
metadata: Query.call("json_set", Query.coalesce(metadata, "{}"), "$.revokeReason", input.revokeReason),
|
|
1450
|
+
}),
|
|
1451
|
+
});
|
|
1063
1452
|
const recoveryCodeRevoke = (input, row) => ({
|
|
1064
1453
|
revoked_at: Number(input.revokedAt),
|
|
1065
1454
|
metadata: jsonEncode(metadataWithReason(row.metadata, "revokeReason", input.reason)),
|
|
@@ -1145,7 +1534,9 @@ const oauthProviderTokenVaultValues = (row) => ({
|
|
|
1145
1534
|
refresh_token_ciphertext: row.refreshTokenCiphertext === undefined
|
|
1146
1535
|
? null
|
|
1147
1536
|
: Redacted.value(row.refreshTokenCiphertext),
|
|
1148
|
-
id_token_ciphertext: row.idTokenCiphertext === undefined
|
|
1537
|
+
id_token_ciphertext: row.idTokenCiphertext === undefined
|
|
1538
|
+
? null
|
|
1539
|
+
: Redacted.value(row.idTokenCiphertext),
|
|
1149
1540
|
token_type: row.tokenType,
|
|
1150
1541
|
scopes: jsonEncode(row.scopes),
|
|
1151
1542
|
expires_at: row.expiresAt === undefined ? null : Number(row.expiresAt),
|
|
@@ -1201,6 +1592,37 @@ const oauthAuthorizationCodeValues = (record) => ({
|
|
|
1201
1592
|
const oauthAuthorizationCodeMarkConsumed = (input) => ({
|
|
1202
1593
|
consumed_at: Number(input.consumedAt),
|
|
1203
1594
|
});
|
|
1595
|
+
const oauthDeviceAuthorizationValues = (record) => ({
|
|
1596
|
+
id: record.id,
|
|
1597
|
+
device_code_hash: record.deviceCodeHash,
|
|
1598
|
+
user_code_hash: record.userCodeHash,
|
|
1599
|
+
client_id: record.clientId,
|
|
1600
|
+
requested_scopes: JSON.stringify(record.requestedScopes),
|
|
1601
|
+
granted_scopes: null,
|
|
1602
|
+
subject: null,
|
|
1603
|
+
status: "pending",
|
|
1604
|
+
issued_at: Number(record.issuedAt),
|
|
1605
|
+
expires_at: Number(record.expiresAt),
|
|
1606
|
+
poll_interval_seconds: record.pollIntervalSeconds,
|
|
1607
|
+
next_poll_at: Number(record.nextPollAt),
|
|
1608
|
+
last_polled_at: record.lastPolledAt === undefined ? null : Number(record.lastPolledAt),
|
|
1609
|
+
approved_at: null,
|
|
1610
|
+
denied_at: null,
|
|
1611
|
+
consumed_at: null,
|
|
1612
|
+
metadata: jsonEncode(record.metadata),
|
|
1613
|
+
});
|
|
1614
|
+
const oauthDeviceAuthorizationApprove = (input) => omitUndefined({
|
|
1615
|
+
status: "approved",
|
|
1616
|
+
subject: input.subject,
|
|
1617
|
+
granted_scopes: JSON.stringify(input.grantedScopes),
|
|
1618
|
+
approved_at: Number(input.decidedAt),
|
|
1619
|
+
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
1620
|
+
});
|
|
1621
|
+
const oauthDeviceAuthorizationDeny = (input) => omitUndefined({
|
|
1622
|
+
status: "denied",
|
|
1623
|
+
denied_at: Number(input.decidedAt),
|
|
1624
|
+
metadata: input.metadata === undefined ? undefined : jsonEncode(input.metadata),
|
|
1625
|
+
});
|
|
1204
1626
|
const oauthClientSecretValues = (record) => ({
|
|
1205
1627
|
prefix: record.prefix,
|
|
1206
1628
|
client_id: record.clientId,
|
|
@@ -1321,6 +1743,101 @@ const webhookReplayUpdate = (input) => ({
|
|
|
1321
1743
|
expires_at: Number(input.expiresAt),
|
|
1322
1744
|
created_at: Number(input.now),
|
|
1323
1745
|
});
|
|
1746
|
+
const permissionScopeValues = (scope) => ({
|
|
1747
|
+
scope_type: scope?.type ?? "global",
|
|
1748
|
+
scope_id_present: scope?.id === undefined ? 0 : 1,
|
|
1749
|
+
scope_id: scope?.id ?? "",
|
|
1750
|
+
});
|
|
1751
|
+
const permissionGrantValues = (input) => ({
|
|
1752
|
+
subject_type: input.subject.type,
|
|
1753
|
+
subject_id: input.subject.id,
|
|
1754
|
+
permission_id: input.permission,
|
|
1755
|
+
...permissionScopeValues(input.scope),
|
|
1756
|
+
expires_at: input.expiresAt === undefined ? null : Number(input.expiresAt),
|
|
1757
|
+
metadata: jsonEncode(input.metadata),
|
|
1758
|
+
revoked_at: null,
|
|
1759
|
+
});
|
|
1760
|
+
const permissionGrantUpdate = (input) => ({
|
|
1761
|
+
expires_at: input.expiresAt === undefined ? null : Number(input.expiresAt),
|
|
1762
|
+
metadata: jsonEncode(input.metadata),
|
|
1763
|
+
revoked_at: null,
|
|
1764
|
+
});
|
|
1765
|
+
const permissionGrantRevoke = (_input, revokedAt) => ({ revoked_at: Number(revokedAt) });
|
|
1766
|
+
const roleGrantValues = (input) => ({
|
|
1767
|
+
subject_type: input.subject.type,
|
|
1768
|
+
subject_id: input.subject.id,
|
|
1769
|
+
role_id: input.role,
|
|
1770
|
+
...permissionScopeValues(input.scope),
|
|
1771
|
+
expires_at: input.expiresAt === undefined ? null : Number(input.expiresAt),
|
|
1772
|
+
metadata: jsonEncode(input.metadata),
|
|
1773
|
+
revoked_at: null,
|
|
1774
|
+
});
|
|
1775
|
+
const roleGrantUpdate = (input) => ({
|
|
1776
|
+
expires_at: input.expiresAt === undefined ? null : Number(input.expiresAt),
|
|
1777
|
+
metadata: jsonEncode(input.metadata),
|
|
1778
|
+
revoked_at: null,
|
|
1779
|
+
});
|
|
1780
|
+
const roleGrantRevoke = (_input, revokedAt) => ({
|
|
1781
|
+
revoked_at: Number(revokedAt),
|
|
1782
|
+
});
|
|
1783
|
+
const rolePermissionValues = (input) => ({
|
|
1784
|
+
role_id: input.role,
|
|
1785
|
+
permission_id: input.permission,
|
|
1786
|
+
scope_type_present: input.scopeType === undefined ? 0 : 1,
|
|
1787
|
+
scope_type: input.scopeType ?? "",
|
|
1788
|
+
});
|
|
1789
|
+
const permissionDefinitionValues = (definition) => ({
|
|
1790
|
+
id: definition.id,
|
|
1791
|
+
description: definition.description ?? null,
|
|
1792
|
+
scope_type_present: definition.scopeType === undefined ? 0 : 1,
|
|
1793
|
+
scope_type: definition.scopeType ?? "",
|
|
1794
|
+
created_at: Number(definition.createdAt),
|
|
1795
|
+
updated_at: Number(definition.updatedAt),
|
|
1796
|
+
disabled_at: definition.disabledAt === undefined ? null : Number(definition.disabledAt),
|
|
1797
|
+
deleted_at: definition.deletedAt === undefined ? null : Number(definition.deletedAt),
|
|
1798
|
+
});
|
|
1799
|
+
const permissionDefinitionUpdate = (input) => ({
|
|
1800
|
+
...(input.description === undefined
|
|
1801
|
+
? {}
|
|
1802
|
+
: { description: input.description }),
|
|
1803
|
+
...(input.scopeType === undefined
|
|
1804
|
+
? {}
|
|
1805
|
+
: {
|
|
1806
|
+
scope_type_present: input.scopeType === null ? 0 : 1,
|
|
1807
|
+
scope_type: input.scopeType ?? "",
|
|
1808
|
+
}),
|
|
1809
|
+
updated_at: Number(input.updatedAt),
|
|
1810
|
+
});
|
|
1811
|
+
const permissionDefinitionSetDisabled = (input) => ({
|
|
1812
|
+
disabled_at: input.disabledAt === undefined ? null : Number(input.disabledAt),
|
|
1813
|
+
updated_at: Number(input.updatedAt),
|
|
1814
|
+
});
|
|
1815
|
+
const permissionDefinitionDelete = (input) => ({
|
|
1816
|
+
deleted_at: Number(input.deletedAt),
|
|
1817
|
+
updated_at: Number(input.updatedAt),
|
|
1818
|
+
});
|
|
1819
|
+
const roleDefinitionValues = (definition) => ({
|
|
1820
|
+
id: definition.id,
|
|
1821
|
+
description: definition.description ?? null,
|
|
1822
|
+
created_at: Number(definition.createdAt),
|
|
1823
|
+
updated_at: Number(definition.updatedAt),
|
|
1824
|
+
disabled_at: definition.disabledAt === undefined ? null : Number(definition.disabledAt),
|
|
1825
|
+
deleted_at: definition.deletedAt === undefined ? null : Number(definition.deletedAt),
|
|
1826
|
+
});
|
|
1827
|
+
const roleDefinitionUpdate = (input) => ({
|
|
1828
|
+
...(input.description === undefined
|
|
1829
|
+
? {}
|
|
1830
|
+
: { description: input.description }),
|
|
1831
|
+
updated_at: Number(input.updatedAt),
|
|
1832
|
+
});
|
|
1833
|
+
const roleDefinitionSetDisabled = (input) => ({
|
|
1834
|
+
disabled_at: input.disabledAt === undefined ? null : Number(input.disabledAt),
|
|
1835
|
+
updated_at: Number(input.updatedAt),
|
|
1836
|
+
});
|
|
1837
|
+
const roleDefinitionDelete = (input) => ({
|
|
1838
|
+
deleted_at: Number(input.deletedAt),
|
|
1839
|
+
updated_at: Number(input.updatedAt),
|
|
1840
|
+
});
|
|
1324
1841
|
const jwtRevocationUpdate = (record) => ({
|
|
1325
1842
|
revoked_at: Number(record.revokedAt),
|
|
1326
1843
|
expires_at: record.expiresAt === undefined ? null : Number(record.expiresAt),
|
|
@@ -1329,6 +1846,7 @@ const jwtRevocationUpdate = (record) => ({
|
|
|
1329
1846
|
const authSqliteTableName = (name, defaultName) => name ?? defaultName;
|
|
1330
1847
|
const makeInternalAuthSqliteTables = (names = {}) => {
|
|
1331
1848
|
const user = makeAuthUserTable(names.user ?? "auth_user");
|
|
1849
|
+
const identity = makeAuthUserIdentityTable(names.identity ?? "auth_user_identity");
|
|
1332
1850
|
const credential = makeAuthCredentialTable(names.credential ?? "auth_credential");
|
|
1333
1851
|
const session = makeAuthSessionTable(names.session ?? "auth_session");
|
|
1334
1852
|
const verification = makeAuthVerificationTable(names.verification ?? "auth_verification");
|
|
@@ -1346,6 +1864,7 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1346
1864
|
const oauthClient = makeAuthOAuthClientTable(authSqliteTableName(names.oauthClient, "auth_oauth_client"));
|
|
1347
1865
|
const oauthConsent = makeAuthOAuthConsentTable(authSqliteTableName(names.oauthConsent, "auth_oauth_consent"));
|
|
1348
1866
|
const oauthAuthorizationCode = makeAuthOAuthAuthorizationCodeTable(authSqliteTableName(names.oauthAuthorizationCode, "auth_oauth_authorization_code"));
|
|
1867
|
+
const oauthDeviceAuthorization = makeAuthOAuthDeviceAuthorizationTable(authSqliteTableName(names.oauthDeviceAuthorization, "auth_oauth_device_authorization"));
|
|
1349
1868
|
const oauthClientSecret = makeAuthOAuthClientSecretTable(authSqliteTableName(names.oauthClientSecret, "auth_oauth_client_secret"));
|
|
1350
1869
|
const oauthProviderModeToken = makeAuthOAuthProviderModeTokenTable(authSqliteTableName(names.oauthProviderModeToken, "auth_oauth_provider_mode_token"));
|
|
1351
1870
|
const oauthProviderTokenVault = makeAuthOAuthProviderTokenVaultTable(names.oauthProviderTokenVault ?? "auth_oauth_provider_token_vault");
|
|
@@ -1353,20 +1872,46 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1353
1872
|
const securityTimeline = makeAuthSecurityTimelineTable(names.securityTimeline ?? "auth_security_timeline");
|
|
1354
1873
|
const webhookOutbox = makeAuthWebhookOutboxTable(names.webhookOutbox ?? "auth_webhook_outbox");
|
|
1355
1874
|
const webhookReplay = makeAuthWebhookReplayTable(names.webhookReplay ?? "auth_webhook_replay");
|
|
1875
|
+
const permissionDefinition = makeAuthPermissionDefinitionTable(names.permissionDefinition ?? "auth_permission_definition");
|
|
1876
|
+
const roleDefinition = makeAuthRoleDefinitionTable(names.roleDefinition ?? "auth_role_definition");
|
|
1877
|
+
const permissionGrant = makeAuthPermissionGrantTable(names.permissionGrant ?? "auth_permission_grant");
|
|
1878
|
+
const roleGrant = makeAuthRoleGrantTable(names.roleGrant ?? "auth_role_grant");
|
|
1879
|
+
const rolePermission = makeAuthRolePermissionTable(names.rolePermission ?? "auth_role_permission");
|
|
1356
1880
|
return {
|
|
1357
1881
|
user: {
|
|
1358
1882
|
table: user,
|
|
1359
1883
|
select: {
|
|
1360
1884
|
id: user.id,
|
|
1361
|
-
email: user.email,
|
|
1362
|
-
emailVerified: user.email_verified,
|
|
1363
1885
|
createdAt: user.created_at,
|
|
1364
1886
|
updatedAt: user.updated_at,
|
|
1365
1887
|
disabledAt: user.disabled_at,
|
|
1366
1888
|
metadata: user.metadata,
|
|
1367
1889
|
},
|
|
1368
1890
|
insert: userInsert,
|
|
1369
|
-
|
|
1891
|
+
},
|
|
1892
|
+
identity: {
|
|
1893
|
+
table: identity,
|
|
1894
|
+
select: {
|
|
1895
|
+
id: identity.id,
|
|
1896
|
+
userId: identity.user_id,
|
|
1897
|
+
scopeType: identity.scope_type,
|
|
1898
|
+
scopeId: identity.scope_id,
|
|
1899
|
+
kind: identity.kind,
|
|
1900
|
+
value: identity.value,
|
|
1901
|
+
normalizedValue: identity.normalized_value,
|
|
1902
|
+
verifiedAt: identity.verified_at,
|
|
1903
|
+
isPrimaryLogin: identity.is_primary_login,
|
|
1904
|
+
createdAt: identity.created_at,
|
|
1905
|
+
updatedAt: identity.updated_at,
|
|
1906
|
+
revokedAt: identity.revoked_at,
|
|
1907
|
+
replacedById: identity.replaced_by_id,
|
|
1908
|
+
metadata: identity.metadata,
|
|
1909
|
+
},
|
|
1910
|
+
insert: userIdentityInsert,
|
|
1911
|
+
verify: userIdentityVerify,
|
|
1912
|
+
setPrimary: userIdentitySetPrimary,
|
|
1913
|
+
replace: userIdentityReplace,
|
|
1914
|
+
revoke: userIdentityRevoke,
|
|
1370
1915
|
},
|
|
1371
1916
|
credential: {
|
|
1372
1917
|
table: credential,
|
|
@@ -1392,6 +1937,7 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1392
1937
|
createdAt: session.created_at,
|
|
1393
1938
|
expiresAt: session.expires_at,
|
|
1394
1939
|
authTime: session.auth_time,
|
|
1940
|
+
authenticationEvents: session.authentication_events,
|
|
1395
1941
|
aal: session.aal,
|
|
1396
1942
|
amr: session.amr,
|
|
1397
1943
|
mfaVerifiedAt: session.mfa_verified_at,
|
|
@@ -1404,6 +1950,8 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1404
1950
|
refresh: sessionRefreshUpdate,
|
|
1405
1951
|
rotate: sessionRotateUpdate,
|
|
1406
1952
|
updateClaims: sessionUpdateClaimsUpdate,
|
|
1953
|
+
appendEvidence: sessionAppendEvidenceUpdate,
|
|
1954
|
+
assureAndRotate: sessionAssureAndRotateUpdate,
|
|
1407
1955
|
revoke: sessionRevokeUpdate,
|
|
1408
1956
|
},
|
|
1409
1957
|
verification: {
|
|
@@ -1424,6 +1972,7 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1424
1972
|
auditLog: {
|
|
1425
1973
|
table: auditLog,
|
|
1426
1974
|
select: {
|
|
1975
|
+
storageOrder: auditLog.storage_id,
|
|
1427
1976
|
id: auditLog.id,
|
|
1428
1977
|
type: auditLog.type,
|
|
1429
1978
|
userId: auditLog.user_id,
|
|
@@ -1526,12 +2075,13 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1526
2075
|
createdAt: totpFactor.created_at,
|
|
1527
2076
|
confirmedAt: totpFactor.confirmed_at,
|
|
1528
2077
|
lastUsedAt: totpFactor.last_used_at,
|
|
2078
|
+
lastAcceptedCounter: totpFactor.last_accepted_counter,
|
|
1529
2079
|
revokedAt: totpFactor.revoked_at,
|
|
1530
2080
|
metadata: totpFactor.metadata,
|
|
1531
2081
|
},
|
|
1532
2082
|
insert: totpFactorInsert,
|
|
1533
2083
|
confirm: totpFactorConfirm,
|
|
1534
|
-
|
|
2084
|
+
acceptCounter: totpFactorAcceptCounter,
|
|
1535
2085
|
revoke: totpFactorRevoke,
|
|
1536
2086
|
},
|
|
1537
2087
|
recoveryCode: {
|
|
@@ -1547,6 +2097,7 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1547
2097
|
},
|
|
1548
2098
|
insert: recoveryCodeInsert,
|
|
1549
2099
|
markUsed: recoveryCodeMarkUsed,
|
|
2100
|
+
replaceActive: recoveryCodeReplaceActive,
|
|
1550
2101
|
revoke: recoveryCodeRevoke,
|
|
1551
2102
|
},
|
|
1552
2103
|
apiKey: {
|
|
@@ -1670,6 +2221,31 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1670
2221
|
update: oauthAuthorizationCodeValues,
|
|
1671
2222
|
markConsumed: oauthAuthorizationCodeMarkConsumed,
|
|
1672
2223
|
},
|
|
2224
|
+
oauthDeviceAuthorization: {
|
|
2225
|
+
table: oauthDeviceAuthorization,
|
|
2226
|
+
select: {
|
|
2227
|
+
id: oauthDeviceAuthorization.id,
|
|
2228
|
+
deviceCodeHash: oauthDeviceAuthorization.device_code_hash,
|
|
2229
|
+
userCodeHash: oauthDeviceAuthorization.user_code_hash,
|
|
2230
|
+
clientId: oauthDeviceAuthorization.client_id,
|
|
2231
|
+
requestedScopes: oauthDeviceAuthorization.requested_scopes,
|
|
2232
|
+
grantedScopes: oauthDeviceAuthorization.granted_scopes,
|
|
2233
|
+
subject: oauthDeviceAuthorization.subject,
|
|
2234
|
+
status: oauthDeviceAuthorization.status,
|
|
2235
|
+
issuedAt: oauthDeviceAuthorization.issued_at,
|
|
2236
|
+
expiresAt: oauthDeviceAuthorization.expires_at,
|
|
2237
|
+
pollIntervalSeconds: oauthDeviceAuthorization.poll_interval_seconds,
|
|
2238
|
+
nextPollAt: oauthDeviceAuthorization.next_poll_at,
|
|
2239
|
+
lastPolledAt: oauthDeviceAuthorization.last_polled_at,
|
|
2240
|
+
approvedAt: oauthDeviceAuthorization.approved_at,
|
|
2241
|
+
deniedAt: oauthDeviceAuthorization.denied_at,
|
|
2242
|
+
consumedAt: oauthDeviceAuthorization.consumed_at,
|
|
2243
|
+
metadata: oauthDeviceAuthorization.metadata,
|
|
2244
|
+
},
|
|
2245
|
+
insert: oauthDeviceAuthorizationValues,
|
|
2246
|
+
approve: oauthDeviceAuthorizationApprove,
|
|
2247
|
+
deny: oauthDeviceAuthorizationDeny,
|
|
2248
|
+
},
|
|
1673
2249
|
oauthClientSecret: {
|
|
1674
2250
|
table: oauthClientSecret,
|
|
1675
2251
|
select: {
|
|
@@ -1799,6 +2375,84 @@ const makeInternalAuthSqliteTables = (names = {}) => {
|
|
|
1799
2375
|
insert: webhookReplayInsert,
|
|
1800
2376
|
update: webhookReplayUpdate,
|
|
1801
2377
|
},
|
|
2378
|
+
permissionDefinition: {
|
|
2379
|
+
table: permissionDefinition,
|
|
2380
|
+
conflictTarget: "id",
|
|
2381
|
+
select: {
|
|
2382
|
+
id: permissionDefinition.id,
|
|
2383
|
+
description: permissionDefinition.description,
|
|
2384
|
+
scopeTypePresent: permissionDefinition.scope_type_present,
|
|
2385
|
+
scopeType: permissionDefinition.scope_type,
|
|
2386
|
+
createdAt: permissionDefinition.created_at,
|
|
2387
|
+
updatedAt: permissionDefinition.updated_at,
|
|
2388
|
+
disabledAt: permissionDefinition.disabled_at,
|
|
2389
|
+
deletedAt: permissionDefinition.deleted_at,
|
|
2390
|
+
},
|
|
2391
|
+
insert: permissionDefinitionValues,
|
|
2392
|
+
update: permissionDefinitionUpdate,
|
|
2393
|
+
setDisabled: permissionDefinitionSetDisabled,
|
|
2394
|
+
delete: permissionDefinitionDelete,
|
|
2395
|
+
},
|
|
2396
|
+
roleDefinition: {
|
|
2397
|
+
table: roleDefinition,
|
|
2398
|
+
conflictTarget: "id",
|
|
2399
|
+
select: {
|
|
2400
|
+
id: roleDefinition.id,
|
|
2401
|
+
description: roleDefinition.description,
|
|
2402
|
+
createdAt: roleDefinition.created_at,
|
|
2403
|
+
updatedAt: roleDefinition.updated_at,
|
|
2404
|
+
disabledAt: roleDefinition.disabled_at,
|
|
2405
|
+
deletedAt: roleDefinition.deleted_at,
|
|
2406
|
+
},
|
|
2407
|
+
insert: roleDefinitionValues,
|
|
2408
|
+
update: roleDefinitionUpdate,
|
|
2409
|
+
setDisabled: roleDefinitionSetDisabled,
|
|
2410
|
+
delete: roleDefinitionDelete,
|
|
2411
|
+
},
|
|
2412
|
+
permissionGrant: {
|
|
2413
|
+
table: permissionGrant,
|
|
2414
|
+
select: {
|
|
2415
|
+
subjectType: permissionGrant.subject_type,
|
|
2416
|
+
subjectId: permissionGrant.subject_id,
|
|
2417
|
+
permissionId: permissionGrant.permission_id,
|
|
2418
|
+
scopeType: permissionGrant.scope_type,
|
|
2419
|
+
scopeIdPresent: permissionGrant.scope_id_present,
|
|
2420
|
+
scopeId: permissionGrant.scope_id,
|
|
2421
|
+
expiresAt: permissionGrant.expires_at,
|
|
2422
|
+
metadata: permissionGrant.metadata,
|
|
2423
|
+
revokedAt: permissionGrant.revoked_at,
|
|
2424
|
+
},
|
|
2425
|
+
insert: permissionGrantValues,
|
|
2426
|
+
update: permissionGrantUpdate,
|
|
2427
|
+
revoke: permissionGrantRevoke,
|
|
2428
|
+
},
|
|
2429
|
+
roleGrant: {
|
|
2430
|
+
table: roleGrant,
|
|
2431
|
+
select: {
|
|
2432
|
+
subjectType: roleGrant.subject_type,
|
|
2433
|
+
subjectId: roleGrant.subject_id,
|
|
2434
|
+
roleId: roleGrant.role_id,
|
|
2435
|
+
scopeType: roleGrant.scope_type,
|
|
2436
|
+
scopeIdPresent: roleGrant.scope_id_present,
|
|
2437
|
+
scopeId: roleGrant.scope_id,
|
|
2438
|
+
expiresAt: roleGrant.expires_at,
|
|
2439
|
+
metadata: roleGrant.metadata,
|
|
2440
|
+
revokedAt: roleGrant.revoked_at,
|
|
2441
|
+
},
|
|
2442
|
+
insert: roleGrantValues,
|
|
2443
|
+
update: roleGrantUpdate,
|
|
2444
|
+
revoke: roleGrantRevoke,
|
|
2445
|
+
},
|
|
2446
|
+
rolePermission: {
|
|
2447
|
+
table: rolePermission,
|
|
2448
|
+
select: {
|
|
2449
|
+
roleId: rolePermission.role_id,
|
|
2450
|
+
permissionId: rolePermission.permission_id,
|
|
2451
|
+
scopeTypePresent: rolePermission.scope_type_present,
|
|
2452
|
+
scopeType: rolePermission.scope_type,
|
|
2453
|
+
},
|
|
2454
|
+
insert: rolePermissionValues,
|
|
2455
|
+
},
|
|
1802
2456
|
};
|
|
1803
2457
|
};
|
|
1804
2458
|
const asAuthSqliteTables = (tables) => tables;
|
|
@@ -1806,6 +2460,7 @@ export const defineEffectQbAuthTables = (tables) => asAuthSqliteTables(tables);
|
|
|
1806
2460
|
export const makeAuthSqliteTables = (names = {}) => asAuthSqliteTables(makeInternalAuthSqliteTables(names));
|
|
1807
2461
|
export const authSqliteTables = makeAuthSqliteTables();
|
|
1808
2462
|
const selectUserColumns = (tables) => tables.user.select;
|
|
2463
|
+
const selectIdentityColumns = (tables) => tables.identity.select;
|
|
1809
2464
|
const selectCredentialColumns = (tables) => tables.credential.select;
|
|
1810
2465
|
const selectSessionColumns = (tables) => tables.session.select;
|
|
1811
2466
|
const selectVerificationColumns = (tables) => tables.verification.select;
|
|
@@ -1823,6 +2478,8 @@ const selectOAuthAccountColumns = (tables) => tables.oauthAccount.select;
|
|
|
1823
2478
|
const selectOAuthClientColumns = (tables) => tables.oauthClient.select;
|
|
1824
2479
|
const selectOAuthConsentColumns = (tables) => tables.oauthConsent.select;
|
|
1825
2480
|
const selectOAuthAuthorizationCodeColumns = (tables) => tables.oauthAuthorizationCode.select;
|
|
2481
|
+
const oauthDeviceAuthorizationTable = (tables) => tables.oauthDeviceAuthorization ?? authSqliteTables.oauthDeviceAuthorization;
|
|
2482
|
+
const selectOAuthDeviceAuthorizationColumns = (tables) => oauthDeviceAuthorizationTable(tables).select;
|
|
1826
2483
|
const selectOAuthClientSecretColumns = (tables) => tables.oauthClientSecret.select;
|
|
1827
2484
|
const selectOAuthProviderModeTokenColumns = (tables) => tables.oauthProviderModeToken.select;
|
|
1828
2485
|
const selectOAuthProviderTokenVaultColumns = (tables) => tables.oauthProviderTokenVault.select;
|
|
@@ -1830,7 +2487,16 @@ const selectDomainVerificationColumns = (tables) => tables.domainVerification.se
|
|
|
1830
2487
|
const selectSecurityTimelineColumns = (tables) => tables.securityTimeline.select;
|
|
1831
2488
|
const selectWebhookOutboxColumns = (tables) => tables.webhookOutbox.select;
|
|
1832
2489
|
const selectWebhookReplayColumns = (tables) => tables.webhookReplay.select;
|
|
2490
|
+
const selectPermissionDefinitionColumns = (tables) => tables.permissionDefinition.select;
|
|
2491
|
+
const selectRoleDefinitionColumns = (tables) => tables.roleDefinition.select;
|
|
2492
|
+
const selectPermissionGrantColumns = (tables) => tables.permissionGrant.select;
|
|
2493
|
+
const selectRoleGrantColumns = (tables) => tables.roleGrant.select;
|
|
2494
|
+
const selectRolePermissionColumns = (tables) => tables.rolePermission.select;
|
|
1833
2495
|
const storageError = (entity, operation, cause) => StorageError.fromUnknown(entity, operation, cause);
|
|
2496
|
+
const decodeSessionResult = (operation, decode) => Effect.try({
|
|
2497
|
+
try: decode,
|
|
2498
|
+
catch: (cause) => storageError("session", operation, cause),
|
|
2499
|
+
});
|
|
1834
2500
|
const auditLogStoreError = (operation, cause) => new AuditLogStoreError({
|
|
1835
2501
|
operation,
|
|
1836
2502
|
message: `Audit log ${operation} failed`,
|
|
@@ -1900,6 +2566,11 @@ const oauthAuthorizationCodeStoreError = (operation, cause) => new OAuthAuthoriz
|
|
|
1900
2566
|
message: `OAuth authorization code ${operation} failed`,
|
|
1901
2567
|
cause,
|
|
1902
2568
|
});
|
|
2569
|
+
const oauthDeviceAuthorizationStoreError = (operation, cause) => new OAuthDeviceAuthorizationStoreError({
|
|
2570
|
+
operation,
|
|
2571
|
+
message: `OAuth device authorization ${operation} failed`,
|
|
2572
|
+
cause,
|
|
2573
|
+
});
|
|
1903
2574
|
const oauthClientSecretStoreError = (operation, cause) => new OAuthClientSecretStoreError({
|
|
1904
2575
|
operation,
|
|
1905
2576
|
message: `OAuth client secret ${operation} failed`,
|
|
@@ -1935,22 +2606,36 @@ const webhookReplayStoreError = (operation, cause) => new WebhookReplayStoreErro
|
|
|
1935
2606
|
message: `Webhook replay ${operation} failed`,
|
|
1936
2607
|
cause,
|
|
1937
2608
|
});
|
|
2609
|
+
const permissionStoreError = (operation, cause) => new PermissionStoreError({
|
|
2610
|
+
operation,
|
|
2611
|
+
message: `Permission store ${operation} failed`,
|
|
2612
|
+
cause,
|
|
2613
|
+
});
|
|
1938
2614
|
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
|
|
1939
2615
|
? Option.none()
|
|
1940
2616
|
: Option.some(decodeUserRow(tables, rows[0]))));
|
|
1941
|
-
const
|
|
2617
|
+
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
|
|
1942
2618
|
? Option.none()
|
|
1943
|
-
: Option.some(
|
|
2619
|
+
: Option.some(decodeUserIdentityRow(tables, rows[0]))));
|
|
1944
2620
|
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
|
|
1945
2621
|
? Option.none()
|
|
1946
2622
|
: Option.some(decodePasswordCredentialRow(tables, rows[0]))));
|
|
1947
2623
|
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
|
|
1948
2624
|
? Option.none()
|
|
1949
2625
|
: Option.some(decodeJwtRevocationRow(tables, rows[0]))));
|
|
1950
|
-
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.
|
|
2626
|
+
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
|
|
1951
2627
|
? Option.none()
|
|
1952
|
-
: Option.some(decodeSessionRow(tables, rows[0]))));
|
|
1953
|
-
const
|
|
2628
|
+
: Option.some(decodeSessionRow(tables, rows[0])))));
|
|
2629
|
+
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, () => {
|
|
2630
|
+
const [selected] = rows;
|
|
2631
|
+
return selected === undefined
|
|
2632
|
+
? Option.none()
|
|
2633
|
+
: Option.some({
|
|
2634
|
+
row: decodeSessionRow(tables, selected),
|
|
2635
|
+
authenticationEvents: selected.authenticationEvents,
|
|
2636
|
+
});
|
|
2637
|
+
})));
|
|
2638
|
+
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)))));
|
|
1954
2639
|
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
|
|
1955
2640
|
? Option.none()
|
|
1956
2641
|
: Option.some(decodeVerificationRow(tables, rows[0]))));
|
|
@@ -2022,7 +2707,9 @@ const findOAuthClientSecretByClientIdAndPrefix = (executor, tables, input, opera
|
|
|
2022
2707
|
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
|
|
2023
2708
|
? Option.none()
|
|
2024
2709
|
: Option.some(decodeOAuthProviderModeTokenRow(tables, rows[0]))));
|
|
2025
|
-
const deleteOAuthProviderModeTokenRowsBefore = (executor, tables, column, before) => executePlan(executor, Query.delete(tables.oauthProviderModeToken.table).pipe(Query.where(queryLt(column, Number(before))), Query.returning({
|
|
2710
|
+
const deleteOAuthProviderModeTokenRowsBefore = (executor, tables, column, before) => executePlan(executor, Query.delete(tables.oauthProviderModeToken.table).pipe(Query.where(queryLt(column, Number(before))), Query.returning({
|
|
2711
|
+
tokenHash: tables.oauthProviderModeToken.select.tokenHash,
|
|
2712
|
+
}))).pipe(Effect.map((rows) => rows.length));
|
|
2026
2713
|
const deleteOAuthProviderModeRetiredTokens = (executor, tables, input) => Effect.gen(function* () {
|
|
2027
2714
|
const expiredDeleted = input.beforeExpiresAt === undefined
|
|
2028
2715
|
? 0
|
|
@@ -2096,7 +2783,14 @@ const listAuditLogRecords = (executor, tables, input = {}) => {
|
|
|
2096
2783
|
const query = input.types === undefined
|
|
2097
2784
|
? byOccurredAt
|
|
2098
2785
|
: byOccurredAt.pipe(Query.where(queryIn(tables.auditLog.select.type, input.types[0], ...input.types.slice(1))));
|
|
2099
|
-
|
|
2786
|
+
let ordered = query.pipe(Query.orderBy(tables.auditLog.select.occurredAt, "desc"), Query.orderBy(Query.coalesce(tables.auditLog.select.id, Query.literal("")), "desc"));
|
|
2787
|
+
if (tables.auditLog.select.storageOrder !== undefined) {
|
|
2788
|
+
ordered = ordered.pipe(Query.orderBy(tables.auditLog.select.storageOrder, "asc"));
|
|
2789
|
+
}
|
|
2790
|
+
return executePlan(executor, ordered).pipe(Effect.mapError((cause) => auditLogStoreError("list", cause)), Effect.flatMap((rows) => Effect.try({
|
|
2791
|
+
try: () => filterAuditLogEvents(rows.map((row) => decodeAuditLogEvent(tables, row)), input),
|
|
2792
|
+
catch: (cause) => auditLogStoreError("list", cause),
|
|
2793
|
+
})));
|
|
2100
2794
|
};
|
|
2101
2795
|
const deleteAuditLogRecordsBefore = (executor, tables, input) => {
|
|
2102
2796
|
const base = Query.delete(tables.auditLog.table).pipe(Query.where(queryLt(tables.auditLog.select.occurredAt, Number(input.beforeOccurredAt))));
|
|
@@ -2132,7 +2826,9 @@ const distanceKm = (first, second) => {
|
|
|
2132
2826
|
Math.cos(firstLatitude) *
|
|
2133
2827
|
Math.cos(secondLatitude) *
|
|
2134
2828
|
Math.sin(longitudeDelta / 2) ** 2;
|
|
2135
|
-
return (2 *
|
|
2829
|
+
return (2 *
|
|
2830
|
+
earthRadiusKm *
|
|
2831
|
+
Math.atan2(Math.sqrt(haversine), Math.sqrt(1 - haversine)));
|
|
2136
2832
|
};
|
|
2137
2833
|
const hasCoordinates = (value) => value.latitude !== undefined &&
|
|
2138
2834
|
Number.isFinite(value.latitude) &&
|
|
@@ -2215,18 +2911,287 @@ const deleteLoginRiskHistoryRecordsBefore = (executor, tables, input) => {
|
|
|
2215
2911
|
};
|
|
2216
2912
|
export const makeEffectQbSqliteUserStore = (executor, options = {}) => {
|
|
2217
2913
|
const tables = options.tables ?? authSqliteTables;
|
|
2218
|
-
return UserStore.
|
|
2914
|
+
return UserStore.of({
|
|
2219
2915
|
findById: (id) => findUserById(executor, tables, id),
|
|
2220
|
-
findByEmail: (email) => findUserByEmail(executor, tables, email),
|
|
2221
2916
|
insert: (row) => executePlan(executor, queryInsert(tables.user.table, mutationValues(tables.user.insert(row)))).pipe(Effect.mapError((cause) => storageError("user", "insert", cause)), Effect.asVoid),
|
|
2222
|
-
|
|
2917
|
+
});
|
|
2918
|
+
};
|
|
2919
|
+
const identityConflict = (row) => new IdentityConflictError({
|
|
2920
|
+
scope: row.scope,
|
|
2921
|
+
kind: row.kind,
|
|
2922
|
+
normalizedValue: row.normalizedValue,
|
|
2923
|
+
});
|
|
2924
|
+
const sqliteConstraintDiscriminator = (cause) => {
|
|
2925
|
+
const pending = [cause];
|
|
2926
|
+
const seen = new Set();
|
|
2927
|
+
while (pending.length > 0) {
|
|
2928
|
+
const current = pending.pop();
|
|
2929
|
+
if (current === undefined || current === null || seen.has(current)) {
|
|
2930
|
+
continue;
|
|
2931
|
+
}
|
|
2932
|
+
seen.add(current);
|
|
2933
|
+
if (typeof current === "string") {
|
|
2934
|
+
if (current === "UniqueViolation" ||
|
|
2935
|
+
/\bUNIQUE constraint failed\b/i.test(current) ||
|
|
2936
|
+
/\bPRIMARY KEY constraint failed\b/i.test(current)) {
|
|
2937
|
+
return true;
|
|
2938
|
+
}
|
|
2939
|
+
continue;
|
|
2940
|
+
}
|
|
2941
|
+
if (typeof current !== "object") {
|
|
2942
|
+
continue;
|
|
2943
|
+
}
|
|
2944
|
+
const record = current;
|
|
2945
|
+
const { code, errno, message, name, _tag } = record;
|
|
2946
|
+
if (_tag === "UniqueViolation" || name === "UniqueViolation") {
|
|
2947
|
+
return true;
|
|
2948
|
+
}
|
|
2949
|
+
if (code === "SQLITE_CONSTRAINT_UNIQUE" ||
|
|
2950
|
+
code === "SQLITE_CONSTRAINT_PRIMARYKEY") {
|
|
2951
|
+
return true;
|
|
2952
|
+
}
|
|
2953
|
+
if (errno === 2067 || errno === 1555) {
|
|
2954
|
+
return true;
|
|
2955
|
+
}
|
|
2956
|
+
if (typeof message === "string" &&
|
|
2957
|
+
(/\bUNIQUE constraint failed\b/i.test(message) ||
|
|
2958
|
+
/\bPRIMARY KEY constraint failed\b/i.test(message))) {
|
|
2959
|
+
return true;
|
|
2960
|
+
}
|
|
2961
|
+
pending.push(record.cause, record.reason, record.error, record.raw);
|
|
2962
|
+
if (Array.isArray(record.failures)) {
|
|
2963
|
+
pending.push(...record.failures);
|
|
2964
|
+
}
|
|
2965
|
+
if (Array.isArray(record.reasons)) {
|
|
2966
|
+
pending.push(...record.reasons);
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
return false;
|
|
2970
|
+
};
|
|
2971
|
+
const isAtomicExecuteConflict = (cause, statement) => cause instanceof AtomicPlanError &&
|
|
2972
|
+
cause.phase === "execute" &&
|
|
2973
|
+
cause.statement === statement &&
|
|
2974
|
+
sqliteConstraintDiscriminator(cause.cause) === true;
|
|
2975
|
+
export const makeEffectQbSqliteIdentityStore = (executor, atomic, options = {}) => {
|
|
2976
|
+
const tables = options.tables ?? authSqliteTables;
|
|
2977
|
+
const byId = (id, operation = "find") => findIdentity(executor, tables, [[tables.identity.select.id, id]], operation);
|
|
2978
|
+
const insert = (row) => executePlan(executor, queryInsert(tables.identity.table, mutationValues(tables.identity.insert(row)))).pipe(Effect.mapError(() => identityConflict(row)), Effect.asVoid);
|
|
2979
|
+
return IdentityStore.of({
|
|
2980
|
+
findById: byId,
|
|
2981
|
+
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
|
|
2223
2982
|
? Option.none()
|
|
2224
|
-
: Option.some(
|
|
2983
|
+
: Option.some(decodeUserIdentityRow(tables, rows[0])))),
|
|
2984
|
+
listByUser: (input) => {
|
|
2985
|
+
let query = Query.select(selectIdentityColumns(tables)).pipe(Query.from(tables.identity.table), Query.where(queryEq(tables.identity.select.userId, input.userId)));
|
|
2986
|
+
if (input.includeRevoked !== true) {
|
|
2987
|
+
query = query.pipe(Query.where(queryIsNull(tables.identity.select.revokedAt)));
|
|
2988
|
+
}
|
|
2989
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => storageError("identity", "find", cause)), Effect.map((rows) => rows.map((row) => decodeUserIdentityRow(tables, row))));
|
|
2990
|
+
},
|
|
2991
|
+
insert,
|
|
2992
|
+
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]
|
|
2993
|
+
? Option.some(decodeUserIdentityRow(tables, rows[0]))
|
|
2994
|
+
: Option.none())),
|
|
2995
|
+
setPrimaryLogin: (input) => {
|
|
2996
|
+
const candidate = aliasTable(tables.identity.table, "primary_candidate");
|
|
2997
|
+
const candidateColumn = (column) => aliasColumn(candidate, column);
|
|
2998
|
+
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))))));
|
|
2999
|
+
const primaryColumnName = boundColumnName(tables.identity.select.isPrimaryLogin);
|
|
3000
|
+
const values = {
|
|
3001
|
+
...mutationValues(tables.identity.setPrimary(input, false)),
|
|
3002
|
+
[primaryColumnName]: Query.case()
|
|
3003
|
+
.when(queryEq(tables.identity.select.id, input.identityId), targetMutationExpression(tables.identity.table, primaryColumnName, 1))
|
|
3004
|
+
.else(targetMutationExpression(tables.identity.table, primaryColumnName, 0)),
|
|
3005
|
+
};
|
|
3006
|
+
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)));
|
|
3007
|
+
return executeAtomicPlans(atomic, [plan]).pipe(Effect.mapError((cause) => storageError("identity", "update", cause)), Effect.map(([rows]) => {
|
|
3008
|
+
const selected = rows.find((row) => row.id === input.identityId);
|
|
3009
|
+
return selected === undefined
|
|
3010
|
+
? Option.none()
|
|
3011
|
+
: Option.some(decodeUserIdentityRow(tables, selected));
|
|
3012
|
+
}));
|
|
3013
|
+
},
|
|
3014
|
+
replace: (input) => byId(input.id, "update").pipe(Effect.flatMap((found) => {
|
|
3015
|
+
if (Option.isNone(found) ||
|
|
3016
|
+
found.value.userId !== input.userId ||
|
|
3017
|
+
input.replacement.userId !== input.userId) {
|
|
3018
|
+
return Effect.succeed(Option.none());
|
|
3019
|
+
}
|
|
3020
|
+
const row = {
|
|
3021
|
+
...input.replacement,
|
|
3022
|
+
isPrimaryLogin: input.replacement.isPrimaryLogin ?? found.value.isPrimaryLogin,
|
|
3023
|
+
};
|
|
3024
|
+
const active = aliasTable(tables.identity.table, "active_identity");
|
|
3025
|
+
const activeColumn = (column) => aliasColumn(active, column);
|
|
3026
|
+
const usableCount = queryScalar(Query.select({
|
|
3027
|
+
count: Query.count(activeColumn(tables.identity.select.id)),
|
|
3028
|
+
}).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))))));
|
|
3029
|
+
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)));
|
|
3030
|
+
const updatePlan = (isIdentityLoginEligible(found.value) &&
|
|
3031
|
+
!isIdentityLoginEligible(row)
|
|
3032
|
+
? updatePlanBase.pipe(Query.where(queryGt(usableCount, 1)))
|
|
3033
|
+
: updatePlanBase).pipe(Query.returning({
|
|
3034
|
+
...selectIdentityColumns(tables),
|
|
3035
|
+
revokedAt: queryWithoutNullRefinement(tables.identity.select.revokedAt),
|
|
3036
|
+
replacedById: queryWithoutNullRefinement(tables.identity.select.replacedById),
|
|
3037
|
+
}));
|
|
3038
|
+
const insertPlan = queryInsertWhere(tables.identity.table, mutationValues(tables.identity.insert(row)), queryChangedOne()).pipe(Query.returning(selectIdentityColumns(tables)));
|
|
3039
|
+
return executeAtomicPlans(atomic, [
|
|
3040
|
+
updatePlan,
|
|
3041
|
+
insertPlan,
|
|
3042
|
+
]).pipe(Effect.mapError((cause) => isAtomicExecuteConflict(cause, 1)
|
|
3043
|
+
? identityConflict(row)
|
|
3044
|
+
: storageError("identity", "update", cause)), Effect.map(([updated, inserted]) => inserted[0] === undefined || updated[0] === undefined
|
|
3045
|
+
? Option.none()
|
|
3046
|
+
: Option.some({
|
|
3047
|
+
previous: decodeUserIdentityRow(tables, updated[0]),
|
|
3048
|
+
replacement: decodeUserIdentityRow(tables, inserted[0]),
|
|
3049
|
+
})));
|
|
3050
|
+
})),
|
|
3051
|
+
revoke: (input) => byId(input.id, "update").pipe(Effect.flatMap((found) => {
|
|
3052
|
+
if (Option.isNone(found)) {
|
|
3053
|
+
return Effect.succeed(Option.none());
|
|
3054
|
+
}
|
|
3055
|
+
const active = aliasTable(tables.identity.table, "active_identity");
|
|
3056
|
+
const activeColumn = (column) => aliasColumn(active, column);
|
|
3057
|
+
const usableCount = queryScalar(Query.select({
|
|
3058
|
+
count: Query.count(activeColumn(tables.identity.select.id)),
|
|
3059
|
+
}).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))))));
|
|
3060
|
+
const revokeValues = mutationValues(tables.identity.revoke(input, found.value, tables.identity.select.metadata));
|
|
3061
|
+
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({
|
|
3062
|
+
...selectIdentityColumns(tables),
|
|
3063
|
+
revokedAt: queryWithoutNullRefinement(tables.identity.select.revokedAt),
|
|
3064
|
+
metadata: queryWithoutNullRefinement(tables.identity.select.metadata),
|
|
3065
|
+
}));
|
|
3066
|
+
const next = aliasTable(tables.identity.table, "next_identity");
|
|
3067
|
+
const nextColumn = (column) => aliasColumn(next, column);
|
|
3068
|
+
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)));
|
|
3069
|
+
const primary = aliasTable(tables.identity.table, "existing_primary");
|
|
3070
|
+
const primaryColumn = (column) => aliasColumn(primary, column);
|
|
3071
|
+
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))));
|
|
3072
|
+
const promotePlan = queryUpdate(tables.identity.table, mutationValues(tables.identity.setPrimary({
|
|
3073
|
+
userId: input.userId,
|
|
3074
|
+
identityId: input.id,
|
|
3075
|
+
expectedUpdatedAt: input.expectedUpdatedAt,
|
|
3076
|
+
updatedAt: input.revokedAt,
|
|
3077
|
+
}, true))).pipe(Query.where(queryEq(tables.identity.select.id, nextId)), Query.where(queryNot(primaryExists)), Query.where(queryChangedOne()));
|
|
3078
|
+
return executeAtomicPlans(atomic, [
|
|
3079
|
+
revokePlan,
|
|
3080
|
+
promotePlan,
|
|
3081
|
+
]).pipe(Effect.mapError((cause) => storageError("identity", "update", cause)), Effect.map(([rows]) => rows[0] === undefined
|
|
3082
|
+
? Option.none()
|
|
3083
|
+
: Option.some(decodeUserIdentityRow(tables, rows[0]))));
|
|
3084
|
+
})),
|
|
3085
|
+
});
|
|
3086
|
+
};
|
|
3087
|
+
export const makeEffectQbSqliteRegistrationStore = (executor, atomic, options = {}) => {
|
|
3088
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3089
|
+
const identityStore = makeEffectQbSqliteIdentityStore(executor, atomic, options);
|
|
3090
|
+
const credentialStore = makeEffectQbSqliteCredentialStore(executor, options);
|
|
3091
|
+
const classify = (input, cause) => {
|
|
3092
|
+
const failure = () => Effect.fail(new RegistrationFailureError({
|
|
3093
|
+
message: "Atomic registration failed",
|
|
3094
|
+
cause,
|
|
3095
|
+
}));
|
|
3096
|
+
if (cause instanceof AtomicPlanError && cause.phase !== "execute") {
|
|
3097
|
+
return failure();
|
|
3098
|
+
}
|
|
3099
|
+
const statement = cause instanceof AtomicPlanError ? (cause.statement ?? -1) : -1;
|
|
3100
|
+
if (statement >= 0 && !isAtomicExecuteConflict(cause, statement)) {
|
|
3101
|
+
return failure();
|
|
3102
|
+
}
|
|
3103
|
+
if (statement < 0) {
|
|
3104
|
+
return Effect.gen(function* () {
|
|
3105
|
+
if (Option.isSome(yield* findUserById(executor, options.tables ?? authSqliteTables, input.user.id))) {
|
|
3106
|
+
return yield* new RegistrationConflictError({ reason: "user-id" });
|
|
3107
|
+
}
|
|
3108
|
+
if (Option.isSome(yield* identityStore.findById(input.identity.id))) {
|
|
3109
|
+
return yield* new RegistrationConflictError({
|
|
3110
|
+
reason: "identity-id",
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
3113
|
+
if (Option.isSome(yield* identityStore.findByKindAndNormalizedValue({
|
|
3114
|
+
scope: input.identity.scope,
|
|
3115
|
+
kind: input.identity.kind,
|
|
3116
|
+
normalizedValue: input.identity.normalizedValue,
|
|
3117
|
+
}))) {
|
|
3118
|
+
return yield* new RegistrationConflictError({
|
|
3119
|
+
reason: "identity-value",
|
|
3120
|
+
});
|
|
3121
|
+
}
|
|
3122
|
+
if (input.passwordCredential !== undefined) {
|
|
3123
|
+
const credential = yield* credentialStore.findPasswordByUserId(input.user.id);
|
|
3124
|
+
if (Option.isSome(credential)) {
|
|
3125
|
+
return yield* new RegistrationConflictError({
|
|
3126
|
+
reason: credential.value.id === input.passwordCredential.id
|
|
3127
|
+
? "credential-id"
|
|
3128
|
+
: "password-credential",
|
|
3129
|
+
});
|
|
3130
|
+
}
|
|
3131
|
+
}
|
|
3132
|
+
return yield* new RegistrationFailureError({
|
|
3133
|
+
message: "Atomic registration failed",
|
|
3134
|
+
cause,
|
|
3135
|
+
});
|
|
3136
|
+
}).pipe(Effect.catch((error) => error instanceof RegistrationConflictError ||
|
|
3137
|
+
error instanceof RegistrationFailureError
|
|
3138
|
+
? Effect.fail(error)
|
|
3139
|
+
: Effect.fail(new RegistrationFailureError({
|
|
3140
|
+
message: "Registration conflict classification failed",
|
|
3141
|
+
cause: error,
|
|
3142
|
+
}))));
|
|
3143
|
+
}
|
|
3144
|
+
if (statement === 0) {
|
|
3145
|
+
return Effect.fail(new RegistrationConflictError({ reason: "user-id" }));
|
|
3146
|
+
}
|
|
3147
|
+
if (statement === 1) {
|
|
3148
|
+
return identityStore.findById(input.identity.id).pipe(Effect.flatMap((found) => Option.isSome(found)
|
|
3149
|
+
? Effect.fail(new RegistrationConflictError({ reason: "identity-id" }))
|
|
3150
|
+
: Effect.fail(new RegistrationConflictError({ reason: "identity-value" }))), Effect.catch(() => Effect.fail(new RegistrationConflictError({ reason: "identity-value" }))));
|
|
3151
|
+
}
|
|
3152
|
+
if (statement === 2 && input.passwordCredential !== undefined) {
|
|
3153
|
+
return credentialStore.findPasswordByUserId(input.user.id).pipe(Effect.flatMap((found) => Effect.fail(new RegistrationConflictError({
|
|
3154
|
+
reason: Option.isSome(found) &&
|
|
3155
|
+
found.value.id === input.passwordCredential?.id
|
|
3156
|
+
? "credential-id"
|
|
3157
|
+
: "password-credential",
|
|
3158
|
+
}))), Effect.catch(() => Effect.fail(new RegistrationConflictError({ reason: "password-credential" }))));
|
|
3159
|
+
}
|
|
3160
|
+
return failure();
|
|
3161
|
+
};
|
|
3162
|
+
return RegistrationStore.of({
|
|
3163
|
+
create: (input) => {
|
|
3164
|
+
if (input.identity.userId !== input.user.id ||
|
|
3165
|
+
(input.passwordCredential !== undefined &&
|
|
3166
|
+
input.passwordCredential.userId !== input.user.id)) {
|
|
3167
|
+
return Effect.fail(new RegistrationConflictError({ reason: "user-id" }));
|
|
3168
|
+
}
|
|
3169
|
+
const userPlan = queryInsert(tables.user.table, mutationValues(tables.user.insert(input.user)));
|
|
3170
|
+
const identityPlan = queryInsert(tables.identity.table, mutationValues(tables.identity.insert(input.identity)));
|
|
3171
|
+
const plans = input.passwordCredential === undefined
|
|
3172
|
+
? [userPlan, identityPlan]
|
|
3173
|
+
: [
|
|
3174
|
+
userPlan,
|
|
3175
|
+
identityPlan,
|
|
3176
|
+
queryInsert(tables.credential.table, mutationValues(tables.credential.insertPassword(input.passwordCredential))),
|
|
3177
|
+
];
|
|
3178
|
+
return atomic.execute(plans).pipe(Effect.map(() => ({
|
|
3179
|
+
user: input.user,
|
|
3180
|
+
identity: {
|
|
3181
|
+
...input.identity,
|
|
3182
|
+
isPrimaryLogin: input.identity.isPrimaryLogin ?? false,
|
|
3183
|
+
updatedAt: input.identity.createdAt,
|
|
3184
|
+
},
|
|
3185
|
+
...(input.passwordCredential === undefined
|
|
3186
|
+
? {}
|
|
3187
|
+
: { passwordCredential: input.passwordCredential }),
|
|
3188
|
+
})), Effect.catch((error) => classify(input, error)));
|
|
3189
|
+
},
|
|
2225
3190
|
});
|
|
2226
3191
|
};
|
|
2227
3192
|
export const makeEffectQbSqliteCredentialStore = (executor, options = {}) => {
|
|
2228
3193
|
const tables = options.tables ?? authSqliteTables;
|
|
2229
|
-
return CredentialStore.
|
|
3194
|
+
return CredentialStore.of({
|
|
2230
3195
|
findPasswordByUserId: (userId) => findPasswordCredentialByUserId(executor, tables, userId),
|
|
2231
3196
|
insertPassword: (row) => executePlan(executor, queryInsert(tables.credential.table, mutationValues(tables.credential.insertPassword(row)))).pipe(Effect.mapError((cause) => storageError("credential", "insert", cause)), Effect.asVoid),
|
|
2232
3197
|
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
|
|
@@ -2236,29 +3201,44 @@ export const makeEffectQbSqliteCredentialStore = (executor, options = {}) => {
|
|
|
2236
3201
|
};
|
|
2237
3202
|
export const makeEffectQbSqliteSessionStore = (executor, options = {}) => {
|
|
2238
3203
|
const tables = options.tables ?? authSqliteTables;
|
|
2239
|
-
return SessionStore.
|
|
3204
|
+
return SessionStore.of({
|
|
2240
3205
|
findById: (id) => findSessionById(executor, tables, id),
|
|
2241
3206
|
listByUser: (input) => listSessionsByUser(executor, tables, input),
|
|
2242
|
-
insert: (row) => executePlan(executor, queryInsert(tables.session.table, mutationValues(tables.session.insert(row))))
|
|
2243
|
-
refresh: (input) =>
|
|
2244
|
-
|
|
2245
|
-
|
|
3207
|
+
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),
|
|
3208
|
+
refresh: (input) => findSessionForMutation(executor, tables, input.sessionId, "refresh").pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
3209
|
+
? Effect.succeed(Option.none())
|
|
3210
|
+
: 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
|
|
2246
3211
|
? Option.none()
|
|
2247
|
-
: Option.some(decodeSessionRow(tables, rows[0]))))
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
return executePlan(executor, query).pipe(Effect.mapError((cause) => storageError("session", "rotate", cause)), Effect.map((rows) => rows[0] === undefined
|
|
3212
|
+
: Option.some(decodeSessionRow(tables, rows[0]))))))),
|
|
3213
|
+
rotate: (input) => findSessionForMutation(executor, tables, input.sessionId, "rotate").pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
3214
|
+
? Effect.succeed(Option.none())
|
|
3215
|
+
: 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
|
|
2252
3216
|
? Option.none()
|
|
2253
|
-
: Option.some(decodeSessionRow(tables, rows[0]))))
|
|
2254
|
-
|
|
2255
|
-
|
|
3217
|
+
: Option.some(decodeSessionRow(tables, rows[0]))))))),
|
|
3218
|
+
updateClaims: (input) => findSessionForMutation(executor, tables, input.sessionId, "updateClaims").pipe(Effect.flatMap((selected) => {
|
|
3219
|
+
if (Option.isNone(selected)) {
|
|
3220
|
+
return Effect.succeed(Option.none());
|
|
3221
|
+
}
|
|
3222
|
+
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
|
|
3223
|
+
? Option.none()
|
|
3224
|
+
: Option.some(decodeSessionRow(tables, rows[0])))));
|
|
3225
|
+
})),
|
|
3226
|
+
appendEvidence: (input) => decodeSessionResult("appendEvidence", () => parseAuthenticationEvents(input.authenticationEvents)).pipe(Effect.flatMap((authenticationEvents) => findSessionForMutation(executor, tables, input.sessionId, "appendEvidence").pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
3227
|
+
? Effect.succeed(Option.none())
|
|
3228
|
+
: executePlan(executor, queryUpdate(tables.session.table, mutationValues(tables.session.appendEvidence({
|
|
3229
|
+
...input,
|
|
3230
|
+
authenticationEvents,
|
|
3231
|
+
}))).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
|
|
3232
|
+
? Option.none()
|
|
3233
|
+
: Option.some(decodeSessionRow(tables, rows[0]))))))))),
|
|
3234
|
+
assureAndRotate: (input) => decodeSessionResult("assureAndRotate", () => parseAuthenticationEvents(input.authenticationEvents)).pipe(Effect.flatMap(() => findSessionById(executor, tables, input.sessionId, "assureAndRotate")), Effect.flatMap((rowOption) => {
|
|
2256
3235
|
if (Option.isNone(rowOption)) {
|
|
2257
3236
|
return Effect.succeed(Option.none());
|
|
2258
3237
|
}
|
|
2259
|
-
|
|
3238
|
+
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)));
|
|
3239
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => storageError("session", "assureAndRotate", cause)), Effect.flatMap((rows) => decodeSessionResult("assureAndRotate", () => rows[0] === undefined
|
|
2260
3240
|
? Option.none()
|
|
2261
|
-
: Option.some(decodeSessionRow(tables, rows[0]))));
|
|
3241
|
+
: Option.some(decodeSessionRow(tables, rows[0])))));
|
|
2262
3242
|
})),
|
|
2263
3243
|
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),
|
|
2264
3244
|
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),
|
|
@@ -2266,7 +3246,7 @@ export const makeEffectQbSqliteSessionStore = (executor, options = {}) => {
|
|
|
2266
3246
|
};
|
|
2267
3247
|
export const makeEffectQbSqliteVerificationStore = (executor, options = {}) => {
|
|
2268
3248
|
const tables = options.tables ?? authSqliteTables;
|
|
2269
|
-
return VerificationStore.
|
|
3249
|
+
return VerificationStore.of({
|
|
2270
3250
|
findById: (id) => findVerificationById(executor, tables, id),
|
|
2271
3251
|
insert: (row) => executePlan(executor, queryInsert(tables.verification.table, mutationValues(tables.verification.insert(row)))).pipe(Effect.mapError((cause) => storageError("verification", "insert", cause)), Effect.asVoid),
|
|
2272
3252
|
consume: (input) => {
|
|
@@ -2294,10 +3274,13 @@ export const makeEffectQbSqliteVerificationStore = (executor, options = {}) => {
|
|
|
2294
3274
|
};
|
|
2295
3275
|
export const makeEffectQbSqliteAuditLogStore = (executor, options = {}) => {
|
|
2296
3276
|
const tables = options.tables ?? authSqliteTables;
|
|
2297
|
-
return AuditLogStore.
|
|
2298
|
-
|
|
3277
|
+
return AuditLogStore.of({
|
|
3278
|
+
insertMany: (events) => Effect.gen(function* () {
|
|
3279
|
+
if (events.length === 0) {
|
|
3280
|
+
return;
|
|
3281
|
+
}
|
|
2299
3282
|
const createdAt = yield* currentUnixMillis;
|
|
2300
|
-
yield* executePlan(executor,
|
|
3283
|
+
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);
|
|
2301
3284
|
}),
|
|
2302
3285
|
list: (input = {}) => listAuditLogRecords(executor, tables, input),
|
|
2303
3286
|
deleteBefore: (input) => deleteAuditLogRecordsBefore(executor, tables, input),
|
|
@@ -2305,7 +3288,7 @@ export const makeEffectQbSqliteAuditLogStore = (executor, options = {}) => {
|
|
|
2305
3288
|
};
|
|
2306
3289
|
export const makeEffectQbSqliteLoginApprovalReviewStore = (executor, options = {}) => {
|
|
2307
3290
|
const tables = options.tables ?? authSqliteTables;
|
|
2308
|
-
return LoginApprovalReviewStore.
|
|
3291
|
+
return LoginApprovalReviewStore.of({
|
|
2309
3292
|
insertPending: (input) => executePlan(executor, queryInsert(tables.loginApprovalReview.table, mutationValues(tables.loginApprovalReview.insertPending(input)))).pipe(Effect.mapError((cause) => loginApprovalReviewStoreError("insert", cause)), Effect.as(input)),
|
|
2310
3293
|
findByApprovalChallengeId: (input) => findLoginApprovalReviewByApprovalChallengeId(executor, tables, input),
|
|
2311
3294
|
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
|
|
@@ -2318,7 +3301,7 @@ export const makeEffectQbSqliteLoginApprovalReviewStore = (executor, options = {
|
|
|
2318
3301
|
};
|
|
2319
3302
|
export const makeEffectQbSqliteLoginRiskHistoryStore = (executor, options = {}) => {
|
|
2320
3303
|
const tables = options.tables ?? authSqliteTables;
|
|
2321
|
-
return LoginRiskHistory.
|
|
3304
|
+
return LoginRiskHistory.of({
|
|
2322
3305
|
lookup: (input) => lookupLoginRiskHistory(executor, tables, input, options.loginRiskHistory),
|
|
2323
3306
|
record: (input) => Effect.gen(function* () {
|
|
2324
3307
|
const createdAt = input.createdAt ?? (yield* currentUnixMillis);
|
|
@@ -2329,15 +3312,21 @@ export const makeEffectQbSqliteLoginRiskHistoryStore = (executor, options = {})
|
|
|
2329
3312
|
};
|
|
2330
3313
|
export const makeEffectQbSqliteTrustedDeviceStore = (executor, options = {}) => {
|
|
2331
3314
|
const tables = options.tables ?? authSqliteTables;
|
|
2332
|
-
|
|
3315
|
+
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);
|
|
3316
|
+
const updateAfterInsertConflict = (input, originalError) => findTrustedDeviceIncludingExpired(executor, tables, input).pipe(Effect.flatMap((existing) => Option.isSome(existing)
|
|
3317
|
+
? updateExisting(input, existing.value)
|
|
3318
|
+
: Effect.fail(trustedDeviceStoreError("trust", originalError))));
|
|
3319
|
+
return TrustedDeviceStore.of({
|
|
2333
3320
|
find: (input) => findTrustedDevice(executor, tables, input),
|
|
2334
3321
|
listForUser: (input) => listTrustedDevicesForUser(executor, tables, input),
|
|
2335
3322
|
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),
|
|
2336
3323
|
upsert: (input) => findTrustedDeviceIncludingExpired(executor, tables, input).pipe(Effect.flatMap((existing) => {
|
|
2337
3324
|
if (Option.isNone(existing)) {
|
|
2338
|
-
return executePlan(executor, queryInsert(tables.trustedDevice.table, mutationValues(tables.trustedDevice.insert(input, input.now)))).pipe(Effect.
|
|
3325
|
+
return executePlan(executor, queryInsert(tables.trustedDevice.table, mutationValues(tables.trustedDevice.insert(input, input.now)))).pipe(Effect.catch((error) => sqliteConstraintDiscriminator(error)
|
|
3326
|
+
? updateAfterInsertConflict(input, error)
|
|
3327
|
+
: Effect.fail(trustedDeviceStoreError("trust", error))), Effect.asVoid);
|
|
2339
3328
|
}
|
|
2340
|
-
return
|
|
3329
|
+
return updateExisting(input, existing.value);
|
|
2341
3330
|
}), Effect.flatMap(() => findTrustedDeviceIncludingExpired(executor, tables, input)), Effect.flatMap((stored) => Option.isSome(stored)
|
|
2342
3331
|
? Effect.succeed(stored.value)
|
|
2343
3332
|
: Effect.fail(trustedDeviceStoreError("trust", "missing row")))),
|
|
@@ -2345,7 +3334,7 @@ export const makeEffectQbSqliteTrustedDeviceStore = (executor, options = {}) =>
|
|
|
2345
3334
|
};
|
|
2346
3335
|
export const makeEffectQbSqlitePasskeyCredentialStore = (executor, options = {}) => {
|
|
2347
3336
|
const tables = options.tables ?? authSqliteTables;
|
|
2348
|
-
return PasskeyCredentialStore.
|
|
3337
|
+
return PasskeyCredentialStore.of({
|
|
2349
3338
|
insert: (row) => executePlan(executor, queryInsert(tables.passkeyCredential.table, mutationValues(tables.passkeyCredential.insert(row)))).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("insert", cause)), Effect.asVoid),
|
|
2350
3339
|
findByCredentialId: (credentialId) => findPasskeyCredentialByCredentialId(executor, tables, credentialId),
|
|
2351
3340
|
listByUser: (input) => {
|
|
@@ -2355,7 +3344,7 @@ export const makeEffectQbSqlitePasskeyCredentialStore = (executor, options = {})
|
|
|
2355
3344
|
: base.pipe(Query.where(queryIsNull(tables.passkeyCredential.select.revokedAt)));
|
|
2356
3345
|
return executePlan(executor, query).pipe(Effect.mapError((cause) => passkeyCredentialStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodePasskeyCredentialRow(tables, row))));
|
|
2357
3346
|
},
|
|
2358
|
-
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
|
|
3347
|
+
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
|
|
2359
3348
|
? Option.none()
|
|
2360
3349
|
: Option.some(decodePasskeyCredentialRow(tables, rows[0])))),
|
|
2361
3350
|
revoke: (input) => findPasskeyCredentialByCredentialId(executor, tables, input.credentialId, "revoke").pipe(Effect.flatMap((row) => {
|
|
@@ -2368,7 +3357,7 @@ export const makeEffectQbSqlitePasskeyCredentialStore = (executor, options = {})
|
|
|
2368
3357
|
};
|
|
2369
3358
|
export const makeEffectQbSqliteTotpFactorStore = (executor, options = {}) => {
|
|
2370
3359
|
const tables = options.tables ?? authSqliteTables;
|
|
2371
|
-
return TotpFactorStore.
|
|
3360
|
+
return TotpFactorStore.of({
|
|
2372
3361
|
insert: (row) => executePlan(executor, queryInsert(tables.totpFactor.table, mutationValues(tables.totpFactor.insert(row)))).pipe(Effect.mapError((cause) => totpFactorStoreError("insert", cause)), Effect.asVoid),
|
|
2373
3362
|
findById: (id) => findTotpFactorById(executor, tables, id),
|
|
2374
3363
|
listByUser: (input) => {
|
|
@@ -2384,9 +3373,35 @@ export const makeEffectQbSqliteTotpFactorStore = (executor, options = {}) => {
|
|
|
2384
3373
|
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
|
|
2385
3374
|
? Option.none()
|
|
2386
3375
|
: Option.some(decodeTotpFactorRow(tables, rows[0])))),
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
3376
|
+
acceptCounter: (input) => {
|
|
3377
|
+
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
|
|
3378
|
+
? queryIsNull(tables.totpFactor.select.lastAcceptedCounter)
|
|
3379
|
+
: queryLt(tables.totpFactor.select.lastAcceptedCounter, input.counter)), Query.returning({ id: tables.totpFactor.select.id })));
|
|
3380
|
+
return Effect.gen(function* () {
|
|
3381
|
+
let rows = yield* attempt(true).pipe(Effect.mapError((cause) => totpFactorStoreError("accept-counter", cause)));
|
|
3382
|
+
if (rows[0] === undefined) {
|
|
3383
|
+
rows = yield* attempt(false).pipe(Effect.mapError((cause) => totpFactorStoreError("accept-counter", cause)));
|
|
3384
|
+
}
|
|
3385
|
+
if (rows[0] !== undefined) {
|
|
3386
|
+
const factor = yield* findTotpFactorById(executor, tables, input.id, "accept-counter");
|
|
3387
|
+
if (Option.isNone(factor)) {
|
|
3388
|
+
return { _tag: "Unavailable" };
|
|
3389
|
+
}
|
|
3390
|
+
return {
|
|
3391
|
+
_tag: "Accepted",
|
|
3392
|
+
factor: factor.value,
|
|
3393
|
+
};
|
|
3394
|
+
}
|
|
3395
|
+
const factor = yield* findTotpFactorById(executor, tables, input.id, "accept-counter");
|
|
3396
|
+
return Option.isSome(factor) &&
|
|
3397
|
+
factor.value.confirmedAt !== undefined &&
|
|
3398
|
+
factor.value.revokedAt === undefined &&
|
|
3399
|
+
factor.value.lastAcceptedCounter !== undefined &&
|
|
3400
|
+
factor.value.lastAcceptedCounter >= input.counter
|
|
3401
|
+
? { _tag: "Replay" }
|
|
3402
|
+
: { _tag: "Unavailable" };
|
|
3403
|
+
});
|
|
3404
|
+
},
|
|
2390
3405
|
revoke: (input) => findTotpFactorById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
|
|
2391
3406
|
if (Option.isNone(row)) {
|
|
2392
3407
|
return Effect.void;
|
|
@@ -2395,9 +3410,9 @@ export const makeEffectQbSqliteTotpFactorStore = (executor, options = {}) => {
|
|
|
2395
3410
|
})),
|
|
2396
3411
|
});
|
|
2397
3412
|
};
|
|
2398
|
-
export const makeEffectQbSqliteRecoveryCodeStore = (executor, options = {}) => {
|
|
3413
|
+
export const makeEffectQbSqliteRecoveryCodeStore = (executor, atomic, options = {}) => {
|
|
2399
3414
|
const tables = options.tables ?? authSqliteTables;
|
|
2400
|
-
return RecoveryCodeStore.
|
|
3415
|
+
return RecoveryCodeStore.of({
|
|
2401
3416
|
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 }),
|
|
2402
3417
|
findById: (id) => findRecoveryCodeById(executor, tables, id),
|
|
2403
3418
|
listByUser: (input) => {
|
|
@@ -2413,6 +3428,13 @@ export const makeEffectQbSqliteRecoveryCodeStore = (executor, options = {}) => {
|
|
|
2413
3428
|
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
|
|
2414
3429
|
? Effect.succeed(Option.none())
|
|
2415
3430
|
: findRecoveryCodeById(executor, tables, input.id, "mark-used"))),
|
|
3431
|
+
replaceActiveForUser: (input) => {
|
|
3432
|
+
const plans = [
|
|
3433
|
+
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))),
|
|
3434
|
+
...input.rows.map((row) => queryInsert(tables.recoveryCode.table, mutationValues(tables.recoveryCode.insert(row)))),
|
|
3435
|
+
];
|
|
3436
|
+
return atomic.execute(plans).pipe(Effect.mapError((cause) => recoveryCodeStoreError("replace-active", cause)), Effect.asVoid);
|
|
3437
|
+
},
|
|
2416
3438
|
revoke: (input) => findRecoveryCodeById(executor, tables, input.id, "revoke").pipe(Effect.flatMap((row) => {
|
|
2417
3439
|
if (Option.isNone(row)) {
|
|
2418
3440
|
return Effect.void;
|
|
@@ -2421,9 +3443,219 @@ export const makeEffectQbSqliteRecoveryCodeStore = (executor, options = {}) => {
|
|
|
2421
3443
|
})),
|
|
2422
3444
|
});
|
|
2423
3445
|
};
|
|
3446
|
+
export const makeEffectQbSqliteRecoveryCodeSessionRotateStore = (executor, atomic, options = {}) => {
|
|
3447
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3448
|
+
return RecoveryCodeSessionRotateStore.of({
|
|
3449
|
+
consumeAndRotate: (input) => findSessionById(executor, tables, input.sessionId, "assureAndRotate").pipe(Effect.flatMap((current) => {
|
|
3450
|
+
if (Option.isNone(current) || current.value.userId !== input.userId) {
|
|
3451
|
+
return Effect.succeed(Option.none());
|
|
3452
|
+
}
|
|
3453
|
+
try {
|
|
3454
|
+
assertSessionAssurance(input);
|
|
3455
|
+
}
|
|
3456
|
+
catch (error) {
|
|
3457
|
+
return Effect.fail(new RecoveryCodeSessionRotateStoreError({
|
|
3458
|
+
message: "Session assurance validation failed",
|
|
3459
|
+
cause: error,
|
|
3460
|
+
}));
|
|
3461
|
+
}
|
|
3462
|
+
const metadata = input.claims === undefined
|
|
3463
|
+
? undefined
|
|
3464
|
+
: encodeSessionMetadata({
|
|
3465
|
+
...current.value,
|
|
3466
|
+
claims: input.claims,
|
|
3467
|
+
});
|
|
3468
|
+
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)))));
|
|
3469
|
+
const consumePlan = queryUpdate(tables.recoveryCode.table, mutationValues(tables.recoveryCode.markUsed({
|
|
3470
|
+
id: input.codeId,
|
|
3471
|
+
usedAt: input.usedAt,
|
|
3472
|
+
...(input.codeMetadata === undefined
|
|
3473
|
+
? {}
|
|
3474
|
+
: { metadata: input.codeMetadata }),
|
|
3475
|
+
}))).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));
|
|
3476
|
+
const sessionValues = mutationValues(tables.session.assureAndRotate(current.value, input));
|
|
3477
|
+
if (metadata !== undefined) {
|
|
3478
|
+
sessionValues[boundColumnName(tables.session.select.metadata)] =
|
|
3479
|
+
metadata;
|
|
3480
|
+
}
|
|
3481
|
+
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 }));
|
|
3482
|
+
return executeAtomicPlans(atomic, [
|
|
3483
|
+
consumePlan,
|
|
3484
|
+
rotatePlan,
|
|
3485
|
+
]).pipe(Effect.mapError((cause) => new RecoveryCodeSessionRotateStoreError({
|
|
3486
|
+
message: "Atomic recovery session rotation failed",
|
|
3487
|
+
cause,
|
|
3488
|
+
})), Effect.flatMap((results) => results[1].length === 0
|
|
3489
|
+
? Effect.succeed(Option.none())
|
|
3490
|
+
: findSessionById(executor, tables, input.sessionId, "assureAndRotate").pipe(Effect.mapError((cause) => new RecoveryCodeSessionRotateStoreError({
|
|
3491
|
+
message: "Failed to load rotated session",
|
|
3492
|
+
cause,
|
|
3493
|
+
})))));
|
|
3494
|
+
}), Effect.mapError((cause) => cause instanceof RecoveryCodeSessionRotateStoreError
|
|
3495
|
+
? cause
|
|
3496
|
+
: new RecoveryCodeSessionRotateStoreError({
|
|
3497
|
+
message: "Failed to load current session",
|
|
3498
|
+
cause,
|
|
3499
|
+
}))),
|
|
3500
|
+
});
|
|
3501
|
+
};
|
|
3502
|
+
export const makeEffectQbSqliteTotpSessionRotateStore = (executor, atomic, options = {}) => {
|
|
3503
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3504
|
+
return TotpSessionRotateStore.of({
|
|
3505
|
+
acceptAndRotate: (input) => findSessionById(executor, tables, input.sessionId, "assureAndRotate").pipe(Effect.flatMap((current) => {
|
|
3506
|
+
if (Option.isNone(current) || current.value.userId !== input.userId) {
|
|
3507
|
+
return Effect.succeed(Option.none());
|
|
3508
|
+
}
|
|
3509
|
+
try {
|
|
3510
|
+
assertSessionAssurance(input);
|
|
3511
|
+
}
|
|
3512
|
+
catch (error) {
|
|
3513
|
+
return Effect.fail(new TotpSessionRotateStoreError({
|
|
3514
|
+
message: "Session assurance validation failed",
|
|
3515
|
+
cause: error,
|
|
3516
|
+
}));
|
|
3517
|
+
}
|
|
3518
|
+
const metadata = input.claims === undefined
|
|
3519
|
+
? undefined
|
|
3520
|
+
: encodeSessionMetadata({
|
|
3521
|
+
...current.value,
|
|
3522
|
+
claims: input.claims,
|
|
3523
|
+
});
|
|
3524
|
+
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)))));
|
|
3525
|
+
const acceptPlan = queryUpdate(tables.totpFactor.table, mutationValues(tables.totpFactor.acceptCounter({
|
|
3526
|
+
id: input.factorId,
|
|
3527
|
+
counter: input.counter,
|
|
3528
|
+
lastUsedAt: input.lastUsedAt,
|
|
3529
|
+
...(input.factorMetadata === undefined
|
|
3530
|
+
? {}
|
|
3531
|
+
: { metadata: input.factorMetadata }),
|
|
3532
|
+
}))).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));
|
|
3533
|
+
const sessionValues = mutationValues(tables.session.assureAndRotate(current.value, input));
|
|
3534
|
+
if (metadata !== undefined) {
|
|
3535
|
+
sessionValues[boundColumnName(tables.session.select.metadata)] =
|
|
3536
|
+
metadata;
|
|
3537
|
+
}
|
|
3538
|
+
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 }));
|
|
3539
|
+
return executeAtomicPlans(atomic, [
|
|
3540
|
+
acceptPlan,
|
|
3541
|
+
rotatePlan,
|
|
3542
|
+
]).pipe(Effect.mapError((cause) => new TotpSessionRotateStoreError({
|
|
3543
|
+
message: "Atomic TOTP session rotation failed",
|
|
3544
|
+
cause,
|
|
3545
|
+
})), Effect.flatMap((results) => results[1].length === 0
|
|
3546
|
+
? Effect.succeed(Option.none())
|
|
3547
|
+
: findSessionById(executor, tables, input.sessionId, "assureAndRotate").pipe(Effect.mapError((cause) => new TotpSessionRotateStoreError({
|
|
3548
|
+
message: "Failed to load rotated session",
|
|
3549
|
+
cause,
|
|
3550
|
+
})))));
|
|
3551
|
+
}), Effect.mapError((cause) => cause instanceof TotpSessionRotateStoreError
|
|
3552
|
+
? cause
|
|
3553
|
+
: new TotpSessionRotateStoreError({
|
|
3554
|
+
message: "Failed to load current session",
|
|
3555
|
+
cause,
|
|
3556
|
+
}))),
|
|
3557
|
+
});
|
|
3558
|
+
};
|
|
3559
|
+
export const makeEffectQbSqliteTotpLoginCommitStore = (atomic, options = {}) => {
|
|
3560
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3561
|
+
return TotpLoginCommitStore.of({
|
|
3562
|
+
commit: (input) => {
|
|
3563
|
+
const { verification, session } = input;
|
|
3564
|
+
if (verification.type !== "auth-flow-state" ||
|
|
3565
|
+
verification.subject !== input.userId ||
|
|
3566
|
+
(session === undefined && input.continuation !== "login-approval") ||
|
|
3567
|
+
(session !== undefined && input.continuation !== undefined) ||
|
|
3568
|
+
(session !== undefined && session.userId !== input.userId)) {
|
|
3569
|
+
return Effect.succeed(false);
|
|
3570
|
+
}
|
|
3571
|
+
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)))));
|
|
3572
|
+
const verificationPlan = queryUpdate(tables.verification.table, mutationValues(tables.verification.consume({
|
|
3573
|
+
id: verification.id,
|
|
3574
|
+
type: verification.type,
|
|
3575
|
+
consumedAt: input.consumedAt,
|
|
3576
|
+
}))).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));
|
|
3577
|
+
const factorPlan = queryUpdate(tables.totpFactor.table, mutationValues(tables.totpFactor.acceptCounter({
|
|
3578
|
+
id: input.factorId,
|
|
3579
|
+
counter: input.counter,
|
|
3580
|
+
lastUsedAt: input.lastUsedAt,
|
|
3581
|
+
...(input.factorMetadata === undefined
|
|
3582
|
+
? {}
|
|
3583
|
+
: { metadata: input.factorMetadata }),
|
|
3584
|
+
}))).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 }));
|
|
3585
|
+
if (session === undefined) {
|
|
3586
|
+
return executeAtomicPlans(atomic, [
|
|
3587
|
+
verificationPlan,
|
|
3588
|
+
factorPlan,
|
|
3589
|
+
]).pipe(Effect.map((results) => results[1].length === 1), Effect.mapError((cause) => new TotpLoginCommitStoreError({
|
|
3590
|
+
message: "Atomic TOTP login commit failed",
|
|
3591
|
+
cause,
|
|
3592
|
+
})));
|
|
3593
|
+
}
|
|
3594
|
+
const sessionExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.id, session.id))));
|
|
3595
|
+
const sessionVerificationPlan = verificationPlan.pipe(Query.where(queryNot(sessionExists)));
|
|
3596
|
+
const sessionPlan = queryInsertWhere(tables.session.table, mutationValues(tables.session.insert(session)), queryChangedOne()).pipe(Query.returning({ id: tables.session.select.id }));
|
|
3597
|
+
return executeAtomicPlans(atomic, [
|
|
3598
|
+
sessionVerificationPlan,
|
|
3599
|
+
factorPlan,
|
|
3600
|
+
sessionPlan,
|
|
3601
|
+
]).pipe(Effect.map((results) => results[2].length === 1), Effect.mapError((cause) => new TotpLoginCommitStoreError({
|
|
3602
|
+
message: "Atomic TOTP login commit failed",
|
|
3603
|
+
cause,
|
|
3604
|
+
})));
|
|
3605
|
+
},
|
|
3606
|
+
});
|
|
3607
|
+
};
|
|
3608
|
+
export const makeEffectQbSqliteRecoveryCodeLoginCommitStore = (atomic, options = {}) => {
|
|
3609
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3610
|
+
return RecoveryCodeLoginCommitStore.of({
|
|
3611
|
+
commit: (input) => {
|
|
3612
|
+
const { verification, session } = input;
|
|
3613
|
+
if (verification.type !== "auth-flow-state" ||
|
|
3614
|
+
verification.subject !== input.userId ||
|
|
3615
|
+
(session === undefined && input.continuation !== "login-approval") ||
|
|
3616
|
+
(session !== undefined && input.continuation !== undefined) ||
|
|
3617
|
+
(session !== undefined && session.userId !== input.userId)) {
|
|
3618
|
+
return Effect.succeed(false);
|
|
3619
|
+
}
|
|
3620
|
+
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))));
|
|
3621
|
+
const verificationPlan = queryUpdate(tables.verification.table, mutationValues(tables.verification.consume({
|
|
3622
|
+
id: verification.id,
|
|
3623
|
+
type: verification.type,
|
|
3624
|
+
consumedAt: input.consumedAt,
|
|
3625
|
+
}))).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));
|
|
3626
|
+
const codePlan = queryUpdate(tables.recoveryCode.table, mutationValues(tables.recoveryCode.markUsed({
|
|
3627
|
+
id: input.codeId,
|
|
3628
|
+
usedAt: input.usedAt,
|
|
3629
|
+
...(input.codeMetadata === undefined
|
|
3630
|
+
? {}
|
|
3631
|
+
: { metadata: input.codeMetadata }),
|
|
3632
|
+
}))).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 }));
|
|
3633
|
+
if (session === undefined) {
|
|
3634
|
+
return executeAtomicPlans(atomic, [
|
|
3635
|
+
verificationPlan,
|
|
3636
|
+
codePlan,
|
|
3637
|
+
]).pipe(Effect.map((results) => results[1].length === 1), Effect.mapError((cause) => new RecoveryCodeLoginCommitStoreError({
|
|
3638
|
+
message: "Atomic recovery-code login commit failed",
|
|
3639
|
+
cause,
|
|
3640
|
+
})));
|
|
3641
|
+
}
|
|
3642
|
+
const sessionExists = queryExists(Query.select({ value: Query.literal(1) }).pipe(Query.from(tables.session.table), Query.where(queryEq(tables.session.select.id, session.id))));
|
|
3643
|
+
const sessionVerificationPlan = verificationPlan.pipe(Query.where(queryNot(sessionExists)));
|
|
3644
|
+
const sessionPlan = queryInsertWhere(tables.session.table, mutationValues(tables.session.insert(session)), queryChangedOne()).pipe(Query.returning({ id: tables.session.select.id }));
|
|
3645
|
+
return executeAtomicPlans(atomic, [
|
|
3646
|
+
sessionVerificationPlan,
|
|
3647
|
+
codePlan,
|
|
3648
|
+
sessionPlan,
|
|
3649
|
+
]).pipe(Effect.map((results) => results[2].length === 1), Effect.mapError((cause) => new RecoveryCodeLoginCommitStoreError({
|
|
3650
|
+
message: "Atomic recovery-code login commit failed",
|
|
3651
|
+
cause,
|
|
3652
|
+
})));
|
|
3653
|
+
},
|
|
3654
|
+
});
|
|
3655
|
+
};
|
|
2424
3656
|
export const makeEffectQbSqliteApiKeyStore = (executor, options = {}) => {
|
|
2425
3657
|
const tables = options.tables ?? authSqliteTables;
|
|
2426
|
-
return ApiKeyStore.
|
|
3658
|
+
return ApiKeyStore.of({
|
|
2427
3659
|
insert: (row) => executePlan(executor, queryInsert(tables.apiKey.table, mutationValues(tables.apiKey.insert(row)))).pipe(Effect.mapError((cause) => apiKeyStoreError("insert", cause)), Effect.asVoid),
|
|
2428
3660
|
findById: (id) => findApiKeyById(executor, tables, id),
|
|
2429
3661
|
findByPrefix: (prefix) => findApiKeyByPrefix(executor, tables, prefix),
|
|
@@ -2445,9 +3677,9 @@ export const makeEffectQbSqliteApiKeyStore = (executor, options = {}) => {
|
|
|
2445
3677
|
})),
|
|
2446
3678
|
});
|
|
2447
3679
|
};
|
|
2448
|
-
export const makeEffectQbSqliteRefreshTokenStore = (executor, options = {}) => {
|
|
3680
|
+
export const makeEffectQbSqliteRefreshTokenStore = (executor, atomic, options = {}) => {
|
|
2449
3681
|
const tables = options.tables ?? authSqliteTables;
|
|
2450
|
-
return RefreshTokenStore.
|
|
3682
|
+
return RefreshTokenStore.of({
|
|
2451
3683
|
insert: (row) => executePlan(executor, queryInsert(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(row)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("insert", cause)), Effect.asVoid),
|
|
2452
3684
|
findById: (id) => findRefreshTokenById(executor, tables, id),
|
|
2453
3685
|
findBySecretHash: (hash) => findRefreshTokenBySecretHash(executor, tables, hash),
|
|
@@ -2465,21 +3697,28 @@ export const makeEffectQbSqliteRefreshTokenStore = (executor, options = {}) => {
|
|
|
2465
3697
|
: base.pipe(Query.where(queryIsNull(tables.refreshToken.select.revokedAt)));
|
|
2466
3698
|
return executePlan(executor, query).pipe(Effect.mapError((cause) => refreshTokenStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeRefreshTokenRow(tables, row))));
|
|
2467
3699
|
},
|
|
2468
|
-
rotate: (input) =>
|
|
2469
|
-
const
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
return Effect.succeed(Option.none());
|
|
2476
|
-
}
|
|
2477
|
-
return executePlan(executor, queryInsert(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(input.replacement)))).pipe(Effect.mapError((cause) => refreshTokenStoreError("rotate", cause)), Effect.as(Option.some({
|
|
2478
|
-
previous: previous.value,
|
|
2479
|
-
replacement: input.replacement,
|
|
2480
|
-
})));
|
|
3700
|
+
rotate: (input) => {
|
|
3701
|
+
const { replacement } = input;
|
|
3702
|
+
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({
|
|
3703
|
+
...selectRefreshTokenColumns(tables),
|
|
3704
|
+
lastUsedAt: queryWithoutNullRefinement(tables.refreshToken.select.lastUsedAt),
|
|
3705
|
+
rotatedAt: queryWithoutNullRefinement(tables.refreshToken.select.rotatedAt),
|
|
3706
|
+
replacedById: queryWithoutNullRefinement(tables.refreshToken.select.replacedById),
|
|
2481
3707
|
}));
|
|
2482
|
-
|
|
3708
|
+
const replacementPlan = queryInsertWhere(tables.refreshToken.table, mutationValues(tables.refreshToken.insert(replacement)), queryChangedOne()).pipe(Query.returning({ id: tables.refreshToken.select.id }));
|
|
3709
|
+
return executeAtomicPlans(atomic, [
|
|
3710
|
+
rotatePlan,
|
|
3711
|
+
replacementPlan,
|
|
3712
|
+
]).pipe(Effect.mapError((cause) => refreshTokenStoreError("rotate", cause)), Effect.map(([previousRows, replacementRows]) => {
|
|
3713
|
+
const [previous] = previousRows;
|
|
3714
|
+
return previous === undefined || replacementRows.length === 0
|
|
3715
|
+
? Option.none()
|
|
3716
|
+
: Option.some({
|
|
3717
|
+
previous: decodeRefreshTokenRow(tables, previous),
|
|
3718
|
+
replacement,
|
|
3719
|
+
});
|
|
3720
|
+
}));
|
|
3721
|
+
},
|
|
2483
3722
|
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
|
|
2484
3723
|
? Option.none()
|
|
2485
3724
|
: Option.some(decodeRefreshTokenRow(tables, rows[0])))),
|
|
@@ -2494,7 +3733,7 @@ export const makeEffectQbSqliteRefreshTokenStore = (executor, options = {}) => {
|
|
|
2494
3733
|
};
|
|
2495
3734
|
export const makeEffectQbSqliteJwtRevocationStore = (executor, options = {}) => {
|
|
2496
3735
|
const tables = options.tables ?? authSqliteTables;
|
|
2497
|
-
return JwtRevocationStore.
|
|
3736
|
+
return JwtRevocationStore.of({
|
|
2498
3737
|
revoke: (record) => findJwtRevocation(executor, tables, record.jwtId, "revoke").pipe(Effect.flatMap((existing) => {
|
|
2499
3738
|
if (Option.isNone(existing)) {
|
|
2500
3739
|
return executePlan(executor, queryInsert(tables.jwtRevocation.table, mutationValues(tables.jwtRevocation.insert(record))));
|
|
@@ -2508,7 +3747,7 @@ export const makeEffectQbSqliteJwtRevocationStore = (executor, options = {}) =>
|
|
|
2508
3747
|
};
|
|
2509
3748
|
export const makeEffectQbSqliteOAuthAccountStore = (executor, options = {}) => {
|
|
2510
3749
|
const tables = options.tables ?? authSqliteTables;
|
|
2511
|
-
return OAuthAccountStore.
|
|
3750
|
+
return OAuthAccountStore.of({
|
|
2512
3751
|
insert: (row) => executePlan(executor, queryInsert(tables.oauthAccount.table, mutationValues(tables.oauthAccount.insert(row)))).pipe(Effect.mapError((cause) => oauthAccountStoreError("insert", cause)), Effect.asVoid),
|
|
2513
3752
|
findById: (id) => findOAuthAccountById(executor, tables, id),
|
|
2514
3753
|
findByProviderAccount: (input) => findOAuthAccountByProviderAccount(executor, tables, input),
|
|
@@ -2532,9 +3771,90 @@ export const makeEffectQbSqliteOAuthAccountStore = (executor, options = {}) => {
|
|
|
2532
3771
|
})),
|
|
2533
3772
|
});
|
|
2534
3773
|
};
|
|
3774
|
+
export const makeEffectQbSqliteOAuthIdentityBridgeStore = (executor, atomic, options = {}) => {
|
|
3775
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3776
|
+
const identities = makeEffectQbSqliteIdentityStore(executor, atomic, options);
|
|
3777
|
+
const accounts = makeEffectQbSqliteOAuthAccountStore(executor, options);
|
|
3778
|
+
const classify = (input, cause) => Effect.gen(function* () {
|
|
3779
|
+
if (Option.isSome(yield* findUserById(executor, tables, input.user.id))) {
|
|
3780
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3781
|
+
reason: "user-id",
|
|
3782
|
+
});
|
|
3783
|
+
}
|
|
3784
|
+
if (input.identity !== undefined) {
|
|
3785
|
+
if (Option.isSome(yield* identities.findById(input.identity.id))) {
|
|
3786
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3787
|
+
reason: "identity-id",
|
|
3788
|
+
});
|
|
3789
|
+
}
|
|
3790
|
+
if (Option.isSome(yield* identities.findByKindAndNormalizedValue({
|
|
3791
|
+
scope: input.identity.scope,
|
|
3792
|
+
kind: input.identity.kind,
|
|
3793
|
+
normalizedValue: input.identity.normalizedValue,
|
|
3794
|
+
}))) {
|
|
3795
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3796
|
+
reason: "identity-value",
|
|
3797
|
+
});
|
|
3798
|
+
}
|
|
3799
|
+
}
|
|
3800
|
+
if (Option.isSome(yield* accounts.findById(input.account.id))) {
|
|
3801
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3802
|
+
reason: "oauth-account-id",
|
|
3803
|
+
});
|
|
3804
|
+
}
|
|
3805
|
+
if (Option.isSome(yield* accounts.findByProviderAccount({
|
|
3806
|
+
providerId: input.account.providerId,
|
|
3807
|
+
providerAccountId: input.account.providerAccountId,
|
|
3808
|
+
includeUnlinked: true,
|
|
3809
|
+
}))) {
|
|
3810
|
+
return yield* new OAuthIdentityBridgeConflictError({
|
|
3811
|
+
reason: "provider-account",
|
|
3812
|
+
});
|
|
3813
|
+
}
|
|
3814
|
+
return yield* new OAuthIdentityBridgeStoreError({
|
|
3815
|
+
message: "Atomic OAuth identity creation failed",
|
|
3816
|
+
cause,
|
|
3817
|
+
});
|
|
3818
|
+
}).pipe(Effect.catch((error) => error instanceof OAuthIdentityBridgeConflictError ||
|
|
3819
|
+
error instanceof OAuthIdentityBridgeStoreError
|
|
3820
|
+
? Effect.fail(error)
|
|
3821
|
+
: Effect.fail(new OAuthIdentityBridgeStoreError({
|
|
3822
|
+
message: "OAuth identity conflict classification failed",
|
|
3823
|
+
cause: error,
|
|
3824
|
+
}))));
|
|
3825
|
+
return OAuthIdentityBridgeStore.of({
|
|
3826
|
+
create: (input) => {
|
|
3827
|
+
if (input.account.userId !== input.user.id ||
|
|
3828
|
+
(input.identity !== undefined &&
|
|
3829
|
+
input.identity.userId !== input.user.id)) {
|
|
3830
|
+
return Effect.fail(new OAuthIdentityBridgeConflictError({ reason: "user-id" }));
|
|
3831
|
+
}
|
|
3832
|
+
const plans = [
|
|
3833
|
+
queryInsert(tables.user.table, mutationValues(tables.user.insert(input.user))),
|
|
3834
|
+
];
|
|
3835
|
+
if (input.identity !== undefined) {
|
|
3836
|
+
plans.push(queryInsert(tables.identity.table, mutationValues(tables.identity.insert(input.identity))));
|
|
3837
|
+
}
|
|
3838
|
+
plans.push(queryInsert(tables.oauthAccount.table, mutationValues(tables.oauthAccount.insert(input.account))));
|
|
3839
|
+
return atomic.execute(plans).pipe(Effect.map(() => ({
|
|
3840
|
+
user: input.user,
|
|
3841
|
+
...(input.identity === undefined
|
|
3842
|
+
? {}
|
|
3843
|
+
: {
|
|
3844
|
+
identity: {
|
|
3845
|
+
...input.identity,
|
|
3846
|
+
isPrimaryLogin: input.identity.isPrimaryLogin ?? false,
|
|
3847
|
+
updatedAt: input.identity.createdAt,
|
|
3848
|
+
},
|
|
3849
|
+
}),
|
|
3850
|
+
account: input.account,
|
|
3851
|
+
})), Effect.catch((error) => classify(input, error)));
|
|
3852
|
+
},
|
|
3853
|
+
});
|
|
3854
|
+
};
|
|
2535
3855
|
export const makeEffectQbSqliteOAuthClientStore = (executor, options = {}) => {
|
|
2536
3856
|
const tables = options.tables ?? authSqliteTables;
|
|
2537
|
-
return OAuthClientStore.
|
|
3857
|
+
return OAuthClientStore.of({
|
|
2538
3858
|
upsert: (record) => executePlan(executor, queryInsert(tables.oauthClient.table, mutationValues(tables.oauthClient.insert(record))).pipe(Query.onConflict("id", {
|
|
2539
3859
|
update: mutationValues(tables.oauthClient.update(record)),
|
|
2540
3860
|
}))).pipe(Effect.mapError((cause) => oauthClientStoreError("upsert", cause)), Effect.asVoid),
|
|
@@ -2551,7 +3871,7 @@ export const makeEffectQbSqliteOAuthClientStore = (executor, options = {}) => {
|
|
|
2551
3871
|
};
|
|
2552
3872
|
export const makeEffectQbSqliteOAuthConsentStore = (executor, options = {}) => {
|
|
2553
3873
|
const tables = options.tables ?? authSqliteTables;
|
|
2554
|
-
return OAuthConsentStore.
|
|
3874
|
+
return OAuthConsentStore.of({
|
|
2555
3875
|
upsert: (record) => executePlan(executor, queryInsert(tables.oauthConsent.table, mutationValues(tables.oauthConsent.insert(record))).pipe(Query.onConflict("id", {
|
|
2556
3876
|
update: mutationValues(tables.oauthConsent.update(record)),
|
|
2557
3877
|
}))).pipe(Effect.mapError((cause) => oauthConsentStoreError("upsert", cause)), Effect.asVoid),
|
|
@@ -2575,12 +3895,14 @@ export const makeEffectQbSqliteOAuthConsentStore = (executor, options = {}) => {
|
|
|
2575
3895
|
};
|
|
2576
3896
|
export const makeEffectQbSqliteOAuthAuthorizationCodeStore = (executor, options = {}) => {
|
|
2577
3897
|
const tables = options.tables ?? authSqliteTables;
|
|
2578
|
-
return OAuthAuthorizationCodeStore.
|
|
3898
|
+
return OAuthAuthorizationCodeStore.of({
|
|
2579
3899
|
upsert: (record) => executePlan(executor, queryInsert(tables.oauthAuthorizationCode.table, mutationValues(tables.oauthAuthorizationCode.insert(record))).pipe(Query.onConflict("code_hash", {
|
|
2580
3900
|
update: mutationValues(tables.oauthAuthorizationCode.update(record)),
|
|
2581
3901
|
}))).pipe(Effect.mapError((cause) => oauthAuthorizationCodeStoreError("upsert", cause)), Effect.asVoid),
|
|
2582
3902
|
findByHash: (input) => findOAuthAuthorizationCodeByHash(executor, tables, input),
|
|
2583
|
-
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({
|
|
3903
|
+
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({
|
|
3904
|
+
codeHash: tables.oauthAuthorizationCode.select.codeHash,
|
|
3905
|
+
}))).pipe(Effect.mapError((cause) => oauthAuthorizationCodeStoreError("mark-consumed", cause)), Effect.flatMap((rows) => {
|
|
2584
3906
|
const [consumed] = rows;
|
|
2585
3907
|
if (consumed !== undefined) {
|
|
2586
3908
|
return findOAuthAuthorizationCodeByHash(executor, tables, { codeHash: consumed.codeHash }, "mark-consumed").pipe(Effect.map((record) => Option.isNone(record)
|
|
@@ -2599,10 +3921,214 @@ export const makeEffectQbSqliteOAuthAuthorizationCodeStore = (executor, options
|
|
|
2599
3921
|
})),
|
|
2600
3922
|
});
|
|
2601
3923
|
};
|
|
3924
|
+
export const makeEffectQbSqliteOAuthDeviceAuthorizationStore = (executor, atomic, options = {}) => {
|
|
3925
|
+
const tables = options.tables ?? authSqliteTables;
|
|
3926
|
+
const mapping = oauthDeviceAuthorizationTable(tables);
|
|
3927
|
+
const selection = selectOAuthDeviceAuthorizationColumns(tables);
|
|
3928
|
+
const decode = (row) => decodeOAuthDeviceAuthorizationRow(mapping, row);
|
|
3929
|
+
const find = (column, value, operation) => executePlan(executor, Query.select(selection).pipe(Query.from(mapping.table), Query.where(queryEq(column, value)))).pipe(Effect.mapError((cause) => oauthDeviceAuthorizationStoreError(operation, cause)), Effect.flatMap((rows) => Effect.try({
|
|
3930
|
+
try: () => rows[0] === undefined
|
|
3931
|
+
? Option.none()
|
|
3932
|
+
: Option.some(decode(rows[0])),
|
|
3933
|
+
catch: (cause) => oauthDeviceAuthorizationStoreError(operation, cause),
|
|
3934
|
+
})));
|
|
3935
|
+
return OAuthDeviceAuthorizationStore.of({
|
|
3936
|
+
insert: (record) => executePlan(executor, queryInsert(mapping.table, mutationValues(mapping.insert(record))).pipe(Query.returning({ id: mapping.select.id }))).pipe(Effect.as(true), Effect.catch((error) => sqliteConstraintDiscriminator(error)
|
|
3937
|
+
? Effect.succeed(false)
|
|
3938
|
+
: Effect.fail(oauthDeviceAuthorizationStoreError("insert", error)))),
|
|
3939
|
+
findByDeviceCodeHash: (input) => find(mapping.select.deviceCodeHash, input.deviceCodeHash, "find-by-device-code-hash"),
|
|
3940
|
+
findByUserCodeHash: (input) => find(mapping.select.userCodeHash, input.userCodeHash, "find-by-user-code-hash"),
|
|
3941
|
+
findById: (id) => find(mapping.select.id, id, "find-by-id"),
|
|
3942
|
+
decide: (input) => {
|
|
3943
|
+
const updatePlan = queryUpdate(mapping.table, mutationValues(input.decision === "approve"
|
|
3944
|
+
? mapping.approve(input)
|
|
3945
|
+
: mapping.deny(input))).pipe(Query.where(queryEq(mapping.select.id, input.id)), Query.where(queryEq(mapping.select.status, "pending")), Query.where(queryGt(mapping.select.expiresAt, Number(input.decidedAt))), Query.returning(selection));
|
|
3946
|
+
const classifyPlan = Query.select(selection).pipe(Query.from(mapping.table), Query.where(queryEq(mapping.select.id, input.id)));
|
|
3947
|
+
return executeAtomicPlans(atomic, [
|
|
3948
|
+
updatePlan,
|
|
3949
|
+
classifyPlan,
|
|
3950
|
+
]).pipe(Effect.mapError((cause) => oauthDeviceAuthorizationStoreError("decide", cause)), Effect.flatMap(([updated, selected]) => Effect.try({
|
|
3951
|
+
try: () => {
|
|
3952
|
+
const changed = updated[0];
|
|
3953
|
+
if (changed !== undefined) {
|
|
3954
|
+
const record = decode(changed);
|
|
3955
|
+
return record.status === "approved"
|
|
3956
|
+
? { status: "approved", record }
|
|
3957
|
+
: {
|
|
3958
|
+
status: "denied",
|
|
3959
|
+
record: record,
|
|
3960
|
+
};
|
|
3961
|
+
}
|
|
3962
|
+
const row = selected[0];
|
|
3963
|
+
if (row === undefined) {
|
|
3964
|
+
return { status: "missing" };
|
|
3965
|
+
}
|
|
3966
|
+
const record = decode(row);
|
|
3967
|
+
if (Number(input.decidedAt) >= Number(record.expiresAt)) {
|
|
3968
|
+
return { status: "expired", record };
|
|
3969
|
+
}
|
|
3970
|
+
if (record.status === "pending") {
|
|
3971
|
+
return { status: "expired", record };
|
|
3972
|
+
}
|
|
3973
|
+
return { status: "already_decided", record };
|
|
3974
|
+
},
|
|
3975
|
+
catch: (cause) => oauthDeviceAuthorizationStoreError("decide", cause),
|
|
3976
|
+
})));
|
|
3977
|
+
},
|
|
3978
|
+
poll: (input) => {
|
|
3979
|
+
const active = [
|
|
3980
|
+
queryEq(mapping.select.deviceCodeHash, input.deviceCodeHash),
|
|
3981
|
+
queryEq(mapping.select.clientId, input.clientId),
|
|
3982
|
+
queryGt(mapping.select.expiresAt, Number(input.now)),
|
|
3983
|
+
queryIsNull(mapping.select.consumedAt),
|
|
3984
|
+
queryIn(mapping.select.status, "pending", "approved"),
|
|
3985
|
+
];
|
|
3986
|
+
const intervalColumn = boundColumnName(mapping.select.pollIntervalSeconds);
|
|
3987
|
+
const nextPollColumn = boundColumnName(mapping.select.nextPollAt);
|
|
3988
|
+
const lastPolledColumn = boundColumnName(mapping.select.lastPolledAt);
|
|
3989
|
+
const slowedInterval = sqliteIncrementByFive(mapping.select.pollIntervalSeconds);
|
|
3990
|
+
const slowPlan = queryUpdate(mapping.table, {
|
|
3991
|
+
[intervalColumn]: slowedInterval,
|
|
3992
|
+
[nextPollColumn]: sqliteUnixMillisAfterSeconds(input.now, slowedInterval, mapping.select.status, mapping.select.nextPollAt),
|
|
3993
|
+
[lastPolledColumn]: Number(input.now),
|
|
3994
|
+
}).pipe(Query.where(active[0]), Query.where(active[1]), Query.where(active[2]), Query.where(active[3]), Query.where(active[4]), Query.where(queryEq(mapping.select.status, "pending")), Query.where(queryGt(mapping.select.nextPollAt, Number(input.now))), Query.returning({
|
|
3995
|
+
...selection,
|
|
3996
|
+
consumedAt: queryWithoutNullRefinement(mapping.select.consumedAt),
|
|
3997
|
+
}));
|
|
3998
|
+
const pendingPlan = queryUpdate(mapping.table, {
|
|
3999
|
+
[nextPollColumn]: sqliteUnixMillisAfterSeconds(input.now, mapping.select.pollIntervalSeconds, mapping.select.status, mapping.select.nextPollAt),
|
|
4000
|
+
[lastPolledColumn]: Number(input.now),
|
|
4001
|
+
}).pipe(Query.where(active[0]), Query.where(active[1]), Query.where(active[2]), Query.where(active[3]), Query.where(active[4]), Query.where(queryEq(mapping.select.status, "pending")), Query.where(queryLte(mapping.select.nextPollAt, Number(input.now))), Query.returning(selection));
|
|
4002
|
+
const classifyPlan = Query.select(selection).pipe(Query.from(mapping.table), Query.where(queryEq(mapping.select.deviceCodeHash, input.deviceCodeHash)), Query.where(queryEq(mapping.select.clientId, input.clientId)));
|
|
4003
|
+
return executeAtomicPlans(atomic, [
|
|
4004
|
+
slowPlan,
|
|
4005
|
+
pendingPlan,
|
|
4006
|
+
classifyPlan,
|
|
4007
|
+
]).pipe(Effect.mapError((cause) => oauthDeviceAuthorizationStoreError("poll", cause)), Effect.flatMap(([slowed, pending, selected]) => Effect.try({
|
|
4008
|
+
try: () => {
|
|
4009
|
+
const slowRow = slowed[0];
|
|
4010
|
+
if (slowRow !== undefined) {
|
|
4011
|
+
const record = decode(slowRow);
|
|
4012
|
+
return {
|
|
4013
|
+
status: "slow_down",
|
|
4014
|
+
record,
|
|
4015
|
+
interval: record.pollIntervalSeconds,
|
|
4016
|
+
};
|
|
4017
|
+
}
|
|
4018
|
+
const pendingRow = pending[0];
|
|
4019
|
+
if (pendingRow !== undefined) {
|
|
4020
|
+
return {
|
|
4021
|
+
status: "pending",
|
|
4022
|
+
record: decode(pendingRow),
|
|
4023
|
+
};
|
|
4024
|
+
}
|
|
4025
|
+
const row = selected[0];
|
|
4026
|
+
if (row === undefined) {
|
|
4027
|
+
return { status: "missing" };
|
|
4028
|
+
}
|
|
4029
|
+
const record = decode(row);
|
|
4030
|
+
if (record.status === "approved" &&
|
|
4031
|
+
record.consumedAt !== undefined) {
|
|
4032
|
+
return {
|
|
4033
|
+
status: "already_consumed",
|
|
4034
|
+
record: record,
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
if (Number(input.now) >= Number(record.expiresAt)) {
|
|
4038
|
+
return { status: "expired", record };
|
|
4039
|
+
}
|
|
4040
|
+
if (record.status === "denied") {
|
|
4041
|
+
return { status: "denied", record };
|
|
4042
|
+
}
|
|
4043
|
+
if (record.status === "approved") {
|
|
4044
|
+
return {
|
|
4045
|
+
status: "approved",
|
|
4046
|
+
record: record,
|
|
4047
|
+
};
|
|
4048
|
+
}
|
|
4049
|
+
return { status: "missing" };
|
|
4050
|
+
},
|
|
4051
|
+
catch: (cause) => oauthDeviceAuthorizationStoreError("poll", cause),
|
|
4052
|
+
})));
|
|
4053
|
+
},
|
|
4054
|
+
deleteExpired: (input) => executePlan(executor, Query.delete(mapping.table).pipe(Query.where(queryLte(mapping.select.expiresAt, Number(input.beforeExpiresAt))), Query.returning({ id: mapping.select.id }))).pipe(Effect.mapError((cause) => oauthDeviceAuthorizationStoreError("delete-expired", cause)), Effect.map((rows) => rows.length)),
|
|
4055
|
+
});
|
|
4056
|
+
};
|
|
4057
|
+
export const makeEffectQbSqliteOAuthDeviceCodeTokenCommitStore = (atomic, options = {}) => {
|
|
4058
|
+
const tables = options.tables ?? authSqliteTables;
|
|
4059
|
+
const authorizationTable = oauthDeviceAuthorizationTable(tables);
|
|
4060
|
+
const authorizationSelection = selectOAuthDeviceAuthorizationColumns(tables);
|
|
4061
|
+
const decodeAuthorization = (row) => decodeOAuthDeviceAuthorizationRow(authorizationTable, row);
|
|
4062
|
+
return OAuthDeviceCodeTokenCommitStore.of({
|
|
4063
|
+
commit: (input) => {
|
|
4064
|
+
const { authorization } = input;
|
|
4065
|
+
const nextPollColumn = boundColumnName(authorizationTable.select.nextPollAt);
|
|
4066
|
+
const lastPolledColumn = boundColumnName(authorizationTable.select.lastPolledAt);
|
|
4067
|
+
const consumedColumn = boundColumnName(authorizationTable.select.consumedAt);
|
|
4068
|
+
const consumePlan = queryUpdate(authorizationTable.table, {
|
|
4069
|
+
[consumedColumn]: Number(input.consumedAt),
|
|
4070
|
+
[lastPolledColumn]: Number(input.consumedAt),
|
|
4071
|
+
[nextPollColumn]: sqliteUnixMillisAfterSeconds(input.consumedAt, authorizationTable.select.pollIntervalSeconds, authorizationTable.select.status, authorizationTable.select.nextPollAt),
|
|
4072
|
+
}).pipe(Query.where(queryEq(authorizationTable.select.id, authorization.id)), Query.where(queryEq(authorizationTable.select.deviceCodeHash, authorization.deviceCodeHash)), Query.where(queryEq(authorizationTable.select.userCodeHash, authorization.userCodeHash)), Query.where(queryEq(authorizationTable.select.clientId, authorization.clientId)), Query.where(queryEq(authorizationTable.select.requestedScopes, JSON.stringify(authorization.requestedScopes))), Query.where(queryEq(authorizationTable.select.status, "approved")), Query.where(queryEq(authorizationTable.select.subject, authorization.subject)), Query.where(queryEq(authorizationTable.select.grantedScopes, JSON.stringify(authorization.grantedScopes))), Query.where(queryEq(authorizationTable.select.issuedAt, Number(authorization.issuedAt))), Query.where(queryEq(authorizationTable.select.expiresAt, Number(authorization.expiresAt))), Query.where(queryEq(authorizationTable.select.pollIntervalSeconds, authorization.pollIntervalSeconds)), Query.where(queryEq(authorizationTable.select.nextPollAt, Number(authorization.nextPollAt))), Query.where(queryIsNotDistinctFrom(authorizationTable.select.lastPolledAt, authorization.lastPolledAt === undefined
|
|
4073
|
+
? null
|
|
4074
|
+
: Number(authorization.lastPolledAt))), Query.where(queryEq(authorizationTable.select.approvedAt, Number(authorization.approvedAt))), Query.where(queryIsNotDistinctFrom(authorizationTable.select.metadata, jsonEncode(authorization.metadata))), Query.where(queryIsNull(authorizationTable.select.consumedAt)), Query.where(queryGt(authorizationTable.select.expiresAt, Number(input.consumedAt))), Query.returning({
|
|
4075
|
+
...authorizationSelection,
|
|
4076
|
+
lastPolledAt: queryWithoutNullRefinement(authorizationTable.select.lastPolledAt),
|
|
4077
|
+
consumedAt: queryWithoutNullRefinement(authorizationTable.select.consumedAt),
|
|
4078
|
+
}));
|
|
4079
|
+
const tokenPlans = input.tokenRecords.map((record) => queryInsertWhere(tables.oauthProviderModeToken.table, mutationValues(tables.oauthProviderModeToken.insert(record)), queryChangedOne()).pipe(Query.returning({
|
|
4080
|
+
tokenHash: tables.oauthProviderModeToken.select.tokenHash,
|
|
4081
|
+
})));
|
|
4082
|
+
const classifyPlan = Query.select(authorizationSelection).pipe(Query.from(authorizationTable.table), Query.where(queryEq(authorizationTable.select.id, authorization.id)));
|
|
4083
|
+
const plans = [
|
|
4084
|
+
consumePlan,
|
|
4085
|
+
...tokenPlans,
|
|
4086
|
+
classifyPlan,
|
|
4087
|
+
];
|
|
4088
|
+
return executeAtomicPlans(atomic, plans).pipe(Effect.mapError((cause) => new OAuthDeviceCodeTokenCommitStoreError({
|
|
4089
|
+
operation: "commit",
|
|
4090
|
+
message: "Atomic OAuth device token commit failed",
|
|
4091
|
+
cause,
|
|
4092
|
+
})), Effect.flatMap((results) => Effect.try({
|
|
4093
|
+
try: () => {
|
|
4094
|
+
const changed = results[0]?.[0];
|
|
4095
|
+
if (changed !== undefined) {
|
|
4096
|
+
return {
|
|
4097
|
+
status: "committed",
|
|
4098
|
+
record: decodeAuthorization(changed),
|
|
4099
|
+
};
|
|
4100
|
+
}
|
|
4101
|
+
const selected = results.at(-1)?.[0];
|
|
4102
|
+
if (selected === undefined) {
|
|
4103
|
+
return { status: "missing" };
|
|
4104
|
+
}
|
|
4105
|
+
const record = decodeAuthorization(selected);
|
|
4106
|
+
if (record.status === "approved" &&
|
|
4107
|
+
record.consumedAt !== undefined) {
|
|
4108
|
+
return {
|
|
4109
|
+
status: "already_consumed",
|
|
4110
|
+
record: record,
|
|
4111
|
+
};
|
|
4112
|
+
}
|
|
4113
|
+
if (record.status === "approved" &&
|
|
4114
|
+
Number(input.consumedAt) >= Number(record.expiresAt)) {
|
|
4115
|
+
return { status: "expired", record };
|
|
4116
|
+
}
|
|
4117
|
+
return { status: "missing" };
|
|
4118
|
+
},
|
|
4119
|
+
catch: (cause) => new OAuthDeviceCodeTokenCommitStoreError({
|
|
4120
|
+
operation: "commit",
|
|
4121
|
+
message: "Failed to decode OAuth device token commit result",
|
|
4122
|
+
cause,
|
|
4123
|
+
}),
|
|
4124
|
+
})));
|
|
4125
|
+
},
|
|
4126
|
+
});
|
|
4127
|
+
};
|
|
2602
4128
|
export const makeEffectQbSqliteOAuthClientSecretStore = (executor, options = {}) => {
|
|
2603
4129
|
const tables = options.tables ?? authSqliteTables;
|
|
2604
|
-
return OAuthClientSecretStore.
|
|
2605
|
-
upsert: (record) => executePlan(executor, queryInsert(tables.oauthClientSecret.table, mutationValues(tables.oauthClientSecret.insert(record))).pipe(Query.onConflict("prefix", {
|
|
4130
|
+
return OAuthClientSecretStore.of({
|
|
4131
|
+
upsert: (record) => executePlan(executor, queryInsert(tables.oauthClientSecret.table, mutationValues(tables.oauthClientSecret.insert(record))).pipe(Query.onConflict(["client_id", "prefix"], {
|
|
2606
4132
|
update: mutationValues(tables.oauthClientSecret.update(record)),
|
|
2607
4133
|
}))).pipe(Effect.mapError((cause) => oauthClientSecretStoreError("upsert", cause)), Effect.asVoid),
|
|
2608
4134
|
findByClientIdAndPrefix: (input) => findOAuthClientSecretByClientIdAndPrefix(executor, tables, input),
|
|
@@ -2621,7 +4147,7 @@ export const makeEffectQbSqliteOAuthClientSecretStore = (executor, options = {})
|
|
|
2621
4147
|
};
|
|
2622
4148
|
export const makeEffectQbSqliteOAuthProviderModeTokenStore = (executor, options = {}) => {
|
|
2623
4149
|
const tables = options.tables ?? authSqliteTables;
|
|
2624
|
-
return OAuthProviderModeTokenStore.
|
|
4150
|
+
return OAuthProviderModeTokenStore.of({
|
|
2625
4151
|
upsert: (record) => executePlan(executor, queryInsert(tables.oauthProviderModeToken.table, mutationValues(tables.oauthProviderModeToken.insert(record))).pipe(Query.onConflict("token_hash", {
|
|
2626
4152
|
update: mutationValues(tables.oauthProviderModeToken.update(record)),
|
|
2627
4153
|
}))).pipe(Effect.mapError((cause) => oauthProviderModeTokenStoreError("upsert", cause)), Effect.asVoid),
|
|
@@ -2646,7 +4172,7 @@ export const makeEffectQbSqliteOAuthProviderModeTokenStore = (executor, options
|
|
|
2646
4172
|
};
|
|
2647
4173
|
export const makeEffectQbSqliteOAuthProviderTokenVault = (executor, options = {}) => {
|
|
2648
4174
|
const tables = options.tables ?? authSqliteTables;
|
|
2649
|
-
return OAuthProviderTokenVault.
|
|
4175
|
+
return OAuthProviderTokenVault.of({
|
|
2650
4176
|
upsert: (record) => findOAuthProviderTokenVaultRecordByAccountId(executor, tables, { accountId: record.accountId }, "upsert", true).pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
2651
4177
|
? executePlan(executor, queryInsert(tables.oauthProviderTokenVault.table, mutationValues(tables.oauthProviderTokenVault.insert(record))))
|
|
2652
4178
|
: 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
|
|
@@ -2671,7 +4197,7 @@ export const makeEffectQbSqliteOAuthProviderTokenVault = (executor, options = {}
|
|
|
2671
4197
|
};
|
|
2672
4198
|
export const makeEffectQbSqliteDomainVerificationStore = (executor, options = {}) => {
|
|
2673
4199
|
const tables = options.tables ?? authSqliteTables;
|
|
2674
|
-
return DomainVerificationStore.
|
|
4200
|
+
return DomainVerificationStore.of({
|
|
2675
4201
|
upsert: (record) => findDomainVerificationByOwnerAndDomain(executor, tables, { ownerId: record.ownerId, domain: record.domain }, "upsert").pipe(Effect.flatMap((existing) => Option.isNone(existing)
|
|
2676
4202
|
? executePlan(executor, queryInsert(tables.domainVerification.table, mutationValues(tables.domainVerification.insert(record))))
|
|
2677
4203
|
: 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
|
|
@@ -2683,8 +4209,8 @@ export const makeEffectQbSqliteDomainVerificationStore = (executor, options = {}
|
|
|
2683
4209
|
const base = Query.select(selectDomainVerificationColumns(tables)).pipe(Query.from(tables.domainVerification.table), Query.where(queryEq(tables.domainVerification.select.ownerId, input.ownerId)));
|
|
2684
4210
|
const query = input.includeRevoked === true
|
|
2685
4211
|
? base
|
|
2686
|
-
: base.pipe(Query.where(
|
|
2687
|
-
return executePlan(executor, query).pipe(Effect.mapError((cause) => domainVerificationStoreError("list", cause)), Effect.map((rows) => rows.map((row) => decodeDomainVerificationRecord(tables, row))));
|
|
4212
|
+
: base.pipe(Query.where(queryNeq(tables.domainVerification.select.status, "revoked")));
|
|
4213
|
+
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))));
|
|
2688
4214
|
},
|
|
2689
4215
|
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
|
|
2690
4216
|
? Option.none()
|
|
@@ -2699,7 +4225,7 @@ export const makeEffectQbSqliteDomainVerificationStore = (executor, options = {}
|
|
|
2699
4225
|
};
|
|
2700
4226
|
export const makeEffectQbSqliteSecurityTimelineStore = (executor, options = {}) => {
|
|
2701
4227
|
const tables = options.tables ?? authSqliteTables;
|
|
2702
|
-
return SecurityTimelineStore.
|
|
4228
|
+
return SecurityTimelineStore.of({
|
|
2703
4229
|
insert: (event) => executePlan(executor, queryInsert(tables.securityTimeline.table, mutationValues(tables.securityTimeline.insert(event)))).pipe(Effect.mapError((cause) => securityTimelineStoreError("insert", cause)), Effect.asVoid),
|
|
2704
4230
|
listByUser: (input) => listSecurityTimelineByUser(executor, tables, input),
|
|
2705
4231
|
deleteBefore: (input) => deleteSecurityTimelineBefore(executor, tables, input),
|
|
@@ -2707,7 +4233,7 @@ export const makeEffectQbSqliteSecurityTimelineStore = (executor, options = {})
|
|
|
2707
4233
|
};
|
|
2708
4234
|
export const makeEffectQbSqliteWebhookOutboxStore = (executor, options = {}) => {
|
|
2709
4235
|
const tables = options.tables ?? authSqliteTables;
|
|
2710
|
-
return WebhookOutboxStore.
|
|
4236
|
+
return WebhookOutboxStore.of({
|
|
2711
4237
|
enqueue: (input) => Effect.gen(function* () {
|
|
2712
4238
|
const now = input.now ?? (yield* currentUnixMillis);
|
|
2713
4239
|
const record = {
|
|
@@ -2726,7 +4252,8 @@ export const makeEffectQbSqliteWebhookOutboxStore = (executor, options = {}) =>
|
|
|
2726
4252
|
claimDue: (input) => claimDueWebhookOutboxRecords(executor, tables, input),
|
|
2727
4253
|
markDelivered: (input) => Effect.gen(function* () {
|
|
2728
4254
|
const existing = yield* findWebhookOutboxRecordById(executor, tables, input.id, "markDelivered");
|
|
2729
|
-
if (Option.isNone(existing) ||
|
|
4255
|
+
if (Option.isNone(existing) ||
|
|
4256
|
+
existing.value.status === "dead_lettered") {
|
|
2730
4257
|
return;
|
|
2731
4258
|
}
|
|
2732
4259
|
const deliveredAt = input.deliveredAt ?? (yield* currentUnixMillis);
|
|
@@ -2764,7 +4291,7 @@ export const makeEffectQbSqliteWebhookOutboxStore = (executor, options = {}) =>
|
|
|
2764
4291
|
};
|
|
2765
4292
|
export const makeEffectQbSqliteWebhookReplayStore = (executor, options = {}) => {
|
|
2766
4293
|
const tables = options.tables ?? authSqliteTables;
|
|
2767
|
-
return WebhookReplayStore.
|
|
4294
|
+
return WebhookReplayStore.of({
|
|
2768
4295
|
claim: (input) => Effect.gen(function* () {
|
|
2769
4296
|
const now = input.now ?? (yield* currentUnixMillis);
|
|
2770
4297
|
const claim = { ...input, now };
|
|
@@ -2777,8 +4304,297 @@ export const makeEffectQbSqliteWebhookReplayStore = (executor, options = {}) =>
|
|
|
2777
4304
|
deleteExpired: (input = {}) => deleteExpiredWebhookReplayRecords(executor, tables, input),
|
|
2778
4305
|
});
|
|
2779
4306
|
};
|
|
2780
|
-
|
|
4307
|
+
const permissionScopeKey = (scope) => [
|
|
4308
|
+
scope?.type ?? "global",
|
|
4309
|
+
scope?.id === undefined ? 0 : 1,
|
|
4310
|
+
scope?.id ?? "",
|
|
4311
|
+
];
|
|
4312
|
+
const comparePermissionText = (left, right) => left === right
|
|
4313
|
+
? 0
|
|
4314
|
+
: left === undefined
|
|
4315
|
+
? -1
|
|
4316
|
+
: right === undefined
|
|
4317
|
+
? 1
|
|
4318
|
+
: left < right
|
|
4319
|
+
? -1
|
|
4320
|
+
: 1;
|
|
4321
|
+
const comparePermissionScopes = (left, right) => {
|
|
4322
|
+
if (left === undefined || right === undefined) {
|
|
4323
|
+
return left === right ? 0 : left === undefined ? -1 : 1;
|
|
4324
|
+
}
|
|
4325
|
+
return (comparePermissionText(left.type, right.type) ||
|
|
4326
|
+
comparePermissionText(left.id, right.id));
|
|
4327
|
+
};
|
|
4328
|
+
const permissionScopesMatchExactly = (left, right) => {
|
|
4329
|
+
const [leftType, leftIdPresent, leftId] = permissionScopeKey(left);
|
|
4330
|
+
const [rightType, rightIdPresent, rightId] = permissionScopeKey(right);
|
|
4331
|
+
return (leftType === rightType &&
|
|
4332
|
+
leftIdPresent === rightIdPresent &&
|
|
4333
|
+
leftId === rightId);
|
|
4334
|
+
};
|
|
4335
|
+
const permissionGrantIsActive = (grant, at) => grant.revokedAt === undefined &&
|
|
4336
|
+
(grant.expiresAt === undefined || Number(grant.expiresAt) > Number(at));
|
|
4337
|
+
const comparePermissionGrantRecords = (left, right) => comparePermissionText(left.subject.type, right.subject.type) ||
|
|
4338
|
+
comparePermissionText(left.subject.id, right.subject.id) ||
|
|
4339
|
+
comparePermissionText(left.permission, right.permission) ||
|
|
4340
|
+
comparePermissionScopes(left.scope, right.scope);
|
|
4341
|
+
const compareRoleGrantRecords = (left, right) => comparePermissionText(left.subject.type, right.subject.type) ||
|
|
4342
|
+
comparePermissionText(left.subject.id, right.subject.id) ||
|
|
4343
|
+
comparePermissionText(left.role, right.role) ||
|
|
4344
|
+
comparePermissionScopes(left.scope, right.scope);
|
|
4345
|
+
const compareRolePermissionRecords = (left, right) => comparePermissionText(left.role, right.role) ||
|
|
4346
|
+
comparePermissionText(left.permission, right.permission) ||
|
|
4347
|
+
comparePermissionText(left.scopeType, right.scopeType);
|
|
4348
|
+
export const makeEffectQbSqlitePermissionStore = (executor, options = {}) => {
|
|
4349
|
+
const tables = options.tables ?? authSqliteTables;
|
|
4350
|
+
const permissionGrantWhere = (input) => {
|
|
4351
|
+
const { scope: _scope, ...withoutScope } = input;
|
|
4352
|
+
const scope = canonicalPermissionScope(input.scope);
|
|
4353
|
+
const canonicalInput = {
|
|
4354
|
+
...withoutScope,
|
|
4355
|
+
...(scope === undefined ? {} : { scope }),
|
|
4356
|
+
};
|
|
4357
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(scope);
|
|
4358
|
+
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)));
|
|
4359
|
+
};
|
|
4360
|
+
const roleGrantWhere = (input) => {
|
|
4361
|
+
const { scope: _scope, ...withoutScope } = input;
|
|
4362
|
+
const scope = canonicalPermissionScope(input.scope);
|
|
4363
|
+
const canonicalInput = {
|
|
4364
|
+
...withoutScope,
|
|
4365
|
+
...(scope === undefined ? {} : { scope }),
|
|
4366
|
+
};
|
|
4367
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(scope);
|
|
4368
|
+
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)));
|
|
4369
|
+
};
|
|
4370
|
+
const checkResult = (operation, allowed) => executePlan(executor, Query.select({ allowed: allowed })).pipe(Effect.mapError((cause) => permissionStoreError(operation, cause)), Effect.map((rows) => decodeBoolean(rows[0]?.allowed)));
|
|
4371
|
+
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
|
|
4372
|
+
? Option.none()
|
|
4373
|
+
: Option.some(decodePermissionDefinition(tables, rows[0]))));
|
|
4374
|
+
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
|
|
4375
|
+
? Option.none()
|
|
4376
|
+
: Option.some(decodeRoleDefinition(tables, rows[0]))));
|
|
4377
|
+
const mutatePermissionDefinition = (operation, input, values) => {
|
|
4378
|
+
const returning = operation === "delete_permission_definition"
|
|
4379
|
+
? {
|
|
4380
|
+
...selectPermissionDefinitionColumns(tables),
|
|
4381
|
+
deletedAt: queryWithoutNullRefinement(tables.permissionDefinition.select.deletedAt),
|
|
4382
|
+
}
|
|
4383
|
+
: selectPermissionDefinitionColumns(tables);
|
|
4384
|
+
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
|
|
4385
|
+
? Option.none()
|
|
4386
|
+
: Option.some(decodePermissionDefinition(tables, rows[0]))));
|
|
4387
|
+
};
|
|
4388
|
+
const mutateRoleDefinition = (operation, input, values) => {
|
|
4389
|
+
const returning = operation === "delete_role_definition"
|
|
4390
|
+
? {
|
|
4391
|
+
...selectRoleDefinitionColumns(tables),
|
|
4392
|
+
deletedAt: queryWithoutNullRefinement(tables.roleDefinition.select.deletedAt),
|
|
4393
|
+
}
|
|
4394
|
+
: selectRoleDefinitionColumns(tables);
|
|
4395
|
+
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
|
|
4396
|
+
? Option.none()
|
|
4397
|
+
: Option.some(decodeRoleDefinition(tables, rows[0]))));
|
|
4398
|
+
};
|
|
4399
|
+
return PermissionStore.of({
|
|
4400
|
+
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)),
|
|
4401
|
+
findPermissionDefinition: (id) => findPermissionDefinition(id),
|
|
4402
|
+
listPermissionDefinitions: (input = {}) => {
|
|
4403
|
+
const base = Query.select(selectPermissionDefinitionColumns(tables)).pipe(Query.from(tables.permissionDefinition.table));
|
|
4404
|
+
const withoutDisabled = input.includeDisabled === true
|
|
4405
|
+
? base
|
|
4406
|
+
: base.pipe(Query.where(queryIsNull(tables.permissionDefinition.select.disabledAt)));
|
|
4407
|
+
const withoutDeleted = input.includeDeleted === true
|
|
4408
|
+
? withoutDisabled
|
|
4409
|
+
: withoutDisabled.pipe(Query.where(queryIsNull(tables.permissionDefinition.select.deletedAt)));
|
|
4410
|
+
const after = input.after === undefined
|
|
4411
|
+
? withoutDeleted
|
|
4412
|
+
: withoutDeleted.pipe(Query.where(queryGt(tables.permissionDefinition.select.id, input.after)));
|
|
4413
|
+
const query = after.pipe(Query.orderBy(tables.permissionDefinition.select.id, "asc"), Query.limit(normalizePermissionDefinitionListLimit(input.limit)));
|
|
4414
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_permission_definitions", cause)), Effect.map((rows) => rows.map((row) => decodePermissionDefinition(tables, row))));
|
|
4415
|
+
},
|
|
4416
|
+
updatePermissionDefinition: (input) => mutatePermissionDefinition("update_permission_definition", input, tables.permissionDefinition.update(input)),
|
|
4417
|
+
setPermissionDefinitionDisabled: (input) => mutatePermissionDefinition("set_permission_definition_disabled", input, tables.permissionDefinition.setDisabled(input)),
|
|
4418
|
+
deletePermissionDefinition: (input) => mutatePermissionDefinition("delete_permission_definition", input, tables.permissionDefinition.delete(input)),
|
|
4419
|
+
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)),
|
|
4420
|
+
findRoleDefinition: (id) => findRoleDefinition(id),
|
|
4421
|
+
listRoleDefinitions: (input = {}) => {
|
|
4422
|
+
const base = Query.select(selectRoleDefinitionColumns(tables)).pipe(Query.from(tables.roleDefinition.table));
|
|
4423
|
+
const withoutDisabled = input.includeDisabled === true
|
|
4424
|
+
? base
|
|
4425
|
+
: base.pipe(Query.where(queryIsNull(tables.roleDefinition.select.disabledAt)));
|
|
4426
|
+
const query = input.includeDeleted === true
|
|
4427
|
+
? withoutDisabled
|
|
4428
|
+
: withoutDisabled.pipe(Query.where(queryIsNull(tables.roleDefinition.select.deletedAt)));
|
|
4429
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_role_definitions", cause)), Effect.map((rows) => rows
|
|
4430
|
+
.map((row) => decodeRoleDefinition(tables, row))
|
|
4431
|
+
.sort((left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0)));
|
|
4432
|
+
},
|
|
4433
|
+
updateRoleDefinition: (input) => mutateRoleDefinition("update_role_definition", input, tables.roleDefinition.update(input)),
|
|
4434
|
+
setRoleDefinitionDisabled: (input) => mutateRoleDefinition("set_role_definition_disabled", input, tables.roleDefinition.setDisabled(input)),
|
|
4435
|
+
deleteRoleDefinition: (input) => mutateRoleDefinition("delete_role_definition", input, tables.roleDefinition.delete(input)),
|
|
4436
|
+
grantPermission: (input) => Effect.suspend(() => {
|
|
4437
|
+
const { scope: _scope, ...withoutScope } = input;
|
|
4438
|
+
const scope = canonicalPermissionScope(input.scope);
|
|
4439
|
+
const grant = {
|
|
4440
|
+
...withoutScope,
|
|
4441
|
+
...(scope === undefined ? {} : { scope }),
|
|
4442
|
+
};
|
|
4443
|
+
return executePlan(executor, queryInsert(tables.permissionGrant.table, mutationValues(tables.permissionGrant.insert(grant))).pipe(SqliteQuery.onConflict([
|
|
4444
|
+
"subject_type",
|
|
4445
|
+
"subject_id",
|
|
4446
|
+
"permission_id",
|
|
4447
|
+
"scope_type",
|
|
4448
|
+
"scope_id_present",
|
|
4449
|
+
"scope_id",
|
|
4450
|
+
], { update: mutationValues(tables.permissionGrant.update(grant)) }))).pipe(Effect.mapError((cause) => permissionStoreError("grant_permission", cause)), Effect.asVoid);
|
|
4451
|
+
}),
|
|
4452
|
+
revokePermission: (input) => Effect.suspend(() => executePlan(executor, permissionGrantWhere(input)).pipe(Effect.mapError((cause) => permissionStoreError("revoke_permission", cause)), Effect.asVoid)),
|
|
4453
|
+
listPermissionGrants: (input = {}) => Effect.suspend(() => {
|
|
4454
|
+
const at = input.at ?? UnixMillis(Date.now());
|
|
4455
|
+
const activity = input.activity ?? "active";
|
|
4456
|
+
const base = Query.select(selectPermissionGrantColumns(tables)).pipe(Query.from(tables.permissionGrant.table));
|
|
4457
|
+
const bySubjectType = input.subject === undefined
|
|
4458
|
+
? base
|
|
4459
|
+
: base.pipe(Query.where(queryEq(tables.permissionGrant.select.subjectType, input.subject.type)));
|
|
4460
|
+
const bySubject = input.subject === undefined
|
|
4461
|
+
? bySubjectType
|
|
4462
|
+
: bySubjectType.pipe(Query.where(queryEq(tables.permissionGrant.select.subjectId, input.subject.id)));
|
|
4463
|
+
const byPermission = input.permission === undefined
|
|
4464
|
+
? bySubject
|
|
4465
|
+
: bySubject.pipe(Query.where(queryEq(tables.permissionGrant.select.permissionId, input.permission)));
|
|
4466
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(input.scope);
|
|
4467
|
+
const query = input.scope === undefined
|
|
4468
|
+
? byPermission
|
|
4469
|
+
: 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)));
|
|
4470
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_permission_grants", cause)), Effect.flatMap((rows) => Effect.try({
|
|
4471
|
+
try: () => rows
|
|
4472
|
+
.map((row) => decodePermissionGrant(tables, row))
|
|
4473
|
+
.filter((grant) => (input.subject === undefined ||
|
|
4474
|
+
(grant.subject.type === input.subject.type &&
|
|
4475
|
+
grant.subject.id === input.subject.id)) &&
|
|
4476
|
+
(input.permission === undefined ||
|
|
4477
|
+
grant.permission === input.permission) &&
|
|
4478
|
+
(input.scope === undefined ||
|
|
4479
|
+
permissionScopesMatchExactly(grant.scope, input.scope)) &&
|
|
4480
|
+
(activity === "all" ||
|
|
4481
|
+
permissionGrantIsActive(grant, at) ===
|
|
4482
|
+
(activity === "active")))
|
|
4483
|
+
.sort(comparePermissionGrantRecords),
|
|
4484
|
+
catch: (cause) => permissionStoreError("list_permission_grants", cause),
|
|
4485
|
+
})));
|
|
4486
|
+
}),
|
|
4487
|
+
grantRole: (input) => Effect.suspend(() => {
|
|
4488
|
+
const { scope: _scope, ...withoutScope } = input;
|
|
4489
|
+
const scope = canonicalPermissionScope(input.scope);
|
|
4490
|
+
const grant = {
|
|
4491
|
+
...withoutScope,
|
|
4492
|
+
...(scope === undefined ? {} : { scope }),
|
|
4493
|
+
};
|
|
4494
|
+
return executePlan(executor, queryInsert(tables.roleGrant.table, mutationValues(tables.roleGrant.insert(grant))).pipe(SqliteQuery.onConflict([
|
|
4495
|
+
"subject_type",
|
|
4496
|
+
"subject_id",
|
|
4497
|
+
"role_id",
|
|
4498
|
+
"scope_type",
|
|
4499
|
+
"scope_id_present",
|
|
4500
|
+
"scope_id",
|
|
4501
|
+
], { update: mutationValues(tables.roleGrant.update(grant)) }))).pipe(Effect.mapError((cause) => permissionStoreError("grant_role", cause)), Effect.asVoid);
|
|
4502
|
+
}),
|
|
4503
|
+
revokeRole: (input) => Effect.suspend(() => executePlan(executor, roleGrantWhere(input)).pipe(Effect.mapError((cause) => permissionStoreError("revoke_role", cause)), Effect.asVoid)),
|
|
4504
|
+
listRoleGrants: (input = {}) => Effect.suspend(() => {
|
|
4505
|
+
const at = input.at ?? UnixMillis(Date.now());
|
|
4506
|
+
const activity = input.activity ?? "active";
|
|
4507
|
+
const base = Query.select(selectRoleGrantColumns(tables)).pipe(Query.from(tables.roleGrant.table));
|
|
4508
|
+
const bySubjectType = input.subject === undefined
|
|
4509
|
+
? base
|
|
4510
|
+
: base.pipe(Query.where(queryEq(tables.roleGrant.select.subjectType, input.subject.type)));
|
|
4511
|
+
const bySubject = input.subject === undefined
|
|
4512
|
+
? bySubjectType
|
|
4513
|
+
: bySubjectType.pipe(Query.where(queryEq(tables.roleGrant.select.subjectId, input.subject.id)));
|
|
4514
|
+
const byRole = input.role === undefined
|
|
4515
|
+
? bySubject
|
|
4516
|
+
: bySubject.pipe(Query.where(queryEq(tables.roleGrant.select.roleId, input.role)));
|
|
4517
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(input.scope);
|
|
4518
|
+
const query = input.scope === undefined
|
|
4519
|
+
? byRole
|
|
4520
|
+
: 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)));
|
|
4521
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_role_grants", cause)), Effect.flatMap((rows) => Effect.try({
|
|
4522
|
+
try: () => rows
|
|
4523
|
+
.map((row) => decodeRoleGrant(tables, row))
|
|
4524
|
+
.filter((grant) => (input.subject === undefined ||
|
|
4525
|
+
(grant.subject.type === input.subject.type &&
|
|
4526
|
+
grant.subject.id === input.subject.id)) &&
|
|
4527
|
+
(input.role === undefined || grant.role === input.role) &&
|
|
4528
|
+
(input.scope === undefined ||
|
|
4529
|
+
permissionScopesMatchExactly(grant.scope, input.scope)) &&
|
|
4530
|
+
(activity === "all" ||
|
|
4531
|
+
permissionGrantIsActive(grant, at) ===
|
|
4532
|
+
(activity === "active")))
|
|
4533
|
+
.sort(compareRoleGrantRecords),
|
|
4534
|
+
catch: (cause) => permissionStoreError("list_role_grants", cause),
|
|
4535
|
+
})));
|
|
4536
|
+
}),
|
|
4537
|
+
assignRolePermission: (input) => executePlan(executor, queryInsert(tables.rolePermission.table, mutationValues(tables.rolePermission.insert(input))).pipe(SqliteQuery.onConflict([
|
|
4538
|
+
"role_id",
|
|
4539
|
+
"permission_id",
|
|
4540
|
+
"scope_type_present",
|
|
4541
|
+
"scope_type",
|
|
4542
|
+
]))).pipe(Effect.mapError((cause) => permissionStoreError("assign_role_permission", cause)), Effect.asVoid),
|
|
4543
|
+
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),
|
|
4544
|
+
listRolePermissions: (input = {}) => {
|
|
4545
|
+
const base = Query.select(selectRolePermissionColumns(tables)).pipe(Query.from(tables.rolePermission.table));
|
|
4546
|
+
const byRole = input.role === undefined
|
|
4547
|
+
? base
|
|
4548
|
+
: base.pipe(Query.where(queryEq(tables.rolePermission.select.roleId, input.role)));
|
|
4549
|
+
const byPermission = input.permission === undefined
|
|
4550
|
+
? byRole
|
|
4551
|
+
: byRole.pipe(Query.where(queryEq(tables.rolePermission.select.permissionId, input.permission)));
|
|
4552
|
+
const byScopePresence = input.scopeType === undefined
|
|
4553
|
+
? byPermission
|
|
4554
|
+
: byPermission.pipe(Query.where(queryEq(tables.rolePermission.select.scopeTypePresent, input.scopeType === null ? 0 : 1)));
|
|
4555
|
+
const query = input.scopeType === undefined || input.scopeType === null
|
|
4556
|
+
? byScopePresence
|
|
4557
|
+
: byScopePresence.pipe(Query.where(queryEq(tables.rolePermission.select.scopeType, input.scopeType)));
|
|
4558
|
+
return executePlan(executor, query).pipe(Effect.mapError((cause) => permissionStoreError("list_role_permissions", cause)), Effect.flatMap((rows) => Effect.try({
|
|
4559
|
+
try: () => rows
|
|
4560
|
+
.map((row) => decodeRolePermission(tables, row))
|
|
4561
|
+
.filter((rolePermission) => (input.role === undefined ||
|
|
4562
|
+
rolePermission.role === input.role) &&
|
|
4563
|
+
(input.permission === undefined ||
|
|
4564
|
+
rolePermission.permission === input.permission) &&
|
|
4565
|
+
(input.scopeType === undefined ||
|
|
4566
|
+
(input.scopeType === null
|
|
4567
|
+
? rolePermission.scopeType === undefined
|
|
4568
|
+
: rolePermission.scopeType === input.scopeType)))
|
|
4569
|
+
.sort(compareRolePermissionRecords),
|
|
4570
|
+
catch: (cause) => permissionStoreError("list_role_permissions", cause),
|
|
4571
|
+
})));
|
|
4572
|
+
},
|
|
4573
|
+
hasPermission: (input) => Effect.suspend(() => {
|
|
4574
|
+
const now = Date.now();
|
|
4575
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(input.scope);
|
|
4576
|
+
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)));
|
|
4577
|
+
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)));
|
|
4578
|
+
const roleGrant = aliasTable(tables.roleGrant.table, "role_grant");
|
|
4579
|
+
const roleGrantColumn = (column) => aliasColumn(roleGrant, column);
|
|
4580
|
+
const rolePermission = aliasTable(tables.rolePermission.table, "role_permission");
|
|
4581
|
+
const rolePermissionColumn = (column) => aliasColumn(rolePermission, column);
|
|
4582
|
+
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))))));
|
|
4583
|
+
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)));
|
|
4584
|
+
return checkResult("has_permission", queryOr(direct, role));
|
|
4585
|
+
}),
|
|
4586
|
+
hasRole: (input) => Effect.suspend(() => {
|
|
4587
|
+
const now = Date.now();
|
|
4588
|
+
const [scopeType, scopeIdPresent, scopeId] = permissionScopeKey(input.scope);
|
|
4589
|
+
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))))));
|
|
4590
|
+
return checkResult("has_role", allowed);
|
|
4591
|
+
}),
|
|
4592
|
+
});
|
|
4593
|
+
};
|
|
4594
|
+
export const makeEffectQbSqliteStorage = (executor, atomic, options = {}) => ({
|
|
2781
4595
|
userStore: makeEffectQbSqliteUserStore(executor, options),
|
|
4596
|
+
identityStore: makeEffectQbSqliteIdentityStore(executor, atomic, options),
|
|
4597
|
+
registrationStore: makeEffectQbSqliteRegistrationStore(executor, atomic, options),
|
|
2782
4598
|
credentialStore: makeEffectQbSqliteCredentialStore(executor, options),
|
|
2783
4599
|
sessionStore: makeEffectQbSqliteSessionStore(executor, options),
|
|
2784
4600
|
verificationStore: makeEffectQbSqliteVerificationStore(executor, options),
|
|
@@ -2788,14 +4604,21 @@ export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
|
|
|
2788
4604
|
trustedDeviceStore: makeEffectQbSqliteTrustedDeviceStore(executor, options),
|
|
2789
4605
|
passkeyCredentialStore: makeEffectQbSqlitePasskeyCredentialStore(executor, options),
|
|
2790
4606
|
totpFactorStore: makeEffectQbSqliteTotpFactorStore(executor, options),
|
|
2791
|
-
|
|
4607
|
+
totpSessionRotateStore: makeEffectQbSqliteTotpSessionRotateStore(executor, atomic, options),
|
|
4608
|
+
totpLoginCommitStore: makeEffectQbSqliteTotpLoginCommitStore(atomic, options),
|
|
4609
|
+
recoveryCodeStore: makeEffectQbSqliteRecoveryCodeStore(executor, atomic, options),
|
|
4610
|
+
recoveryCodeSessionRotateStore: makeEffectQbSqliteRecoveryCodeSessionRotateStore(executor, atomic, options),
|
|
4611
|
+
recoveryCodeLoginCommitStore: makeEffectQbSqliteRecoveryCodeLoginCommitStore(atomic, options),
|
|
2792
4612
|
apiKeyStore: makeEffectQbSqliteApiKeyStore(executor, options),
|
|
2793
|
-
refreshTokenStore: makeEffectQbSqliteRefreshTokenStore(executor, options),
|
|
4613
|
+
refreshTokenStore: makeEffectQbSqliteRefreshTokenStore(executor, atomic, options),
|
|
2794
4614
|
jwtRevocationStore: makeEffectQbSqliteJwtRevocationStore(executor, options),
|
|
2795
4615
|
oauthAccountStore: makeEffectQbSqliteOAuthAccountStore(executor, options),
|
|
4616
|
+
oauthIdentityBridgeStore: makeEffectQbSqliteOAuthIdentityBridgeStore(executor, atomic, options),
|
|
2796
4617
|
oauthClientStore: makeEffectQbSqliteOAuthClientStore(executor, options),
|
|
2797
4618
|
oauthConsentStore: makeEffectQbSqliteOAuthConsentStore(executor, options),
|
|
2798
4619
|
oauthAuthorizationCodeStore: makeEffectQbSqliteOAuthAuthorizationCodeStore(executor, options),
|
|
4620
|
+
oauthDeviceAuthorizationStore: makeEffectQbSqliteOAuthDeviceAuthorizationStore(executor, atomic, options),
|
|
4621
|
+
oauthDeviceCodeTokenCommitStore: makeEffectQbSqliteOAuthDeviceCodeTokenCommitStore(atomic, options),
|
|
2799
4622
|
oauthClientSecretStore: makeEffectQbSqliteOAuthClientSecretStore(executor, options),
|
|
2800
4623
|
oauthProviderModeTokenStore: makeEffectQbSqliteOAuthProviderModeTokenStore(executor, options),
|
|
2801
4624
|
oauthProviderTokenVault: makeEffectQbSqliteOAuthProviderTokenVault(executor, options),
|
|
@@ -2803,8 +4626,9 @@ export const makeEffectQbSqliteStorage = (executor, options = {}) => ({
|
|
|
2803
4626
|
securityTimelineStore: makeEffectQbSqliteSecurityTimelineStore(executor, options),
|
|
2804
4627
|
webhookOutboxStore: makeEffectQbSqliteWebhookOutboxStore(executor, options),
|
|
2805
4628
|
webhookReplayStore: makeEffectQbSqliteWebhookReplayStore(executor, options),
|
|
4629
|
+
permissionStore: makeEffectQbSqlitePermissionStore(executor, options),
|
|
2806
4630
|
});
|
|
2807
|
-
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)));
|
|
4631
|
+
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(OAuthDeviceAuthorizationStore)(makeEffectQbSqliteOAuthDeviceAuthorizationStore(executor, atomic, options)), Layer.succeed(OAuthDeviceCodeTokenCommitStore)(makeEffectQbSqliteOAuthDeviceCodeTokenCommitStore(atomic, 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)));
|
|
2808
4632
|
export const makeD1EffectQbSqliteExecutor = (database) => Executor.make({
|
|
2809
4633
|
driver: Executor.driver((query) => Effect.tryPromise({
|
|
2810
4634
|
try: () => database
|
|
@@ -2821,5 +4645,110 @@ export const makeD1EffectQbSqliteExecutor = (database) => Executor.make({
|
|
|
2821
4645
|
return Effect.succeed([...(result.results ?? [])]);
|
|
2822
4646
|
}))),
|
|
2823
4647
|
});
|
|
2824
|
-
export const
|
|
4648
|
+
export const makeD1EffectQbSqliteAtomicPlanExecutor = (database) => {
|
|
4649
|
+
const execute = (plans) => {
|
|
4650
|
+
if (plans.length === 0) {
|
|
4651
|
+
return Effect.succeed([]);
|
|
4652
|
+
}
|
|
4653
|
+
return Effect.try({
|
|
4654
|
+
try: () => plans.map((plan, statement) => {
|
|
4655
|
+
try {
|
|
4656
|
+
return {
|
|
4657
|
+
plan,
|
|
4658
|
+
rendered: Renderer.sqlite.render(plan),
|
|
4659
|
+
};
|
|
4660
|
+
}
|
|
4661
|
+
catch (error) {
|
|
4662
|
+
throw new AtomicPlanError({
|
|
4663
|
+
statement,
|
|
4664
|
+
phase: "render",
|
|
4665
|
+
commitState: "not-committed",
|
|
4666
|
+
cause: error,
|
|
4667
|
+
});
|
|
4668
|
+
}
|
|
4669
|
+
}),
|
|
4670
|
+
catch: (cause) => cause instanceof AtomicPlanError
|
|
4671
|
+
? cause
|
|
4672
|
+
: new AtomicPlanError({
|
|
4673
|
+
phase: "render",
|
|
4674
|
+
commitState: "not-committed",
|
|
4675
|
+
cause,
|
|
4676
|
+
}),
|
|
4677
|
+
}).pipe(Effect.flatMap((renderedPlans) => Effect.tryPromise({
|
|
4678
|
+
try: () => {
|
|
4679
|
+
const statements = renderedPlans.map(({ rendered }, statement) => {
|
|
4680
|
+
try {
|
|
4681
|
+
return database.prepare(rendered.sql).bind(...rendered.params);
|
|
4682
|
+
}
|
|
4683
|
+
catch (error) {
|
|
4684
|
+
throw new AtomicPlanError({
|
|
4685
|
+
statement,
|
|
4686
|
+
phase: "prepare",
|
|
4687
|
+
commitState: "not-committed",
|
|
4688
|
+
cause: error,
|
|
4689
|
+
});
|
|
4690
|
+
}
|
|
4691
|
+
});
|
|
4692
|
+
return database.batch(statements);
|
|
4693
|
+
},
|
|
4694
|
+
catch: (cause) => cause instanceof AtomicPlanError
|
|
4695
|
+
? cause
|
|
4696
|
+
: new AtomicPlanError({
|
|
4697
|
+
phase: "execute",
|
|
4698
|
+
commitState: "unknown",
|
|
4699
|
+
cause,
|
|
4700
|
+
}),
|
|
4701
|
+
}).pipe(Effect.flatMap((results) => {
|
|
4702
|
+
const failed = results.findIndex((result) => result.success === false || result.error !== undefined);
|
|
4703
|
+
if (failed !== -1) {
|
|
4704
|
+
return Effect.fail(new AtomicPlanError({
|
|
4705
|
+
statement: failed,
|
|
4706
|
+
phase: "execute",
|
|
4707
|
+
commitState: "not-committed",
|
|
4708
|
+
cause: results[failed]?.error,
|
|
4709
|
+
}));
|
|
4710
|
+
}
|
|
4711
|
+
if (results.length !== renderedPlans.length) {
|
|
4712
|
+
return Effect.fail(new AtomicPlanError({
|
|
4713
|
+
phase: "execute",
|
|
4714
|
+
commitState: "unknown",
|
|
4715
|
+
cause: new Error(`D1 batch returned ${results.length} results for ${renderedPlans.length} statements`),
|
|
4716
|
+
}));
|
|
4717
|
+
}
|
|
4718
|
+
return Effect.try({
|
|
4719
|
+
try: () => results.map((result, statement) => {
|
|
4720
|
+
const entry = renderedPlans[statement];
|
|
4721
|
+
if (entry === undefined) {
|
|
4722
|
+
throw new AtomicPlanError({
|
|
4723
|
+
statement,
|
|
4724
|
+
phase: "decode",
|
|
4725
|
+
commitState: "committed",
|
|
4726
|
+
cause: new Error("Missing rendered atomic plan"),
|
|
4727
|
+
});
|
|
4728
|
+
}
|
|
4729
|
+
try {
|
|
4730
|
+
return EffectQbExecutor.decodeRows(entry.rendered, entry.plan, [...(result.results ?? [])], { driverMode: "raw" });
|
|
4731
|
+
}
|
|
4732
|
+
catch (error) {
|
|
4733
|
+
throw new AtomicPlanError({
|
|
4734
|
+
statement,
|
|
4735
|
+
phase: "decode",
|
|
4736
|
+
commitState: "committed",
|
|
4737
|
+
cause: error,
|
|
4738
|
+
});
|
|
4739
|
+
}
|
|
4740
|
+
}),
|
|
4741
|
+
catch: (cause) => cause instanceof AtomicPlanError
|
|
4742
|
+
? cause
|
|
4743
|
+
: new AtomicPlanError({
|
|
4744
|
+
phase: "decode",
|
|
4745
|
+
commitState: "committed",
|
|
4746
|
+
cause,
|
|
4747
|
+
}),
|
|
4748
|
+
});
|
|
4749
|
+
}))));
|
|
4750
|
+
};
|
|
4751
|
+
return { dialect: "sqlite", execute };
|
|
4752
|
+
};
|
|
4753
|
+
export const D1EffectQbSqliteAuthStorageLive = (database, options = {}) => EffectQbSqliteAuthStorageLive(makeD1EffectQbSqliteExecutor(database), makeD1EffectQbSqliteAtomicPlanExecutor(database), options);
|
|
2825
4754
|
//# sourceMappingURL=EffectQbSqliteStorage.js.map
|