@yielded/auth-persistence 0.1.0-beta.7
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/README.md +21 -0
- package/dist/Drizzle.d.mts +28 -0
- package/dist/Drizzle.mjs +28 -0
- package/dist/DrizzleD1.d.mts +963 -0
- package/dist/DrizzleD1.mjs +29 -0
- package/dist/DrizzleD1.mjs.map +1 -0
- package/dist/DrizzleLibsql.d.mts +2284 -0
- package/dist/DrizzleLibsql.mjs +92 -0
- package/dist/DrizzleLibsql.mjs.map +1 -0
- package/dist/DrizzleMysql2.d.mts +1820 -0
- package/dist/DrizzleMysql2.mjs +55 -0
- package/dist/DrizzleMysql2.mjs.map +1 -0
- package/dist/DrizzlePglite.d.mts +1826 -0
- package/dist/DrizzlePglite.mjs +53 -0
- package/dist/DrizzlePglite.mjs.map +1 -0
- package/dist/DrizzlePostgres.d.mts +1826 -0
- package/dist/DrizzlePostgres.mjs +53 -0
- package/dist/DrizzlePostgres.mjs.map +1 -0
- package/dist/DrizzleSqliteBun.d.mts +2279 -0
- package/dist/DrizzleSqliteBun.mjs +70 -0
- package/dist/DrizzleSqliteBun.mjs.map +1 -0
- package/dist/DrizzleSqliteDo.d.mts +2329 -0
- package/dist/DrizzleSqliteDo.mjs +105 -0
- package/dist/DrizzleSqliteDo.mjs.map +1 -0
- package/dist/DrizzleSqliteNode.d.mts +2279 -0
- package/dist/DrizzleSqliteNode.mjs +70 -0
- package/dist/DrizzleSqliteNode.mjs.map +1 -0
- package/dist/DrizzleSqliteWasm.d.mts +2282 -0
- package/dist/DrizzleSqliteWasm.mjs +70 -0
- package/dist/DrizzleSqliteWasm.mjs.map +1 -0
- package/dist/drizzle/D1BatchStatements.d.mts +11 -0
- package/dist/drizzle/D1BatchStatements.mjs +8 -0
- package/dist/drizzle/D1BatchStatements.mjs.map +1 -0
- package/dist/drizzle/PasswordPreparedPostconditions.mjs +12 -0
- package/dist/drizzle/PasswordPreparedPostconditions.mjs.map +1 -0
- package/dist/drizzle/SuppliedService.d.mts +10 -0
- package/dist/drizzle/d1-emails.d.mts +62 -0
- package/dist/drizzle/d1-emails.mjs +580 -0
- package/dist/drizzle/d1-emails.mjs.map +1 -0
- package/dist/drizzle/d1-generated-statement.d.mts +2 -0
- package/dist/drizzle/d1-generated-statement.mjs +79 -0
- package/dist/drizzle/d1-generated-statement.mjs.map +1 -0
- package/dist/drizzle/d1-identity.d.mts +31 -0
- package/dist/drizzle/d1-identity.mjs +119 -0
- package/dist/drizzle/d1-identity.mjs.map +1 -0
- package/dist/drizzle/d1-oauth.d.mts +301 -0
- package/dist/drizzle/d1-oauth.mjs +76 -0
- package/dist/drizzle/d1-oauth.mjs.map +1 -0
- package/dist/drizzle/d1-passkey.d.mts +51 -0
- package/dist/drizzle/d1-passkey.mjs +95 -0
- package/dist/drizzle/d1-passkey.mjs.map +1 -0
- package/dist/drizzle/d1-password-prepared.d.mts +25 -0
- package/dist/drizzle/d1-password-prepared.mjs +461 -0
- package/dist/drizzle/d1-password-prepared.mjs.map +1 -0
- package/dist/drizzle/d1-passwords.d.mts +87 -0
- package/dist/drizzle/d1-passwords.mjs +1004 -0
- package/dist/drizzle/d1-passwords.mjs.map +1 -0
- package/dist/drizzle/d1-phone.d.mts +26 -0
- package/dist/drizzle/d1-phone.mjs +30 -0
- package/dist/drizzle/d1-phone.mjs.map +1 -0
- package/dist/drizzle/d1-planning.d.mts +12 -0
- package/dist/drizzle/d1-planning.mjs +43 -0
- package/dist/drizzle/d1-planning.mjs.map +1 -0
- package/dist/drizzle/d1-proofs.d.mts +80 -0
- package/dist/drizzle/d1-proofs.mjs +830 -0
- package/dist/drizzle/d1-proofs.mjs.map +1 -0
- package/dist/drizzle/d1-sessions.d.mts +67 -0
- package/dist/drizzle/d1-sessions.mjs +966 -0
- package/dist/drizzle/d1-sessions.mjs.map +1 -0
- package/dist/drizzle/d1-totp.d.mts +26 -0
- package/dist/drizzle/d1-totp.mjs +27 -0
- package/dist/drizzle/d1-totp.mjs.map +1 -0
- package/dist/drizzle/email-model.d.mts +354 -0
- package/dist/drizzle/email-model.mjs +21 -0
- package/dist/drizzle/email-model.mjs.map +1 -0
- package/dist/drizzle/email-registration.d.mts +30 -0
- package/dist/drizzle/email-registration.mjs +238 -0
- package/dist/drizzle/email-registration.mjs.map +1 -0
- package/dist/drizzle/email-sql.d.mts +4 -0
- package/dist/drizzle/email-sql.mjs +9 -0
- package/dist/drizzle/email-sql.mjs.map +1 -0
- package/dist/drizzle/email-target.d.mts +20 -0
- package/dist/drizzle/email-target.mjs +72 -0
- package/dist/drizzle/email-target.mjs.map +1 -0
- package/dist/drizzle/model.d.mts +250 -0
- package/dist/drizzle/model.mjs +58 -0
- package/dist/drizzle/model.mjs.map +1 -0
- package/dist/drizzle/mysql-emails.d.mts +71 -0
- package/dist/drizzle/mysql-emails.mjs +38 -0
- package/dist/drizzle/mysql-emails.mjs.map +1 -0
- package/dist/drizzle/mysql-identity.d.mts +22 -0
- package/dist/drizzle/mysql-identity.mjs +94 -0
- package/dist/drizzle/mysql-identity.mjs.map +1 -0
- package/dist/drizzle/mysql-password-prepared.d.mts +30 -0
- package/dist/drizzle/mysql-password-prepared.mjs +16 -0
- package/dist/drizzle/mysql-password-prepared.mjs.map +1 -0
- package/dist/drizzle/mysql-passwords.d.mts +86 -0
- package/dist/drizzle/mysql-passwords.mjs +38 -0
- package/dist/drizzle/mysql-passwords.mjs.map +1 -0
- package/dist/drizzle/mysql-proofs.d.mts +75 -0
- package/dist/drizzle/mysql-proofs.mjs +23 -0
- package/dist/drizzle/mysql-proofs.mjs.map +1 -0
- package/dist/drizzle/mysql-sessions.d.mts +237 -0
- package/dist/drizzle/mysql-sessions.mjs +53 -0
- package/dist/drizzle/mysql-sessions.mjs.map +1 -0
- package/dist/drizzle/mysql.d.mts +70 -0
- package/dist/drizzle/mysql.mjs +218 -0
- package/dist/drizzle/mysql.mjs.map +1 -0
- package/dist/drizzle/oauth-accounts.mjs +358 -0
- package/dist/drizzle/oauth-accounts.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-access.mjs +210 -0
- package/dist/drizzle/oauth-connected-access.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-collection.mjs +202 -0
- package/dist/drizzle/oauth-connected-collection.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-custody.mjs +146 -0
- package/dist/drizzle/oauth-connected-custody.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-drivers.mjs +76 -0
- package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-flow.mjs +247 -0
- package/dist/drizzle/oauth-connected-flow.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-input.mjs +103 -0
- package/dist/drizzle/oauth-connected-input.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-maintenance.mjs +214 -0
- package/dist/drizzle/oauth-connected-maintenance.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-management.mjs +228 -0
- package/dist/drizzle/oauth-connected-management.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-model.d.mts +278 -0
- package/dist/drizzle/oauth-connected-model.mjs +22 -0
- package/dist/drizzle/oauth-connected-model.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-reference.d.mts +17 -0
- package/dist/drizzle/oauth-connected-reference.mjs +53 -0
- package/dist/drizzle/oauth-connected-reference.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-settlement.mjs +117 -0
- package/dist/drizzle/oauth-connected-settlement.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-state.d.mts +14 -0
- package/dist/drizzle/oauth-connected-state.mjs +422 -0
- package/dist/drizzle/oauth-connected-state.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-target.d.mts +753 -0
- package/dist/drizzle/oauth-connected-target.mjs +91 -0
- package/dist/drizzle/oauth-connected-target.mjs.map +1 -0
- package/dist/drizzle/oauth-drivers.mjs +51 -0
- package/dist/drizzle/oauth-drivers.mjs.map +1 -0
- package/dist/drizzle/oauth-flow.d.mts +5 -0
- package/dist/drizzle/oauth-flow.mjs +253 -0
- package/dist/drizzle/oauth-flow.mjs.map +1 -0
- package/dist/drizzle/oauth-input.mjs +127 -0
- package/dist/drizzle/oauth-input.mjs.map +1 -0
- package/dist/drizzle/oauth-model.d.mts +463 -0
- package/dist/drizzle/oauth-model.mjs +49 -0
- package/dist/drizzle/oauth-model.mjs.map +1 -0
- package/dist/drizzle/oauth-mysql.mjs +8 -0
- package/dist/drizzle/oauth-mysql.mjs.map +1 -0
- package/dist/drizzle/oauth-owner.d.mts +4 -0
- package/dist/drizzle/oauth-owner.mjs +10 -0
- package/dist/drizzle/oauth-owner.mjs.map +1 -0
- package/dist/drizzle/oauth-registration.d.mts +6 -0
- package/dist/drizzle/oauth-registration.mjs +374 -0
- package/dist/drizzle/oauth-registration.mjs.map +1 -0
- package/dist/drizzle/oauth-state.mjs +107 -0
- package/dist/drizzle/oauth-state.mjs.map +1 -0
- package/dist/drizzle/oauth-target.d.mts +111 -0
- package/dist/drizzle/oauth-target.mjs +246 -0
- package/dist/drizzle/oauth-target.mjs.map +1 -0
- package/dist/drizzle/passkey-drivers.mjs +34 -0
- package/dist/drizzle/passkey-drivers.mjs.map +1 -0
- package/dist/drizzle/passkey-kernel.mjs +8 -0
- package/dist/drizzle/passkey-kernel.mjs.map +1 -0
- package/dist/drizzle/passkey-model.d.mts +272 -0
- package/dist/drizzle/passkey-model.mjs +34 -0
- package/dist/drizzle/passkey-model.mjs.map +1 -0
- package/dist/drizzle/passkey-registration-ceremony-model.d.mts +50 -0
- package/dist/drizzle/passkey-registration-ceremony-model.mjs +18 -0
- package/dist/drizzle/passkey-registration-ceremony-model.mjs.map +1 -0
- package/dist/drizzle/passkey-state.mjs +7 -0
- package/dist/drizzle/passkey-state.mjs.map +1 -0
- package/dist/drizzle/passkey-target.d.mts +9 -0
- package/dist/drizzle/passkey-target.mjs +7 -0
- package/dist/drizzle/passkey-target.mjs.map +1 -0
- package/dist/drizzle/passkey-write-drivers.mjs +32 -0
- package/dist/drizzle/passkey-write-drivers.mjs.map +1 -0
- package/dist/drizzle/passkey-write-model.d.mts +190 -0
- package/dist/drizzle/passkey-write-model.mjs +13 -0
- package/dist/drizzle/passkey-write-model.mjs.map +1 -0
- package/dist/drizzle/passkey-write-target.d.mts +11 -0
- package/dist/drizzle/passkey-write-target.mjs +7 -0
- package/dist/drizzle/passkey-write-target.mjs.map +1 -0
- package/dist/drizzle/password-model.d.mts +329 -0
- package/dist/drizzle/password-model.mjs +23 -0
- package/dist/drizzle/password-model.mjs.map +1 -0
- package/dist/drizzle/password-prepared-model.d.mts +94 -0
- package/dist/drizzle/password-prepared-model.mjs +11 -0
- package/dist/drizzle/password-prepared-model.mjs.map +1 -0
- package/dist/drizzle/password-prepared-sql.mjs +390 -0
- package/dist/drizzle/password-prepared-sql.mjs.map +1 -0
- package/dist/drizzle/password-prepared-state.mjs +104 -0
- package/dist/drizzle/password-prepared-state.mjs.map +1 -0
- package/dist/drizzle/password-prepared-target.d.mts +12 -0
- package/dist/drizzle/password-prepared-target.mjs +56 -0
- package/dist/drizzle/password-prepared-target.mjs.map +1 -0
- package/dist/drizzle/password-registration.d.mts +19 -0
- package/dist/drizzle/password-registration.mjs +135 -0
- package/dist/drizzle/password-registration.mjs.map +1 -0
- package/dist/drizzle/password-sql.d.mts +5 -0
- package/dist/drizzle/password-sql.mjs +9 -0
- package/dist/drizzle/password-sql.mjs.map +1 -0
- package/dist/drizzle/password-target.d.mts +17 -0
- package/dist/drizzle/password-target.mjs +61 -0
- package/dist/drizzle/password-target.mjs.map +1 -0
- package/dist/drizzle/pg-emails.d.mts +72 -0
- package/dist/drizzle/pg-emails.mjs +38 -0
- package/dist/drizzle/pg-emails.mjs.map +1 -0
- package/dist/drizzle/pg-password-prepared.d.mts +31 -0
- package/dist/drizzle/pg-password-prepared.mjs +16 -0
- package/dist/drizzle/pg-password-prepared.mjs.map +1 -0
- package/dist/drizzle/pg-passwords.d.mts +87 -0
- package/dist/drizzle/pg-passwords.mjs +38 -0
- package/dist/drizzle/pg-passwords.mjs.map +1 -0
- package/dist/drizzle/pg-proofs.d.mts +76 -0
- package/dist/drizzle/pg-proofs.mjs +22 -0
- package/dist/drizzle/pg-proofs.mjs.map +1 -0
- package/dist/drizzle/pg-sessions.d.mts +238 -0
- package/dist/drizzle/pg-sessions.mjs +53 -0
- package/dist/drizzle/pg-sessions.mjs.map +1 -0
- package/dist/drizzle/pg.d.mts +81 -0
- package/dist/drizzle/pg.mjs +290 -0
- package/dist/drizzle/pg.mjs.map +1 -0
- package/dist/drizzle/phone-model.d.mts +115 -0
- package/dist/drizzle/phone-model.mjs +39 -0
- package/dist/drizzle/phone-model.mjs.map +1 -0
- package/dist/drizzle/phone-state.mjs +16 -0
- package/dist/drizzle/phone-state.mjs.map +1 -0
- package/dist/drizzle/phone-target.d.mts +17 -0
- package/dist/drizzle/phone-target.mjs +170 -0
- package/dist/drizzle/phone-target.mjs.map +1 -0
- package/dist/drizzle/proof-model.d.mts +286 -0
- package/dist/drizzle/proof-model.mjs +17 -0
- package/dist/drizzle/proof-model.mjs.map +1 -0
- package/dist/drizzle/proof-sql.d.mts +4 -0
- package/dist/drizzle/proof-sql.mjs +8 -0
- package/dist/drizzle/proof-sql.mjs.map +1 -0
- package/dist/drizzle/proof-target.d.mts +12 -0
- package/dist/drizzle/proof-target.mjs +36 -0
- package/dist/drizzle/proof-target.mjs.map +1 -0
- package/dist/drizzle/query-operations.mjs +26 -0
- package/dist/drizzle/query-operations.mjs.map +1 -0
- package/dist/drizzle/session-model.d.mts +200 -0
- package/dist/drizzle/session-model.mjs +20 -0
- package/dist/drizzle/session-model.mjs.map +1 -0
- package/dist/drizzle/session-sql.mjs +8 -0
- package/dist/drizzle/session-sql.mjs.map +1 -0
- package/dist/drizzle/session-target.d.mts +28 -0
- package/dist/drizzle/session-target.mjs +47 -0
- package/dist/drizzle/session-target.mjs.map +1 -0
- package/dist/drizzle/sqlite-emails.mjs +45 -0
- package/dist/drizzle/sqlite-emails.mjs.map +1 -0
- package/dist/drizzle/sqlite-identity.mjs +91 -0
- package/dist/drizzle/sqlite-identity.mjs.map +1 -0
- package/dist/drizzle/sqlite-password-prepared.mjs +21 -0
- package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -0
- package/dist/drizzle/sqlite-passwords.mjs +49 -0
- package/dist/drizzle/sqlite-passwords.mjs.map +1 -0
- package/dist/drizzle/sqlite-proofs.mjs +29 -0
- package/dist/drizzle/sqlite-proofs.mjs.map +1 -0
- package/dist/drizzle/sqlite-sessions.mjs +74 -0
- package/dist/drizzle/sqlite-sessions.mjs.map +1 -0
- package/dist/drizzle/sqlite.d.mts +78 -0
- package/dist/drizzle/sqlite.mjs +217 -0
- package/dist/drizzle/sqlite.mjs.map +1 -0
- package/dist/drizzle/standalone-guard.mjs +19 -0
- package/dist/drizzle/standalone-guard.mjs.map +1 -0
- package/dist/drizzle/step-up-model.d.mts +65 -0
- package/dist/drizzle/step-up-model.mjs +9 -0
- package/dist/drizzle/step-up-model.mjs.map +1 -0
- package/dist/drizzle/step-up-state.mjs +32 -0
- package/dist/drizzle/step-up-state.mjs.map +1 -0
- package/dist/drizzle/totp-model.d.mts +84 -0
- package/dist/drizzle/totp-model.mjs +13 -0
- package/dist/drizzle/totp-model.mjs.map +1 -0
- package/dist/drizzle/totp-state.d.mts +38 -0
- package/dist/drizzle/totp-state.mjs +287 -0
- package/dist/drizzle/totp-state.mjs.map +1 -0
- package/dist/drizzle/totp-target.d.mts +17 -0
- package/dist/drizzle/totp-target.mjs +81 -0
- package/dist/drizzle/totp-target.mjs.map +1 -0
- package/dist/drizzle/transaction-execution.d.mts +4 -0
- package/dist/drizzle/transaction-execution.mjs +11 -0
- package/dist/drizzle/transaction-execution.mjs.map +1 -0
- package/dist/drizzle/transaction-mysql.mjs +46 -0
- package/dist/drizzle/transaction-mysql.mjs.map +1 -0
- package/dist/drizzle/transaction-owner.d.mts +2 -0
- package/dist/drizzle/transaction-owner.mjs +8 -0
- package/dist/drizzle/transaction-owner.mjs.map +1 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +5 -0
- package/dist/internal/configuration.d.mts +174 -0
- package/dist/internal/configuration.mjs +7 -0
- package/dist/internal/configuration.mjs.map +1 -0
- package/dist/internal/drizzle-migrations.mjs +19 -0
- package/dist/internal/drizzle-migrations.mjs.map +1 -0
- package/dist/internal/drizzle-postgres.mjs +39 -0
- package/dist/internal/drizzle-postgres.mjs.map +1 -0
- package/dist/internal/drizzle-sqlite.mjs +37 -0
- package/dist/internal/drizzle-sqlite.mjs.map +1 -0
- package/dist/internal/email-kernel.d.mts +13 -0
- package/dist/internal/email-kernel.mjs +487 -0
- package/dist/internal/email-kernel.mjs.map +1 -0
- package/dist/internal/mapping-error.d.mts +11 -0
- package/dist/internal/mapping-error.mjs +30 -0
- package/dist/internal/mapping-error.mjs.map +1 -0
- package/dist/internal/passkey/admission.d.mts +7 -0
- package/dist/internal/passkey/admission.mjs +216 -0
- package/dist/internal/passkey/admission.mjs.map +1 -0
- package/dist/internal/passkey/credentials.d.mts +6 -0
- package/dist/internal/passkey/credentials.mjs +249 -0
- package/dist/internal/passkey/credentials.mjs.map +1 -0
- package/dist/internal/passkey/enrollment.mjs +138 -0
- package/dist/internal/passkey/enrollment.mjs.map +1 -0
- package/dist/internal/passkey/flow.d.mts +10 -0
- package/dist/internal/passkey/flow.mjs +452 -0
- package/dist/internal/passkey/flow.mjs.map +1 -0
- package/dist/internal/passkey/management.mjs +231 -0
- package/dist/internal/passkey/management.mjs.map +1 -0
- package/dist/internal/passkey/registration-ceremony.d.mts +9 -0
- package/dist/internal/passkey/registration-ceremony.mjs +129 -0
- package/dist/internal/passkey/registration-ceremony.mjs.map +1 -0
- package/dist/internal/passkey/registration-custody.d.mts +2 -0
- package/dist/internal/passkey/registration-custody.mjs +52 -0
- package/dist/internal/passkey/registration-custody.mjs.map +1 -0
- package/dist/internal/passkey/registration-write.mjs +211 -0
- package/dist/internal/passkey/registration-write.mjs.map +1 -0
- package/dist/internal/passkey/state.d.mts +5 -0
- package/dist/internal/passkey/state.mjs +234 -0
- package/dist/internal/passkey/state.mjs.map +1 -0
- package/dist/internal/passkey/target.d.mts +19 -0
- package/dist/internal/passkey/target.mjs +190 -0
- package/dist/internal/passkey/target.mjs.map +1 -0
- package/dist/internal/passkey/write-state.mjs +345 -0
- package/dist/internal/passkey/write-state.mjs.map +1 -0
- package/dist/internal/passkey/write-target.mjs +192 -0
- package/dist/internal/passkey/write-target.mjs.map +1 -0
- package/dist/internal/passkey-kernel.mjs +49 -0
- package/dist/internal/passkey-kernel.mjs.map +1 -0
- package/dist/internal/passkeys.mjs +367 -0
- package/dist/internal/passkeys.mjs.map +1 -0
- package/dist/internal/password-kernel.d.mts +13 -0
- package/dist/internal/password-kernel.mjs +704 -0
- package/dist/internal/password-kernel.mjs.map +1 -0
- package/dist/internal/persistence.mjs +289 -0
- package/dist/internal/persistence.mjs.map +1 -0
- package/dist/internal/phone-kernel.mjs +434 -0
- package/dist/internal/phone-kernel.mjs.map +1 -0
- package/dist/internal/proof-kernel.d.mts +9 -0
- package/dist/internal/proof-kernel.mjs +619 -0
- package/dist/internal/proof-kernel.mjs.map +1 -0
- package/dist/internal/query-operations.d.mts +3 -0
- package/dist/internal/registration.mjs +65 -0
- package/dist/internal/registration.mjs.map +1 -0
- package/dist/internal/session-kernel.mjs +737 -0
- package/dist/internal/session-kernel.mjs.map +1 -0
- package/dist/internal/sql-persistence.d.mts +11 -0
- package/dist/internal/sql-persistence.mjs +37 -0
- package/dist/internal/sql-persistence.mjs.map +1 -0
- package/dist/internal/sql-query.mjs +207 -0
- package/dist/internal/sql-query.mjs.map +1 -0
- package/dist/internal/sql-table.d.mts +37 -0
- package/dist/internal/sql-table.mjs +71 -0
- package/dist/internal/sql-table.mjs.map +1 -0
- package/dist/internal/storage-mapping.mjs +687 -0
- package/dist/internal/storage-mapping.mjs.map +1 -0
- package/dist/internal/storage-tables.d.mts +914 -0
- package/dist/internal/storage-tables.mjs +387 -0
- package/dist/internal/storage-tables.mjs.map +1 -0
- package/dist/internal/storage-validation.mjs +58 -0
- package/dist/internal/storage-validation.mjs.map +1 -0
- package/dist/internal/transaction-execution-kernel.d.mts +18 -0
- package/dist/internal/transaction-execution-kernel.mjs +118 -0
- package/dist/internal/transaction-execution-kernel.mjs.map +1 -0
- package/dist/internal/transaction-kernel.d.mts +7 -0
- package/dist/internal/transaction-kernel.mjs +260 -0
- package/dist/internal/transaction-kernel.mjs.map +1 -0
- package/package.json +1 -0
- package/src/Drizzle.ts +378 -0
- package/src/DrizzleD1.ts +136 -0
- package/src/DrizzleLibsql.ts +301 -0
- package/src/DrizzleMysql2.ts +184 -0
- package/src/DrizzlePglite.ts +174 -0
- package/src/DrizzlePostgres.ts +174 -0
- package/src/DrizzleSqliteBun.ts +258 -0
- package/src/DrizzleSqliteDo.ts +473 -0
- package/src/DrizzleSqliteNode.ts +259 -0
- package/src/DrizzleSqliteWasm.ts +263 -0
- package/src/drizzle/D1BatchStatements.ts +10 -0
- package/src/drizzle/PasswordPreparedPostconditions.ts +30 -0
- package/src/drizzle/SuppliedService.ts +10 -0
- package/src/drizzle/d1-emails.ts +1829 -0
- package/src/drizzle/d1-generated-statement.ts +129 -0
- package/src/drizzle/d1-identity.ts +375 -0
- package/src/drizzle/d1-oauth.ts +340 -0
- package/src/drizzle/d1-passkey.ts +373 -0
- package/src/drizzle/d1-password-prepared.ts +1270 -0
- package/src/drizzle/d1-passwords.ts +2656 -0
- package/src/drizzle/d1-phone.ts +96 -0
- package/src/drizzle/d1-planning.ts +104 -0
- package/src/drizzle/d1-proofs.ts +2305 -0
- package/src/drizzle/d1-sessions.ts +2857 -0
- package/src/drizzle/d1-totp.ts +78 -0
- package/src/drizzle/email-model.ts +516 -0
- package/src/drizzle/email-registration.ts +698 -0
- package/src/drizzle/email-sql.ts +24 -0
- package/src/drizzle/email-target.ts +243 -0
- package/src/drizzle/model.ts +463 -0
- package/src/drizzle/mysql-emails.ts +321 -0
- package/src/drizzle/mysql-identity.ts +324 -0
- package/src/drizzle/mysql-password-prepared.ts +204 -0
- package/src/drizzle/mysql-passwords.ts +339 -0
- package/src/drizzle/mysql-proofs.ts +181 -0
- package/src/drizzle/mysql-sessions.ts +741 -0
- package/src/drizzle/mysql.ts +687 -0
- package/src/drizzle/oauth-accounts.ts +804 -0
- package/src/drizzle/oauth-connected-access.ts +354 -0
- package/src/drizzle/oauth-connected-collection.ts +324 -0
- package/src/drizzle/oauth-connected-custody.ts +277 -0
- package/src/drizzle/oauth-connected-drivers.ts +465 -0
- package/src/drizzle/oauth-connected-flow.ts +441 -0
- package/src/drizzle/oauth-connected-input.ts +127 -0
- package/src/drizzle/oauth-connected-maintenance.ts +345 -0
- package/src/drizzle/oauth-connected-management.ts +365 -0
- package/src/drizzle/oauth-connected-model.ts +347 -0
- package/src/drizzle/oauth-connected-reference.ts +112 -0
- package/src/drizzle/oauth-connected-settlement.ts +226 -0
- package/src/drizzle/oauth-connected-state.ts +845 -0
- package/src/drizzle/oauth-connected-target.ts +264 -0
- package/src/drizzle/oauth-drivers.ts +569 -0
- package/src/drizzle/oauth-flow.ts +462 -0
- package/src/drizzle/oauth-input.ts +201 -0
- package/src/drizzle/oauth-model.ts +617 -0
- package/src/drizzle/oauth-mysql.ts +10 -0
- package/src/drizzle/oauth-owner.ts +29 -0
- package/src/drizzle/oauth-registration.ts +640 -0
- package/src/drizzle/oauth-state.ts +215 -0
- package/src/drizzle/oauth-target.ts +750 -0
- package/src/drizzle/passkey-drivers.ts +385 -0
- package/src/drizzle/passkey-kernel.ts +3 -0
- package/src/drizzle/passkey-model.ts +353 -0
- package/src/drizzle/passkey-registration-ceremony-model.ts +69 -0
- package/src/drizzle/passkey-state.ts +3 -0
- package/src/drizzle/passkey-target.ts +23 -0
- package/src/drizzle/passkey-write-drivers.ts +395 -0
- package/src/drizzle/passkey-write-model.ts +271 -0
- package/src/drizzle/passkey-write-target.ts +9 -0
- package/src/drizzle/password-model.ts +491 -0
- package/src/drizzle/password-prepared-model.ts +259 -0
- package/src/drizzle/password-prepared-sql.ts +1191 -0
- package/src/drizzle/password-prepared-state.ts +231 -0
- package/src/drizzle/password-prepared-target.ts +160 -0
- package/src/drizzle/password-registration.ts +374 -0
- package/src/drizzle/password-sql.ts +12 -0
- package/src/drizzle/password-target.ts +224 -0
- package/src/drizzle/pg-emails.ts +321 -0
- package/src/drizzle/pg-password-prepared.ts +205 -0
- package/src/drizzle/pg-passwords.ts +338 -0
- package/src/drizzle/pg-proofs.ts +191 -0
- package/src/drizzle/pg-sessions.ts +750 -0
- package/src/drizzle/pg.ts +984 -0
- package/src/drizzle/phone-model.ts +190 -0
- package/src/drizzle/phone-state.ts +21 -0
- package/src/drizzle/phone-target.ts +472 -0
- package/src/drizzle/proof-model.ts +398 -0
- package/src/drizzle/proof-sql.ts +9 -0
- package/src/drizzle/proof-target.ts +122 -0
- package/src/drizzle/query-operations.ts +37 -0
- package/src/drizzle/session-model.ts +370 -0
- package/src/drizzle/session-sql.ts +13 -0
- package/src/drizzle/session-target.ts +338 -0
- package/src/drizzle/sqlite-emails.ts +366 -0
- package/src/drizzle/sqlite-identity.ts +344 -0
- package/src/drizzle/sqlite-password-prepared.ts +260 -0
- package/src/drizzle/sqlite-passwords.ts +497 -0
- package/src/drizzle/sqlite-proofs.ts +202 -0
- package/src/drizzle/sqlite-sessions.ts +971 -0
- package/src/drizzle/sqlite.ts +705 -0
- package/src/drizzle/standalone-guard.ts +38 -0
- package/src/drizzle/step-up-model.ts +128 -0
- package/src/drizzle/step-up-state.ts +139 -0
- package/src/drizzle/totp-model.ts +92 -0
- package/src/drizzle/totp-state.ts +525 -0
- package/src/drizzle/totp-target.ts +308 -0
- package/src/drizzle/transaction-execution.ts +12 -0
- package/src/drizzle/transaction-mysql.ts +104 -0
- package/src/drizzle/transaction-owner.ts +10 -0
- package/src/index.ts +4 -0
- package/src/internal/configuration.ts +282 -0
- package/src/internal/drizzle-migrations.ts +30 -0
- package/src/internal/drizzle-postgres.ts +81 -0
- package/src/internal/drizzle-sqlite.ts +79 -0
- package/src/internal/email-kernel.ts +1532 -0
- package/src/internal/mapping-error.ts +36 -0
- package/src/internal/passkey/admission.ts +362 -0
- package/src/internal/passkey/credentials.ts +511 -0
- package/src/internal/passkey/enrollment.ts +326 -0
- package/src/internal/passkey/flow.ts +1062 -0
- package/src/internal/passkey/management.ts +422 -0
- package/src/internal/passkey/registration-ceremony.ts +283 -0
- package/src/internal/passkey/registration-custody.ts +91 -0
- package/src/internal/passkey/registration-write.ts +426 -0
- package/src/internal/passkey/state.ts +354 -0
- package/src/internal/passkey/target.ts +586 -0
- package/src/internal/passkey/write-state.ts +681 -0
- package/src/internal/passkey/write-target.ts +452 -0
- package/src/internal/passkey-kernel.ts +102 -0
- package/src/internal/passkeys.ts +597 -0
- package/src/internal/password-kernel.ts +2122 -0
- package/src/internal/persistence.ts +590 -0
- package/src/internal/phone-kernel.ts +843 -0
- package/src/internal/proof-kernel.ts +1949 -0
- package/src/internal/query-operations.ts +26 -0
- package/src/internal/registration.ts +160 -0
- package/src/internal/session-kernel.ts +2210 -0
- package/src/internal/sql-persistence.ts +45 -0
- package/src/internal/sql-query.ts +298 -0
- package/src/internal/sql-table.ts +89 -0
- package/src/internal/storage-mapping.ts +706 -0
- package/src/internal/storage-tables.ts +408 -0
- package/src/internal/storage-validation.ts +117 -0
- package/src/internal/transaction-execution-kernel.ts +317 -0
- package/src/internal/transaction-kernel.ts +582 -0
|
@@ -0,0 +1,984 @@
|
|
|
1
|
+
import { AuthStore, EmailOtpRejected } from "@yielded/auth/AuthStore";
|
|
2
|
+
import { AuthRateLimited, AuthStoreError, InvalidRegistration } from "@yielded/auth/Errors";
|
|
3
|
+
import {
|
|
4
|
+
ExternalIdentityMutation,
|
|
5
|
+
IdentityConflict,
|
|
6
|
+
IdentityUnavailable,
|
|
7
|
+
SubjectProvisioned,
|
|
8
|
+
type ExternalIdentity,
|
|
9
|
+
SubjectProvisioner,
|
|
10
|
+
type SubjectProvisioningInput,
|
|
11
|
+
} from "@yielded/auth/Identity";
|
|
12
|
+
import {
|
|
13
|
+
InvalidOAuthState,
|
|
14
|
+
OAuthStateDecisions,
|
|
15
|
+
OAuthStateStore,
|
|
16
|
+
type OAuthState,
|
|
17
|
+
} from "@yielded/auth/OAuth";
|
|
18
|
+
import { AuthStoreDecisions, reportPersistenceFailure } from "@yielded/auth/Persistence";
|
|
19
|
+
import {
|
|
20
|
+
VerifiedEmail,
|
|
21
|
+
type ConsumeChallenge,
|
|
22
|
+
type ConsumeRegistration,
|
|
23
|
+
type NewChallenge,
|
|
24
|
+
type NewRegistration,
|
|
25
|
+
type SubjectId,
|
|
26
|
+
type TokenDigest,
|
|
27
|
+
} from "@yielded/auth/Schema";
|
|
28
|
+
/* oxlint-disable no-explicit-any -- Drizzle's generic query builders lose the concrete consumer table through a runtime column map. Assertions stay in this adapter. */
|
|
29
|
+
import {
|
|
30
|
+
and,
|
|
31
|
+
eq,
|
|
32
|
+
gt,
|
|
33
|
+
lt,
|
|
34
|
+
lte,
|
|
35
|
+
ne,
|
|
36
|
+
or,
|
|
37
|
+
sql,
|
|
38
|
+
type AnyRelations,
|
|
39
|
+
type InferSelectModel,
|
|
40
|
+
} from "drizzle-orm";
|
|
41
|
+
import type { EffectPgDatabase as PgliteDatabase } from "drizzle-orm/effect-pglite";
|
|
42
|
+
import type { EffectPgDatabase as PgDatabase } from "drizzle-orm/effect-postgres";
|
|
43
|
+
import { type AnyPgTable, type PgColumn } from "drizzle-orm/pg-core";
|
|
44
|
+
import { Cause, Context, DateTime, Duration, Effect, Layer, Schema } from "effect";
|
|
45
|
+
import type { SqlError } from "effect/unstable/sql/SqlError";
|
|
46
|
+
|
|
47
|
+
import {
|
|
48
|
+
column,
|
|
49
|
+
PersistenceMappingError,
|
|
50
|
+
isMappedConstraintConflict,
|
|
51
|
+
provisioningFingerprint,
|
|
52
|
+
requireStandaloneConsume,
|
|
53
|
+
updateValues,
|
|
54
|
+
type AuthTables,
|
|
55
|
+
type AuthStoreTables,
|
|
56
|
+
type ChallengeConsumeDecision,
|
|
57
|
+
type ConsumeDecision,
|
|
58
|
+
type ExternalIdentityTables,
|
|
59
|
+
type IdentityTables,
|
|
60
|
+
type OAuthStateTables,
|
|
61
|
+
type SubjectProvisioningTables,
|
|
62
|
+
} from "./model";
|
|
63
|
+
import type { SuppliedService } from "./SuppliedService";
|
|
64
|
+
|
|
65
|
+
type Database = PgDatabase<AnyRelations> | PgliteDatabase<AnyRelations>;
|
|
66
|
+
type RuntimeDatabase = PgDatabase<any> & Parameters<typeof requireStandaloneConsume>[0];
|
|
67
|
+
type TransactionOf<D extends Database> = Parameters<Parameters<D["transaction"]>[0]>[0];
|
|
68
|
+
|
|
69
|
+
const storeReadError = () => AuthStoreError.make({ message: "Auth store read failed" });
|
|
70
|
+
const storeWriteError = () => AuthStoreError.make({ message: "Auth store write failed" });
|
|
71
|
+
const isStoreFailure = Schema.is(AuthStoreError);
|
|
72
|
+
|
|
73
|
+
const mapStoreFailure =
|
|
74
|
+
(failure: () => AuthStoreError) =>
|
|
75
|
+
<A, E, R>(effect: Effect.Effect<A, E, R>): Effect.Effect<A, AuthStoreError, R> =>
|
|
76
|
+
reportPersistenceFailure(effect, isStoreFailure).pipe(
|
|
77
|
+
Effect.catchCause((cause) => Effect.failCause(Cause.map(cause, failure))),
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const mapStoreReadFailure = mapStoreFailure(storeReadError);
|
|
81
|
+
const mapStoreWriteFailure = mapStoreFailure(storeWriteError);
|
|
82
|
+
const identityUnavailable = () => IdentityUnavailable.make();
|
|
83
|
+
const isIdentityFailure = Schema.is(Schema.Union([IdentityConflict, IdentityUnavailable]));
|
|
84
|
+
|
|
85
|
+
const mapIdentityFailure = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
86
|
+
reportPersistenceFailure(effect, isIdentityFailure).pipe(
|
|
87
|
+
Effect.catchCause((cause) =>
|
|
88
|
+
Effect.failCause(
|
|
89
|
+
Cause.map(cause, (error) => (isIdentityFailure(error) ? error : identityUnavailable())),
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const selected = <T extends AnyPgTable>(row: unknown): InferSelectModel<T> =>
|
|
95
|
+
row as InferSelectModel<T>;
|
|
96
|
+
|
|
97
|
+
export const makePgAuthStoreServices = <C extends AnyPgTable, R extends AnyPgTable>(
|
|
98
|
+
database: Database,
|
|
99
|
+
mapping: AuthStoreTables<C, R>,
|
|
100
|
+
) => {
|
|
101
|
+
const db = database as RuntimeDatabase;
|
|
102
|
+
const challengeTable = mapping.challenge.table;
|
|
103
|
+
const challengeColumns = mapping.challenge.columns;
|
|
104
|
+
|
|
105
|
+
const challenge = {
|
|
106
|
+
challengeId: column(challengeTable, challengeColumns.challengeId),
|
|
107
|
+
tokenDigest: column(challengeTable, challengeColumns.tokenDigest),
|
|
108
|
+
namespace: column(challengeTable, challengeColumns.namespace),
|
|
109
|
+
value: column(challengeTable, challengeColumns.value),
|
|
110
|
+
purpose: column(challengeTable, challengeColumns.purpose),
|
|
111
|
+
otpKeyId: column(challengeTable, challengeColumns.otpKeyId),
|
|
112
|
+
otpDigest: column(challengeTable, challengeColumns.otpDigest),
|
|
113
|
+
issuedAt: column(challengeTable, challengeColumns.issuedAt),
|
|
114
|
+
expiresAt: column(challengeTable, challengeColumns.expiresAt),
|
|
115
|
+
attemptLimit: column(challengeTable, challengeColumns.attemptLimit),
|
|
116
|
+
failedAttempts: column(challengeTable, challengeColumns.failedAttempts),
|
|
117
|
+
consumed: column(challengeTable, challengeColumns.consumed),
|
|
118
|
+
} as const;
|
|
119
|
+
|
|
120
|
+
const pgChallenge = challenge as Record<keyof typeof challenge, PgColumn>;
|
|
121
|
+
const registrationTable = mapping.registration.table;
|
|
122
|
+
const registrationColumns = mapping.registration.columns;
|
|
123
|
+
|
|
124
|
+
const registration = {
|
|
125
|
+
tokenDigest: column(registrationTable, registrationColumns.tokenDigest),
|
|
126
|
+
expiresAt: column(registrationTable, registrationColumns.expiresAt),
|
|
127
|
+
consumed: column(registrationTable, registrationColumns.consumed),
|
|
128
|
+
} as const;
|
|
129
|
+
|
|
130
|
+
const issueChallenge = Effect.fn("DrizzlePgAuthStore.issueChallenge")(function* (
|
|
131
|
+
input: NewChallenge,
|
|
132
|
+
) {
|
|
133
|
+
const now = yield* DateTime.now;
|
|
134
|
+
|
|
135
|
+
const cooldownCutoff = DateTime.mapEpochMillis(
|
|
136
|
+
now,
|
|
137
|
+
(millis) => millis - Duration.toMillis(input.resendCooldown),
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
const rows = yield* Effect.suspend(() => {
|
|
141
|
+
const values = mapping.challenge.encodeInsert(input, {
|
|
142
|
+
namespace: mapping.challenge.identifierNamespace,
|
|
143
|
+
failedAttempts: 0,
|
|
144
|
+
consumed: false,
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const nativeNow = mapping.challenge.encodeInstant(now);
|
|
148
|
+
const nativeCutoff = mapping.challenge.encodeInstant(cooldownCutoff);
|
|
149
|
+
const nativeIssuedAt = mapping.challenge.encodeInstant(input.issuedAt);
|
|
150
|
+
const preservedAttempts = sql`case when ${gt(pgChallenge.expiresAt, nativeNow)} then ${pgChallenge.failedAttempts} else 0 end`;
|
|
151
|
+
|
|
152
|
+
const update = mapping.challenge.encodeUpdate(input, {
|
|
153
|
+
namespace: mapping.challenge.identifierNamespace,
|
|
154
|
+
failedAttempts: preservedAttempts as any,
|
|
155
|
+
consumed: false,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return db
|
|
159
|
+
.insert(challengeTable as any)
|
|
160
|
+
.values(values as any)
|
|
161
|
+
.onConflictDoUpdate({
|
|
162
|
+
target: [pgChallenge.namespace, pgChallenge.value, pgChallenge.purpose],
|
|
163
|
+
set: update as any,
|
|
164
|
+
setWhere: and(
|
|
165
|
+
ne(pgChallenge.challengeId, input.challengeId),
|
|
166
|
+
lt(pgChallenge.issuedAt, nativeIssuedAt),
|
|
167
|
+
or(eq(pgChallenge.consumed, true), lte(pgChallenge.issuedAt, nativeCutoff)),
|
|
168
|
+
),
|
|
169
|
+
})
|
|
170
|
+
.returning();
|
|
171
|
+
}).pipe(mapStoreWriteFailure);
|
|
172
|
+
|
|
173
|
+
if (rows.length > 0) return;
|
|
174
|
+
|
|
175
|
+
const existing = yield* Effect.suspend(() =>
|
|
176
|
+
db
|
|
177
|
+
.select()
|
|
178
|
+
.from(challengeTable as any)
|
|
179
|
+
.where(
|
|
180
|
+
and(
|
|
181
|
+
eq(pgChallenge.namespace, mapping.challenge.identifierNamespace),
|
|
182
|
+
eq(pgChallenge.value, input.email),
|
|
183
|
+
eq(pgChallenge.purpose, input.purpose),
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
.limit(1),
|
|
187
|
+
).pipe(mapStoreReadFailure);
|
|
188
|
+
|
|
189
|
+
const decoded =
|
|
190
|
+
existing[0] === undefined
|
|
191
|
+
? undefined
|
|
192
|
+
: yield* Effect.suspend(() => mapping.challenge.decode(selected<C>(existing[0]))).pipe(
|
|
193
|
+
mapStoreReadFailure,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const retryAfterSeconds =
|
|
197
|
+
decoded === undefined
|
|
198
|
+
? Math.max(1, Math.ceil(Duration.toMillis(input.resendCooldown) / 1000))
|
|
199
|
+
: Math.max(
|
|
200
|
+
1,
|
|
201
|
+
Math.ceil(
|
|
202
|
+
(DateTime.toEpochMillis(decoded.challenge.issuedAt) +
|
|
203
|
+
Duration.toMillis(input.resendCooldown) -
|
|
204
|
+
DateTime.toEpochMillis(now)) /
|
|
205
|
+
1000,
|
|
206
|
+
),
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return yield* AuthRateLimited.make({ retryAfterSeconds });
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const consumeChallengeDecision = Effect.fn("DrizzlePgAuthStore.consumeChallengeDecision")(
|
|
213
|
+
function* (
|
|
214
|
+
input: ConsumeChallenge,
|
|
215
|
+
): Effect.fn.Return<ChallengeConsumeDecision, AuthStoreError> {
|
|
216
|
+
const now = yield* DateTime.now;
|
|
217
|
+
|
|
218
|
+
const alternatives = Object.entries(input.otpDigests).map(([keyId, digest]) =>
|
|
219
|
+
and(eq(pgChallenge.otpKeyId, keyId), eq(pgChallenge.otpDigest, digest)),
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const matches = alternatives.length === 0 ? sql<boolean>`false` : or(...alternatives)!;
|
|
223
|
+
|
|
224
|
+
const rows = yield* Effect.suspend(() =>
|
|
225
|
+
db
|
|
226
|
+
.update(challengeTable as any)
|
|
227
|
+
.set(
|
|
228
|
+
updateValues<C>([
|
|
229
|
+
[
|
|
230
|
+
challengeColumns.consumed,
|
|
231
|
+
sql`case when ${matches} then true else ${pgChallenge.consumed} end`,
|
|
232
|
+
],
|
|
233
|
+
[
|
|
234
|
+
challengeColumns.failedAttempts,
|
|
235
|
+
sql`${pgChallenge.failedAttempts} + case when ${matches} then 0 else 1 end`,
|
|
236
|
+
],
|
|
237
|
+
]) as any,
|
|
238
|
+
)
|
|
239
|
+
.where(
|
|
240
|
+
and(
|
|
241
|
+
eq(pgChallenge.tokenDigest, input.tokenDigest),
|
|
242
|
+
eq(pgChallenge.consumed, false),
|
|
243
|
+
gt(pgChallenge.expiresAt, mapping.challenge.encodeInstant(now)),
|
|
244
|
+
sql`${pgChallenge.failedAttempts} < ${pgChallenge.attemptLimit}`,
|
|
245
|
+
),
|
|
246
|
+
)
|
|
247
|
+
.returning(),
|
|
248
|
+
).pipe(mapStoreWriteFailure);
|
|
249
|
+
|
|
250
|
+
if (rows[0] === undefined) {
|
|
251
|
+
return { _tag: "rejected", reason: "missing_challenge" };
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const decoded = yield* Effect.suspend(() =>
|
|
255
|
+
mapping.challenge.decode(selected<C>(rows[0])),
|
|
256
|
+
).pipe(mapStoreReadFailure);
|
|
257
|
+
|
|
258
|
+
if (!decoded.consumed) return { _tag: "rejected", reason: "bad_code" };
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
_tag: "accepted",
|
|
262
|
+
value: VerifiedEmail.make({
|
|
263
|
+
email: decoded.challenge.email,
|
|
264
|
+
purpose: decoded.challenge.purpose,
|
|
265
|
+
}),
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const consumeChallenge = Effect.fn("DrizzlePgAuthStore.consumeChallenge")(function* (
|
|
271
|
+
input: ConsumeChallenge,
|
|
272
|
+
) {
|
|
273
|
+
yield* requireStandaloneConsume(db);
|
|
274
|
+
const decision = yield* consumeChallengeDecision(input);
|
|
275
|
+
|
|
276
|
+
return decision._tag === "accepted"
|
|
277
|
+
? decision.value
|
|
278
|
+
: yield* EmailOtpRejected.make({ reason: decision.reason });
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const issueRegistration = Effect.fn("DrizzlePgAuthStore.issueRegistration")(function* (
|
|
282
|
+
input: NewRegistration,
|
|
283
|
+
) {
|
|
284
|
+
yield* Effect.suspend(() =>
|
|
285
|
+
db
|
|
286
|
+
.insert(registrationTable as any)
|
|
287
|
+
.values(mapping.registration.encodeInsert(input, false) as any),
|
|
288
|
+
).pipe(mapStoreWriteFailure);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const inspectRegistration = Effect.fn("DrizzlePgAuthStore.inspectRegistration")(function* (
|
|
292
|
+
tokenDigest: TokenDigest,
|
|
293
|
+
) {
|
|
294
|
+
const now = yield* DateTime.now;
|
|
295
|
+
|
|
296
|
+
const rows = yield* Effect.suspend(() =>
|
|
297
|
+
db
|
|
298
|
+
.select()
|
|
299
|
+
.from(registrationTable as any)
|
|
300
|
+
.where(
|
|
301
|
+
and(
|
|
302
|
+
eq(registration.tokenDigest, tokenDigest),
|
|
303
|
+
eq(registration.consumed, false),
|
|
304
|
+
gt(registration.expiresAt, mapping.registration.encodeInstant(now)),
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
.limit(1),
|
|
308
|
+
).pipe(mapStoreReadFailure);
|
|
309
|
+
|
|
310
|
+
if (rows[0] === undefined) return yield* InvalidRegistration.make();
|
|
311
|
+
|
|
312
|
+
const decoded = yield* Effect.suspend(() =>
|
|
313
|
+
mapping.registration.decode(selected<R>(rows[0])),
|
|
314
|
+
).pipe(mapStoreReadFailure);
|
|
315
|
+
|
|
316
|
+
return decoded.registration;
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
const consumeRegistrationDecision = Effect.fn("DrizzlePgAuthStore.consumeRegistrationDecision")(
|
|
320
|
+
function* (
|
|
321
|
+
input: ConsumeRegistration,
|
|
322
|
+
): Effect.fn.Return<ConsumeDecision<void>, AuthStoreError> {
|
|
323
|
+
const now = yield* DateTime.now;
|
|
324
|
+
|
|
325
|
+
const rows = yield* Effect.suspend(() =>
|
|
326
|
+
db
|
|
327
|
+
.update(registrationTable as any)
|
|
328
|
+
.set(updateValues<R>([[registrationColumns.consumed, true]]) as any)
|
|
329
|
+
.where(
|
|
330
|
+
and(
|
|
331
|
+
eq(registration.tokenDigest, input.tokenDigest),
|
|
332
|
+
eq(registration.consumed, false),
|
|
333
|
+
gt(registration.expiresAt, mapping.registration.encodeInstant(now)),
|
|
334
|
+
),
|
|
335
|
+
)
|
|
336
|
+
.returning({ consumed: registration.consumed as PgColumn }),
|
|
337
|
+
).pipe(mapStoreWriteFailure);
|
|
338
|
+
|
|
339
|
+
return rows.length === 0 ? { _tag: "rejected" } : { _tag: "accepted", value: undefined };
|
|
340
|
+
},
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const consumeRegistration = Effect.fn("DrizzlePgAuthStore.consumeRegistration")(function* (
|
|
344
|
+
input: ConsumeRegistration,
|
|
345
|
+
) {
|
|
346
|
+
yield* requireStandaloneConsume(db);
|
|
347
|
+
const decision = yield* consumeRegistrationDecision(input);
|
|
348
|
+
|
|
349
|
+
if (decision._tag === "rejected") return yield* InvalidRegistration.make();
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
authStore: AuthStore.of({
|
|
354
|
+
issueChallenge,
|
|
355
|
+
consumeChallenge,
|
|
356
|
+
issueRegistration,
|
|
357
|
+
inspectRegistration,
|
|
358
|
+
consumeRegistration,
|
|
359
|
+
}),
|
|
360
|
+
decisions: {
|
|
361
|
+
consumeChallenge: consumeChallengeDecision,
|
|
362
|
+
consumeRegistration: consumeRegistrationDecision,
|
|
363
|
+
},
|
|
364
|
+
} as const;
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
export const makePgOAuthStateServices = <S extends AnyPgTable>(
|
|
368
|
+
database: Database,
|
|
369
|
+
mapping: OAuthStateTables<S>,
|
|
370
|
+
) => {
|
|
371
|
+
const db = database as RuntimeDatabase;
|
|
372
|
+
const stateTable = mapping.oauthState.table;
|
|
373
|
+
const stateColumns = mapping.oauthState.columns;
|
|
374
|
+
|
|
375
|
+
const state = {
|
|
376
|
+
stateDigest: column(stateTable, stateColumns.stateDigest),
|
|
377
|
+
expiresAt: column(stateTable, stateColumns.expiresAt),
|
|
378
|
+
consumed: column(stateTable, stateColumns.consumed),
|
|
379
|
+
} as const;
|
|
380
|
+
|
|
381
|
+
const issue = Effect.fn("DrizzlePgOAuthStateStore.issue")(function* (input: OAuthState) {
|
|
382
|
+
yield* Effect.suspend(() =>
|
|
383
|
+
db.insert(stateTable as any).values(mapping.oauthState.encodeInsert(input, false) as any),
|
|
384
|
+
).pipe(mapStoreWriteFailure);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const consumeDecision = Effect.fn("DrizzlePgOAuthStateStore.consumeDecision")(function* (
|
|
388
|
+
digest: TokenDigest,
|
|
389
|
+
): Effect.fn.Return<ConsumeDecision<OAuthState>, AuthStoreError> {
|
|
390
|
+
const now = yield* DateTime.now;
|
|
391
|
+
|
|
392
|
+
const rows = yield* Effect.suspend(() =>
|
|
393
|
+
db
|
|
394
|
+
.update(stateTable as any)
|
|
395
|
+
.set(updateValues<S>([[stateColumns.consumed, true]]) as any)
|
|
396
|
+
.where(
|
|
397
|
+
and(
|
|
398
|
+
eq(state.stateDigest, digest),
|
|
399
|
+
eq(state.consumed, false),
|
|
400
|
+
gt(state.expiresAt, mapping.oauthState.encodeInstant(now)),
|
|
401
|
+
),
|
|
402
|
+
)
|
|
403
|
+
.returning(),
|
|
404
|
+
).pipe(mapStoreWriteFailure);
|
|
405
|
+
|
|
406
|
+
if (rows[0] === undefined) return { _tag: "rejected" };
|
|
407
|
+
|
|
408
|
+
const decoded = yield* Effect.suspend(() =>
|
|
409
|
+
mapping.oauthState.decode(selected<S>(rows[0])),
|
|
410
|
+
).pipe(mapStoreReadFailure);
|
|
411
|
+
|
|
412
|
+
return { _tag: "accepted", value: decoded.state };
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const consume = Effect.fn("DrizzlePgOAuthStateStore.consume")(function* (digest: TokenDigest) {
|
|
416
|
+
yield* requireStandaloneConsume(db);
|
|
417
|
+
const decision = yield* consumeDecision(digest);
|
|
418
|
+
|
|
419
|
+
return decision._tag === "accepted" ? decision.value : yield* InvalidOAuthState.make();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
oauthStateStore: OAuthStateStore.of({ issue, consume }),
|
|
424
|
+
decisions: { consumeOAuthState: consumeDecision },
|
|
425
|
+
} as const;
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
export const makePgAuthServices = <
|
|
429
|
+
C extends AnyPgTable,
|
|
430
|
+
R extends AnyPgTable,
|
|
431
|
+
S extends AnyPgTable,
|
|
432
|
+
>(
|
|
433
|
+
database: Database,
|
|
434
|
+
mapping: AuthTables<C, R, S>,
|
|
435
|
+
) => {
|
|
436
|
+
const auth = makePgAuthStoreServices(database, mapping);
|
|
437
|
+
const oauth = makePgOAuthStateServices(database, mapping);
|
|
438
|
+
|
|
439
|
+
return { ...auth, ...oauth, decisions: { ...auth.decisions, ...oauth.decisions } } as const;
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Owns the native outer commit. Keep rejection as a decision value inside the
|
|
444
|
+
* owner; interruption before commit rolls back. Retry an unknown commit outcome
|
|
445
|
+
* only with the same stable request identity.
|
|
446
|
+
*/
|
|
447
|
+
export function coordinatePgAuthStoreTransaction<
|
|
448
|
+
D extends Database,
|
|
449
|
+
C extends AnyPgTable,
|
|
450
|
+
R extends AnyPgTable,
|
|
451
|
+
A,
|
|
452
|
+
E,
|
|
453
|
+
Requirements,
|
|
454
|
+
DatabaseError,
|
|
455
|
+
DatabaseRequirements,
|
|
456
|
+
>(
|
|
457
|
+
acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>,
|
|
458
|
+
options: { readonly mapping: AuthStoreTables<C, R>; readonly transaction?: never },
|
|
459
|
+
body: Effect.Effect<A, E, Requirements>,
|
|
460
|
+
): Effect.Effect<
|
|
461
|
+
A,
|
|
462
|
+
E | DatabaseError | SqlError,
|
|
463
|
+
Exclude<Requirements, AuthStoreDecisions> | DatabaseRequirements
|
|
464
|
+
>;
|
|
465
|
+
|
|
466
|
+
export function coordinatePgAuthStoreTransaction<
|
|
467
|
+
D extends Database,
|
|
468
|
+
C extends AnyPgTable,
|
|
469
|
+
R extends AnyPgTable,
|
|
470
|
+
A,
|
|
471
|
+
E,
|
|
472
|
+
Requirements,
|
|
473
|
+
DatabaseError,
|
|
474
|
+
DatabaseRequirements,
|
|
475
|
+
TxId,
|
|
476
|
+
TxShape,
|
|
477
|
+
>(
|
|
478
|
+
acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>,
|
|
479
|
+
options: {
|
|
480
|
+
readonly mapping: AuthStoreTables<C, R>;
|
|
481
|
+
readonly transaction: SuppliedService<TxId, NoInfer<TransactionOf<D>>, TxShape>;
|
|
482
|
+
},
|
|
483
|
+
body: Effect.Effect<A, E, Requirements>,
|
|
484
|
+
): Effect.Effect<
|
|
485
|
+
A,
|
|
486
|
+
E | DatabaseError | SqlError,
|
|
487
|
+
Exclude<Requirements, AuthStoreDecisions | TxId> | DatabaseRequirements
|
|
488
|
+
>;
|
|
489
|
+
|
|
490
|
+
export function coordinatePgAuthStoreTransaction<
|
|
491
|
+
D extends Database,
|
|
492
|
+
C extends AnyPgTable,
|
|
493
|
+
R extends AnyPgTable,
|
|
494
|
+
A,
|
|
495
|
+
E,
|
|
496
|
+
Requirements,
|
|
497
|
+
DatabaseError,
|
|
498
|
+
DatabaseRequirements,
|
|
499
|
+
TxId,
|
|
500
|
+
TxShape,
|
|
501
|
+
>(
|
|
502
|
+
acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>,
|
|
503
|
+
options: {
|
|
504
|
+
readonly mapping: AuthStoreTables<C, R>;
|
|
505
|
+
readonly transaction?: SuppliedService<TxId, NoInfer<TransactionOf<D>>, TxShape>;
|
|
506
|
+
},
|
|
507
|
+
body: Effect.Effect<A, E, Requirements>,
|
|
508
|
+
) {
|
|
509
|
+
return Effect.flatMap(acquire, (database) =>
|
|
510
|
+
database.transaction<A, E, Exclude<Requirements, AuthStoreDecisions>>((transaction) => {
|
|
511
|
+
const decisions = makePgAuthStoreServices(transaction as never, options.mapping).decisions;
|
|
512
|
+
const provided = Context.make(AuthStoreDecisions, decisions);
|
|
513
|
+
const work = Effect.provideContext(body, provided);
|
|
514
|
+
|
|
515
|
+
return options.transaction === undefined
|
|
516
|
+
? work
|
|
517
|
+
: Effect.provideService(
|
|
518
|
+
work,
|
|
519
|
+
options.transaction,
|
|
520
|
+
options.transaction.of(transaction as TransactionOf<D>),
|
|
521
|
+
);
|
|
522
|
+
}),
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function coordinatePgOAuthStateTransaction<
|
|
527
|
+
D extends Database,
|
|
528
|
+
S extends AnyPgTable,
|
|
529
|
+
A,
|
|
530
|
+
E,
|
|
531
|
+
Requirements,
|
|
532
|
+
DatabaseError,
|
|
533
|
+
DatabaseRequirements,
|
|
534
|
+
>(
|
|
535
|
+
acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>,
|
|
536
|
+
options: { readonly mapping: OAuthStateTables<S>; readonly transaction?: never },
|
|
537
|
+
body: Effect.Effect<A, E, Requirements>,
|
|
538
|
+
): Effect.Effect<
|
|
539
|
+
A,
|
|
540
|
+
E | DatabaseError | SqlError,
|
|
541
|
+
Exclude<Requirements, OAuthStateDecisions> | DatabaseRequirements
|
|
542
|
+
>;
|
|
543
|
+
|
|
544
|
+
export function coordinatePgOAuthStateTransaction<
|
|
545
|
+
D extends Database,
|
|
546
|
+
S extends AnyPgTable,
|
|
547
|
+
A,
|
|
548
|
+
E,
|
|
549
|
+
Requirements,
|
|
550
|
+
DatabaseError,
|
|
551
|
+
DatabaseRequirements,
|
|
552
|
+
TxId,
|
|
553
|
+
TxShape,
|
|
554
|
+
>(
|
|
555
|
+
acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>,
|
|
556
|
+
options: {
|
|
557
|
+
readonly mapping: OAuthStateTables<S>;
|
|
558
|
+
readonly transaction: SuppliedService<TxId, NoInfer<TransactionOf<D>>, TxShape>;
|
|
559
|
+
},
|
|
560
|
+
body: Effect.Effect<A, E, Requirements>,
|
|
561
|
+
): Effect.Effect<
|
|
562
|
+
A,
|
|
563
|
+
E | DatabaseError | SqlError,
|
|
564
|
+
Exclude<Requirements, OAuthStateDecisions | TxId> | DatabaseRequirements
|
|
565
|
+
>;
|
|
566
|
+
|
|
567
|
+
export function coordinatePgOAuthStateTransaction<
|
|
568
|
+
D extends Database,
|
|
569
|
+
S extends AnyPgTable,
|
|
570
|
+
A,
|
|
571
|
+
E,
|
|
572
|
+
Requirements,
|
|
573
|
+
DatabaseError,
|
|
574
|
+
DatabaseRequirements,
|
|
575
|
+
TxId,
|
|
576
|
+
TxShape,
|
|
577
|
+
>(
|
|
578
|
+
acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>,
|
|
579
|
+
options: {
|
|
580
|
+
readonly mapping: OAuthStateTables<S>;
|
|
581
|
+
readonly transaction?: SuppliedService<TxId, NoInfer<TransactionOf<D>>, TxShape>;
|
|
582
|
+
},
|
|
583
|
+
body: Effect.Effect<A, E, Requirements>,
|
|
584
|
+
) {
|
|
585
|
+
return Effect.flatMap(acquire, (database) =>
|
|
586
|
+
database.transaction<A, E, Exclude<Requirements, OAuthStateDecisions>>((transaction) => {
|
|
587
|
+
const decisions = makePgOAuthStateServices(transaction as never, options.mapping).decisions;
|
|
588
|
+
const provided = Context.make(OAuthStateDecisions, decisions);
|
|
589
|
+
const work = Effect.provideContext(body, provided);
|
|
590
|
+
|
|
591
|
+
return options.transaction === undefined
|
|
592
|
+
? work
|
|
593
|
+
: Effect.provideService(
|
|
594
|
+
work,
|
|
595
|
+
options.transaction,
|
|
596
|
+
options.transaction.of(transaction as TransactionOf<D>),
|
|
597
|
+
);
|
|
598
|
+
}),
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
export function coordinatePgAuthTransaction<
|
|
603
|
+
D extends Database,
|
|
604
|
+
C extends AnyPgTable,
|
|
605
|
+
R extends AnyPgTable,
|
|
606
|
+
S extends AnyPgTable,
|
|
607
|
+
A,
|
|
608
|
+
E,
|
|
609
|
+
Requirements,
|
|
610
|
+
DatabaseError,
|
|
611
|
+
DatabaseRequirements,
|
|
612
|
+
>(
|
|
613
|
+
acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>,
|
|
614
|
+
options: { readonly mapping: AuthTables<C, R, S>; readonly transaction?: never },
|
|
615
|
+
body: Effect.Effect<A, E, Requirements>,
|
|
616
|
+
): Effect.Effect<
|
|
617
|
+
A,
|
|
618
|
+
E | DatabaseError | SqlError,
|
|
619
|
+
Exclude<Requirements, AuthStoreDecisions | OAuthStateDecisions> | DatabaseRequirements
|
|
620
|
+
>;
|
|
621
|
+
|
|
622
|
+
export function coordinatePgAuthTransaction<
|
|
623
|
+
D extends Database,
|
|
624
|
+
C extends AnyPgTable,
|
|
625
|
+
R extends AnyPgTable,
|
|
626
|
+
S extends AnyPgTable,
|
|
627
|
+
A,
|
|
628
|
+
E,
|
|
629
|
+
Requirements,
|
|
630
|
+
DatabaseError,
|
|
631
|
+
DatabaseRequirements,
|
|
632
|
+
TxId,
|
|
633
|
+
TxShape,
|
|
634
|
+
>(
|
|
635
|
+
acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>,
|
|
636
|
+
options: {
|
|
637
|
+
readonly mapping: AuthTables<C, R, S>;
|
|
638
|
+
readonly transaction: SuppliedService<TxId, NoInfer<TransactionOf<D>>, TxShape>;
|
|
639
|
+
},
|
|
640
|
+
body: Effect.Effect<A, E, Requirements>,
|
|
641
|
+
): Effect.Effect<
|
|
642
|
+
A,
|
|
643
|
+
E | DatabaseError | SqlError,
|
|
644
|
+
Exclude<Requirements, AuthStoreDecisions | OAuthStateDecisions | TxId> | DatabaseRequirements
|
|
645
|
+
>;
|
|
646
|
+
|
|
647
|
+
export function coordinatePgAuthTransaction<
|
|
648
|
+
D extends Database,
|
|
649
|
+
C extends AnyPgTable,
|
|
650
|
+
R extends AnyPgTable,
|
|
651
|
+
S extends AnyPgTable,
|
|
652
|
+
A,
|
|
653
|
+
E,
|
|
654
|
+
Requirements,
|
|
655
|
+
DatabaseError,
|
|
656
|
+
DatabaseRequirements,
|
|
657
|
+
TxId,
|
|
658
|
+
TxShape,
|
|
659
|
+
>(
|
|
660
|
+
acquire: Effect.Effect<D, DatabaseError, DatabaseRequirements>,
|
|
661
|
+
options: {
|
|
662
|
+
readonly mapping: AuthTables<C, R, S>;
|
|
663
|
+
readonly transaction?: SuppliedService<TxId, NoInfer<TransactionOf<D>>, TxShape>;
|
|
664
|
+
},
|
|
665
|
+
body: Effect.Effect<A, E, Requirements>,
|
|
666
|
+
) {
|
|
667
|
+
return Effect.flatMap(acquire, (database) =>
|
|
668
|
+
database.transaction<A, E, Exclude<Requirements, AuthStoreDecisions | OAuthStateDecisions>>(
|
|
669
|
+
(transaction) => {
|
|
670
|
+
const decisions = makePgAuthServices(transaction as never, options.mapping).decisions;
|
|
671
|
+
|
|
672
|
+
const provided = Context.make(AuthStoreDecisions, decisions).pipe(
|
|
673
|
+
Context.add(OAuthStateDecisions, decisions),
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
const work = Effect.provideContext(body, provided);
|
|
677
|
+
|
|
678
|
+
return options.transaction === undefined
|
|
679
|
+
? work
|
|
680
|
+
: Effect.provideService(
|
|
681
|
+
work,
|
|
682
|
+
options.transaction,
|
|
683
|
+
options.transaction.of(transaction as TransactionOf<D>),
|
|
684
|
+
);
|
|
685
|
+
},
|
|
686
|
+
),
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
export const makePgSubjectProvisioningServices = <
|
|
691
|
+
Subject extends AnyPgTable,
|
|
692
|
+
Identifier extends AnyPgTable,
|
|
693
|
+
Request extends AnyPgTable,
|
|
694
|
+
NativeId,
|
|
695
|
+
>(
|
|
696
|
+
database: Database,
|
|
697
|
+
mapping: SubjectProvisioningTables<Subject, Identifier, Request, NativeId>,
|
|
698
|
+
) => {
|
|
699
|
+
const db = database as RuntimeDatabase;
|
|
700
|
+
const requestTable = mapping.provisioningRequest.table;
|
|
701
|
+
const requestIdColumn = column(requestTable, mapping.provisioningRequest.requestId);
|
|
702
|
+
const requestFingerprintColumn = column(requestTable, mapping.provisioningRequest.fingerprint);
|
|
703
|
+
const requestSubjectColumn = column(requestTable, mapping.provisioningRequest.subjectId);
|
|
704
|
+
const identifierTable = mapping.identifier.table;
|
|
705
|
+
const identifierNamespaceColumn = column(identifierTable, mapping.identifier.namespace);
|
|
706
|
+
const identifierValueColumn = column(identifierTable, mapping.identifier.value);
|
|
707
|
+
|
|
708
|
+
const findReceipt = Effect.fn("DrizzlePgIdentity.findReceipt")(function* (requestId: string) {
|
|
709
|
+
const rows = yield* db
|
|
710
|
+
.select({
|
|
711
|
+
fingerprint: requestFingerprintColumn as PgColumn,
|
|
712
|
+
subjectId: requestSubjectColumn as PgColumn,
|
|
713
|
+
})
|
|
714
|
+
.from(requestTable as any)
|
|
715
|
+
.where(eq(requestIdColumn, requestId))
|
|
716
|
+
.limit(1);
|
|
717
|
+
|
|
718
|
+
return rows[0];
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
const provision = Effect.fn("DrizzlePgIdentity.provision")(function* (
|
|
722
|
+
input: SubjectProvisioningInput,
|
|
723
|
+
) {
|
|
724
|
+
const fingerprint = provisioningFingerprint(input);
|
|
725
|
+
const existing = yield* findReceipt(input.requestId);
|
|
726
|
+
|
|
727
|
+
if (existing !== undefined) {
|
|
728
|
+
if (existing.fingerprint !== fingerprint) return yield* IdentityConflict.make();
|
|
729
|
+
const id = yield* mapping.subjectId.toSubject(existing.subjectId as NativeId);
|
|
730
|
+
|
|
731
|
+
return SubjectProvisioned.make({ subjectId: id });
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
const attempt = db.transaction((tx) =>
|
|
735
|
+
Effect.gen(function* () {
|
|
736
|
+
const allocated =
|
|
737
|
+
mapping.subject.allocateId === undefined ? undefined : yield* mapping.subject.allocateId;
|
|
738
|
+
|
|
739
|
+
const inserted = yield* tx
|
|
740
|
+
.insert(mapping.subject.table as any)
|
|
741
|
+
.values(mapping.subject.encodeInsert(input, allocated) as any)
|
|
742
|
+
.returning({ id: column(mapping.subject.table, mapping.subject.id) as PgColumn });
|
|
743
|
+
|
|
744
|
+
const nativeId = (inserted[0]?.id ?? allocated) as NativeId | undefined;
|
|
745
|
+
|
|
746
|
+
if (nativeId === undefined) {
|
|
747
|
+
return yield* PersistenceMappingError.make({
|
|
748
|
+
operation: "provisionSubject.generatedId",
|
|
749
|
+
cause: new Error("subject insert did not return its native id"),
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
if (input.identifier !== undefined) {
|
|
753
|
+
yield* tx
|
|
754
|
+
.insert(identifierTable as any)
|
|
755
|
+
.values(
|
|
756
|
+
mapping.identifier.encodeInsert(input.identifier, nativeId, input.verifiedAt) as any,
|
|
757
|
+
);
|
|
758
|
+
}
|
|
759
|
+
yield* tx
|
|
760
|
+
.insert(requestTable as any)
|
|
761
|
+
.values(
|
|
762
|
+
mapping.provisioningRequest.encodeInsert(input.requestId, fingerprint, nativeId) as any,
|
|
763
|
+
);
|
|
764
|
+
|
|
765
|
+
return nativeId;
|
|
766
|
+
}),
|
|
767
|
+
);
|
|
768
|
+
|
|
769
|
+
const nativeId = yield* attempt.pipe(
|
|
770
|
+
Effect.catchCause(
|
|
771
|
+
(
|
|
772
|
+
cause,
|
|
773
|
+
): Effect.Effect<
|
|
774
|
+
NativeId,
|
|
775
|
+
| Effect.Error<typeof attempt>
|
|
776
|
+
| Effect.Error<ReturnType<typeof findReceipt>>
|
|
777
|
+
| IdentityConflict
|
|
778
|
+
> => {
|
|
779
|
+
if (
|
|
780
|
+
cause.reasons.length === 0 ||
|
|
781
|
+
!cause.reasons.every(
|
|
782
|
+
(reason) =>
|
|
783
|
+
Cause.isFailReason(reason) &&
|
|
784
|
+
isMappedConstraintConflict(mapping.isConstraintConflict, reason.error),
|
|
785
|
+
)
|
|
786
|
+
)
|
|
787
|
+
return Effect.failCause(cause);
|
|
788
|
+
|
|
789
|
+
return findReceipt(input.requestId).pipe(
|
|
790
|
+
Effect.flatMap((receipt) => {
|
|
791
|
+
if (receipt !== undefined && receipt.fingerprint === fingerprint) {
|
|
792
|
+
return Effect.succeed(receipt.subjectId as NativeId);
|
|
793
|
+
}
|
|
794
|
+
if (input.identifier === undefined) return IdentityConflict.make();
|
|
795
|
+
|
|
796
|
+
return db
|
|
797
|
+
.select({
|
|
798
|
+
subjectId: column(identifierTable, mapping.identifier.subjectId) as PgColumn,
|
|
799
|
+
})
|
|
800
|
+
.from(identifierTable as any)
|
|
801
|
+
.where(
|
|
802
|
+
and(
|
|
803
|
+
eq(identifierNamespaceColumn, input.identifier.namespace),
|
|
804
|
+
eq(identifierValueColumn, input.identifier.value),
|
|
805
|
+
),
|
|
806
|
+
)
|
|
807
|
+
.limit(1)
|
|
808
|
+
.pipe(Effect.flatMap(() => IdentityConflict.make()));
|
|
809
|
+
}),
|
|
810
|
+
);
|
|
811
|
+
},
|
|
812
|
+
),
|
|
813
|
+
);
|
|
814
|
+
|
|
815
|
+
const subjectId = yield* mapping.subjectId.toSubject(nativeId);
|
|
816
|
+
|
|
817
|
+
return SubjectProvisioned.make({ subjectId });
|
|
818
|
+
}, mapIdentityFailure);
|
|
819
|
+
|
|
820
|
+
return {
|
|
821
|
+
subjectProvisioner: SubjectProvisioner.of({ provision }),
|
|
822
|
+
} as const;
|
|
823
|
+
};
|
|
824
|
+
|
|
825
|
+
export const makePgExternalIdentityServices = <
|
|
826
|
+
Subject extends AnyPgTable,
|
|
827
|
+
External extends AnyPgTable,
|
|
828
|
+
NativeId,
|
|
829
|
+
>(
|
|
830
|
+
database: Database,
|
|
831
|
+
mapping: ExternalIdentityTables<Subject, External, NativeId>,
|
|
832
|
+
) => {
|
|
833
|
+
const db = database as RuntimeDatabase;
|
|
834
|
+
const externalTable = mapping.externalIdentity.table;
|
|
835
|
+
|
|
836
|
+
const externalProviderColumn = column(
|
|
837
|
+
externalTable,
|
|
838
|
+
mapping.externalIdentity.provider,
|
|
839
|
+
) as PgColumn;
|
|
840
|
+
|
|
841
|
+
const externalIssuerColumn = column(externalTable, mapping.externalIdentity.issuer) as PgColumn;
|
|
842
|
+
const externalSubjectColumn = column(externalTable, mapping.externalIdentity.subject) as PgColumn;
|
|
843
|
+
|
|
844
|
+
const externalSubjectIdColumn = column(
|
|
845
|
+
externalTable,
|
|
846
|
+
mapping.externalIdentity.subjectId,
|
|
847
|
+
) as PgColumn;
|
|
848
|
+
|
|
849
|
+
const subjectIdColumn = column(mapping.subject.table, mapping.subject.id) as PgColumn;
|
|
850
|
+
const subjectStatusColumn = column(mapping.subject.table, mapping.subject.status) as PgColumn;
|
|
851
|
+
|
|
852
|
+
const bind = Effect.fn("DrizzlePgIdentity.bindExternalIdentity")(function* (
|
|
853
|
+
subjectId: SubjectId,
|
|
854
|
+
identity: ExternalIdentity,
|
|
855
|
+
) {
|
|
856
|
+
const nativeId = yield* mapping.subjectId.toNative(subjectId);
|
|
857
|
+
|
|
858
|
+
const decision = yield* db
|
|
859
|
+
.transaction((tx) =>
|
|
860
|
+
Effect.gen(function* () {
|
|
861
|
+
const subjects = yield* tx
|
|
862
|
+
.select({ status: subjectStatusColumn })
|
|
863
|
+
.from(mapping.subject.table as any)
|
|
864
|
+
.where(eq(subjectIdColumn, nativeId))
|
|
865
|
+
.for("update")
|
|
866
|
+
.limit(1);
|
|
867
|
+
|
|
868
|
+
if (subjects[0] === undefined || !mapping.subject.isActiveStatus(subjects[0].status))
|
|
869
|
+
return { _tag: "conflict" } as const;
|
|
870
|
+
|
|
871
|
+
const links = yield* tx
|
|
872
|
+
.select({ subjectId: externalSubjectIdColumn })
|
|
873
|
+
.from(externalTable as any)
|
|
874
|
+
.where(
|
|
875
|
+
and(
|
|
876
|
+
eq(externalProviderColumn, identity.provider),
|
|
877
|
+
eq(externalIssuerColumn, identity.issuer),
|
|
878
|
+
eq(externalSubjectColumn, identity.subject),
|
|
879
|
+
),
|
|
880
|
+
)
|
|
881
|
+
.for("update")
|
|
882
|
+
.limit(1);
|
|
883
|
+
|
|
884
|
+
if (links[0] !== undefined)
|
|
885
|
+
return mapping.subjectId.equals(links[0].subjectId as NativeId, nativeId)
|
|
886
|
+
? ({ _tag: "bound" } as const)
|
|
887
|
+
: ({ _tag: "conflict" } as const);
|
|
888
|
+
yield* tx
|
|
889
|
+
.insert(externalTable as any)
|
|
890
|
+
.values(mapping.externalIdentity.encodeInsert(identity, nativeId) as any);
|
|
891
|
+
|
|
892
|
+
return { _tag: "bound" } as const;
|
|
893
|
+
}),
|
|
894
|
+
)
|
|
895
|
+
.pipe(
|
|
896
|
+
Effect.catchCause((cause) => {
|
|
897
|
+
if (
|
|
898
|
+
cause.reasons.length === 0 ||
|
|
899
|
+
!cause.reasons.every(
|
|
900
|
+
(reason) =>
|
|
901
|
+
Cause.isFailReason(reason) &&
|
|
902
|
+
isMappedConstraintConflict(mapping.isConstraintConflict, reason.error),
|
|
903
|
+
)
|
|
904
|
+
)
|
|
905
|
+
return Effect.failCause(cause);
|
|
906
|
+
|
|
907
|
+
return db
|
|
908
|
+
.select({ subjectId: externalSubjectIdColumn })
|
|
909
|
+
.from(externalTable as any)
|
|
910
|
+
.where(
|
|
911
|
+
and(
|
|
912
|
+
eq(externalProviderColumn, identity.provider),
|
|
913
|
+
eq(externalIssuerColumn, identity.issuer),
|
|
914
|
+
eq(externalSubjectColumn, identity.subject),
|
|
915
|
+
),
|
|
916
|
+
)
|
|
917
|
+
.limit(1)
|
|
918
|
+
.pipe(
|
|
919
|
+
Effect.map((rows) =>
|
|
920
|
+
rows[0] !== undefined &&
|
|
921
|
+
mapping.subjectId.equals(rows[0].subjectId as NativeId, nativeId)
|
|
922
|
+
? ({ _tag: "bound" } as const)
|
|
923
|
+
: ({ _tag: "conflict" } as const),
|
|
924
|
+
),
|
|
925
|
+
);
|
|
926
|
+
}),
|
|
927
|
+
);
|
|
928
|
+
|
|
929
|
+
if (decision._tag === "conflict") return yield* IdentityConflict.make();
|
|
930
|
+
}, mapIdentityFailure);
|
|
931
|
+
|
|
932
|
+
return { externalIdentityMutation: ExternalIdentityMutation.of({ bind }) } as const;
|
|
933
|
+
};
|
|
934
|
+
|
|
935
|
+
export const makePgIdentityServices = <
|
|
936
|
+
Subject extends AnyPgTable,
|
|
937
|
+
Identifier extends AnyPgTable,
|
|
938
|
+
External extends AnyPgTable,
|
|
939
|
+
Request extends AnyPgTable,
|
|
940
|
+
NativeId,
|
|
941
|
+
>(
|
|
942
|
+
database: Database,
|
|
943
|
+
mapping: IdentityTables<Subject, Identifier, External, Request, NativeId>,
|
|
944
|
+
) => ({
|
|
945
|
+
...makePgSubjectProvisioningServices(database, mapping),
|
|
946
|
+
...makePgExternalIdentityServices(database, mapping),
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
export const pgLayers = <
|
|
950
|
+
Id,
|
|
951
|
+
C extends AnyPgTable,
|
|
952
|
+
R extends AnyPgTable,
|
|
953
|
+
S extends AnyPgTable,
|
|
954
|
+
Subject extends AnyPgTable,
|
|
955
|
+
Identifier extends AnyPgTable,
|
|
956
|
+
External extends AnyPgTable,
|
|
957
|
+
Request extends AnyPgTable,
|
|
958
|
+
NativeId,
|
|
959
|
+
>(
|
|
960
|
+
database: Context.Service<Id, Database>,
|
|
961
|
+
auth: AuthTables<C, R, S>,
|
|
962
|
+
identity: IdentityTables<Subject, Identifier, External, Request, NativeId>,
|
|
963
|
+
) => ({
|
|
964
|
+
identity: Layer.merge(
|
|
965
|
+
Layer.effect(
|
|
966
|
+
SubjectProvisioner,
|
|
967
|
+
database.use((db) => Effect.succeed(makePgIdentityServices(db, identity).subjectProvisioner)),
|
|
968
|
+
),
|
|
969
|
+
Layer.effect(
|
|
970
|
+
ExternalIdentityMutation,
|
|
971
|
+
database.use((db) =>
|
|
972
|
+
Effect.succeed(makePgIdentityServices(db, identity).externalIdentityMutation),
|
|
973
|
+
),
|
|
974
|
+
),
|
|
975
|
+
),
|
|
976
|
+
authStore: Layer.effect(
|
|
977
|
+
AuthStore,
|
|
978
|
+
database.use((db) => Effect.succeed(makePgAuthServices(db, auth).authStore)),
|
|
979
|
+
),
|
|
980
|
+
oauthStateStore: Layer.effect(
|
|
981
|
+
OAuthStateStore,
|
|
982
|
+
database.use((db) => Effect.succeed(makePgAuthServices(db, auth).oauthStateStore)),
|
|
983
|
+
),
|
|
984
|
+
});
|