@yielded/auth-persistence-drizzle 0.1.0-beta.0 → 0.1.0-beta.1
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 +6 -1
- package/dist/D1.d.mts +135 -25
- package/dist/D1.mjs +1 -3
- package/dist/D1.mjs.map +1 -1
- package/dist/Libsql.d.mts +192 -75
- package/dist/Libsql.mjs +1 -7
- package/dist/Libsql.mjs.map +1 -1
- package/dist/Mysql2.d.mts +177 -37
- package/dist/Mysql2.mjs +1 -3
- package/dist/Mysql2.mjs.map +1 -1
- package/dist/Pglite.d.mts +171 -30
- package/dist/Pglite.mjs +2 -3
- package/dist/Pglite.mjs.map +1 -1
- package/dist/Postgres.d.mts +177 -36
- package/dist/Postgres.mjs +2 -3
- package/dist/Postgres.mjs.map +1 -1
- package/dist/SqliteBun.d.mts +192 -74
- package/dist/SqliteBun.mjs +1 -5
- package/dist/SqliteBun.mjs.map +1 -1
- package/dist/SqliteDo.d.mts +194 -110
- package/dist/SqliteDo.mjs +2 -32
- package/dist/SqliteDo.mjs.map +1 -1
- package/dist/SqliteNode.d.mts +190 -72
- package/dist/SqliteNode.mjs +1 -5
- package/dist/SqliteNode.mjs.map +1 -1
- package/dist/SqliteWasm.d.mts +192 -74
- package/dist/SqliteWasm.mjs +1 -5
- package/dist/SqliteWasm.mjs.map +1 -1
- package/dist/drizzle/D1BatchStatements.d.mts +1 -1
- package/dist/drizzle/D1BatchStatements.mjs.map +1 -1
- package/dist/drizzle/d1-emails.mjs.map +1 -1
- package/dist/drizzle/d1-generated-statement.mjs.map +1 -1
- package/dist/drizzle/d1-identity.d.mts +1 -1
- package/dist/drizzle/d1-oauth.d.mts +35 -5
- package/dist/drizzle/d1-oauth.mjs +1 -1
- package/dist/drizzle/d1-password-prepared.mjs.map +1 -1
- package/dist/drizzle/d1-passwords.d.mts +1 -1
- package/dist/drizzle/d1-passwords.mjs.map +1 -1
- package/dist/drizzle/d1-proofs.d.mts +1 -1
- package/dist/drizzle/d1-proofs.mjs.map +1 -1
- package/dist/drizzle/d1-sessions.d.mts +3 -3
- package/dist/drizzle/d1-sessions.mjs.map +1 -1
- package/dist/drizzle/email-registration.d.mts +1 -1
- package/dist/drizzle/email-target.d.mts +1 -1
- package/dist/drizzle/email-target.mjs.map +1 -1
- package/dist/drizzle/model.d.mts +2 -126
- package/dist/drizzle/model.mjs +2 -21
- package/dist/drizzle/model.mjs.map +1 -1
- package/dist/drizzle/mysql-emails.d.mts +1 -1
- package/dist/drizzle/mysql-emails.mjs.map +1 -1
- package/dist/drizzle/mysql-identity.d.mts +1 -1
- package/dist/drizzle/mysql-password-prepared.d.mts +1 -1
- package/dist/drizzle/mysql-password-prepared.mjs.map +1 -1
- package/dist/drizzle/mysql-passwords.d.mts +1 -1
- package/dist/drizzle/mysql-passwords.mjs.map +1 -1
- package/dist/drizzle/mysql-proofs.d.mts +1 -1
- package/dist/drizzle/mysql-proofs.mjs.map +1 -1
- package/dist/drizzle/mysql-sessions.d.mts +1 -1
- package/dist/drizzle/mysql-sessions.mjs.map +1 -1
- package/dist/drizzle/oauth-accounts.mjs +1 -1
- package/dist/drizzle/oauth-connected-access.mjs +14 -12
- package/dist/drizzle/oauth-connected-access.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-custody.mjs +2 -2
- package/dist/drizzle/oauth-connected-drivers.mjs +4 -1
- package/dist/drizzle/oauth-connected-drivers.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-flow.mjs +2 -2
- package/dist/drizzle/oauth-connected-input.mjs +14 -1
- package/dist/drizzle/oauth-connected-input.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-maintenance.mjs +1 -1
- package/dist/drizzle/oauth-connected-management.mjs +2 -2
- package/dist/drizzle/oauth-connected-model.d.mts +17 -3
- package/dist/drizzle/oauth-connected-model.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-settlement.mjs +51 -41
- package/dist/drizzle/oauth-connected-settlement.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-sign-in.mjs +263 -0
- package/dist/drizzle/oauth-connected-sign-in.mjs.map +1 -0
- package/dist/drizzle/oauth-connected-state.d.mts +110 -2
- package/dist/drizzle/oauth-connected-state.mjs +9 -4
- package/dist/drizzle/oauth-connected-state.mjs.map +1 -1
- package/dist/drizzle/oauth-connected-target.d.mts +137 -9
- package/dist/drizzle/oauth-connected-target.mjs +6 -2
- package/dist/drizzle/oauth-connected-target.mjs.map +1 -1
- package/dist/drizzle/oauth-drivers.mjs +1 -1
- package/dist/drizzle/oauth-flow.d.mts +1 -1
- package/dist/drizzle/oauth-flow.mjs +1 -1
- package/dist/drizzle/oauth-input.mjs +1 -1
- package/dist/drizzle/oauth-model.d.mts +1 -1
- package/dist/drizzle/oauth-owner.d.mts +1 -1
- package/dist/drizzle/oauth-registration.d.mts +2 -2
- package/dist/drizzle/oauth-registration.mjs +1 -1
- package/dist/drizzle/oauth-state.mjs +5 -4
- package/dist/drizzle/oauth-state.mjs.map +1 -1
- package/dist/drizzle/oauth-target.d.mts +2 -2
- package/dist/drizzle/oauth-target.mjs +1 -1
- package/dist/drizzle/oauth-target.mjs.map +1 -1
- package/dist/drizzle/passkey-write-target.d.mts +1 -1
- package/dist/drizzle/password-prepared-state.mjs +1 -1
- package/dist/drizzle/password-prepared-target.d.mts +1 -1
- package/dist/drizzle/password-prepared-target.mjs.map +1 -1
- package/dist/drizzle/password-target.d.mts +1 -1
- package/dist/drizzle/password-target.mjs.map +1 -1
- package/dist/drizzle/pg-emails.d.mts +1 -1
- package/dist/drizzle/pg-emails.mjs.map +1 -1
- package/dist/drizzle/pg-identity.d.mts +22 -0
- package/dist/drizzle/pg-identity.mjs +89 -0
- package/dist/drizzle/pg-identity.mjs.map +1 -0
- package/dist/drizzle/pg-password-prepared.d.mts +1 -1
- package/dist/drizzle/pg-password-prepared.mjs.map +1 -1
- package/dist/drizzle/pg-passwords.d.mts +1 -1
- package/dist/drizzle/pg-passwords.mjs.map +1 -1
- package/dist/drizzle/pg-proofs.d.mts +1 -1
- package/dist/drizzle/pg-proofs.mjs.map +1 -1
- package/dist/drizzle/pg-sessions.d.mts +1 -1
- package/dist/drizzle/pg-sessions.mjs.map +1 -1
- package/dist/drizzle/phone-model.d.mts +1 -1
- package/dist/drizzle/phone-target.d.mts +1 -1
- package/dist/drizzle/phone-target.mjs.map +1 -1
- package/dist/drizzle/proof-target.d.mts +1 -1
- package/dist/drizzle/proof-target.mjs.map +1 -1
- package/dist/drizzle/session-target.d.mts +1 -1
- package/dist/drizzle/session-target.mjs.map +1 -1
- package/dist/drizzle/sqlite-emails.mjs.map +1 -1
- package/dist/drizzle/sqlite-password-prepared.mjs.map +1 -1
- package/dist/drizzle/sqlite-passwords.mjs.map +1 -1
- package/dist/drizzle/sqlite-proofs.mjs.map +1 -1
- package/dist/drizzle/sqlite-sessions.mjs.map +1 -1
- package/dist/drizzle/standalone-guard.mjs.map +1 -1
- package/dist/drizzle/totp-model.d.mts +1 -1
- package/dist/drizzle/totp-target.d.mts +1 -1
- package/dist/drizzle/totp-target.mjs.map +1 -1
- package/dist/drizzle/transaction-mysql.mjs +1 -1
- package/dist/drizzle/transaction-mysql.mjs.map +1 -1
- package/dist/index.d.mts +2 -4
- package/dist/index.mjs +2 -4
- package/dist/internal/drizzle-postgres.mjs.map +1 -1
- package/dist/internal/drizzle-sqlite.mjs.map +1 -1
- package/package.json +1 -1
- package/src/D1.ts +1 -17
- package/src/Libsql.ts +0 -44
- package/src/Mysql2.ts +1 -23
- package/src/Pglite.ts +2 -18
- package/src/Postgres.ts +2 -18
- package/src/SqliteBun.ts +1 -38
- package/src/SqliteDo.ts +1 -256
- package/src/SqliteNode.ts +1 -38
- package/src/SqliteWasm.ts +1 -38
- package/src/drizzle/D1BatchStatements.ts +1 -1
- package/src/drizzle/d1-emails.ts +2 -4
- package/src/drizzle/d1-generated-statement.ts +1 -1
- package/src/drizzle/d1-password-prepared.ts +2 -5
- package/src/drizzle/d1-passwords.ts +2 -4
- package/src/drizzle/d1-proofs.ts +2 -4
- package/src/drizzle/d1-sessions.ts +2 -2
- package/src/drizzle/email-target.ts +1 -1
- package/src/drizzle/model.ts +1 -208
- package/src/drizzle/mysql-emails.ts +1 -1
- package/src/drizzle/mysql-password-prepared.ts +1 -1
- package/src/drizzle/mysql-passwords.ts +1 -1
- package/src/drizzle/mysql-proofs.ts +1 -1
- package/src/drizzle/mysql-sessions.ts +1 -1
- package/src/drizzle/oauth-connected-access.ts +8 -1
- package/src/drizzle/oauth-connected-drivers.ts +3 -0
- package/src/drizzle/oauth-connected-input.ts +13 -0
- package/src/drizzle/oauth-connected-model.ts +17 -0
- package/src/drizzle/oauth-connected-settlement.ts +65 -42
- package/src/drizzle/oauth-connected-sign-in.ts +438 -0
- package/src/drizzle/oauth-connected-state.ts +19 -7
- package/src/drizzle/oauth-connected-target.ts +10 -1
- package/src/drizzle/oauth-state.ts +5 -4
- package/src/drizzle/oauth-target.ts +1 -1
- package/src/drizzle/password-prepared-target.ts +1 -1
- package/src/drizzle/password-target.ts +1 -1
- package/src/drizzle/pg-emails.ts +1 -1
- package/src/drizzle/pg-identity.ts +301 -0
- package/src/drizzle/pg-password-prepared.ts +1 -1
- package/src/drizzle/pg-passwords.ts +1 -1
- package/src/drizzle/pg-proofs.ts +1 -1
- package/src/drizzle/pg-sessions.ts +1 -1
- package/src/drizzle/phone-target.ts +1 -1
- package/src/drizzle/proof-target.ts +1 -1
- package/src/drizzle/session-target.ts +1 -1
- package/src/drizzle/sqlite-emails.ts +1 -1
- package/src/drizzle/sqlite-password-prepared.ts +1 -1
- package/src/drizzle/sqlite-passwords.ts +1 -1
- package/src/drizzle/sqlite-proofs.ts +1 -1
- package/src/drizzle/sqlite-sessions.ts +1 -1
- package/src/drizzle/standalone-guard.ts +1 -1
- package/src/drizzle/totp-target.ts +1 -1
- package/src/drizzle/transaction-mysql.ts +1 -1
- package/src/index.ts +0 -24
- package/src/internal/drizzle-postgres.ts +1 -1
- package/src/internal/drizzle-sqlite.ts +1 -1
- package/dist/drizzle/mysql.d.mts +0 -69
- package/dist/drizzle/mysql.mjs +0 -218
- package/dist/drizzle/mysql.mjs.map +0 -1
- package/dist/drizzle/pg.d.mts +0 -80
- package/dist/drizzle/pg.mjs +0 -289
- package/dist/drizzle/pg.mjs.map +0 -1
- package/dist/drizzle/sqlite.d.mts +0 -77
- package/dist/drizzle/sqlite.mjs +0 -217
- package/dist/drizzle/sqlite.mjs.map +0 -1
- package/src/drizzle/mysql.ts +0 -687
- package/src/drizzle/pg.ts +0 -984
- package/src/drizzle/sqlite.ts +0 -705
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-connected-model.mjs","names":[],"sources":["../../src/drizzle/oauth-connected-model.ts"],"sourcesContent":["import type { OAuthAccountRevision } from \"@yielded/auth/OAuth\";\nimport type * as M from \"@yielded/auth/OAuth\";\nimport type { AuthenticationRequirement } from \"@yielded/auth/Sessions\";\nimport type { InferInsertModel, InferSelectModel, SQL, Table } from \"drizzle-orm\";\n\nimport type { SubjectIdCodec } from \"./model\";\nimport type {\n OAuthAuthorityReadTable,\n OAuthClock,\n OAuthOwnershipMutation,\n OAuthSubjectReadTable,\n requiredOAuthTupleConstraints,\n} from \"./oauth-model\";\n\ntype Column<T extends Table> = Extract<keyof T[\"_\"][\"columns\"], string>;\nexport type OAuthConnectedAction = M.OAuthConnectedActionChallenge[\"action\"];\n\nexport interface OAuthConnectedSubjectTable<S extends Table> extends OAuthSubjectReadTable<S> {\n readonly decodeActionRequirement: (\n row: InferSelectModel<S>,\n action: OAuthConnectedAction,\n ) => AuthenticationRequirement;\n}\n\n/** All mapped order columns use exact SQL integer comparison through 2^53-1.\n * These codecs bridge driver representations, not alternative ordering rules. */\nexport interface OAuthConnectedOrderCodec {\n readonly encode: (order: number) => unknown;\n readonly decode: (value: unknown) => number;\n}\n\nexport interface OAuthConnectedFlowTable<F extends Table, N> {\n readonly table: F;\n readonly moduleId: Column<F>;\n readonly flowId: Column<F>;\n readonly commandId: Column<F>;\n readonly subjectId: Column<F>;\n readonly clientKey: Column<F>;\n readonly cohortKey: Column<F>;\n readonly state: Column<F>;\n readonly version: Column<F>;\n readonly stateDigest: Column<F>;\n readonly snapshot: Column<F>;\n readonly claimId: Column<F>;\n readonly claimDigest: Column<F>;\n readonly claimOrder: Column<F>;\n readonly claimedAt: Column<F>;\n readonly claimExpiresAt: Column<F>;\n readonly expiresAt: Column<F>;\n readonly retentionUntil: Column<F>;\n /** None before claim, Unresolved after claim, Resolved only after definite\n * cancellation or atomic transfer to a grant/job. Never reset by cleanup. */\n readonly work: Column<F>;\n /** Exact sealed quarantine when no correctly sealed revocation job is available. */\n readonly custody: Column<F>;\n readonly encodeInsert: (input: {\n readonly flow: M.OAuthConnectedPendingFlow;\n readonly subjectId: N;\n }) => InferInsertModel<F>;\n}\n\nexport interface OAuthConnectedGrantTable<G extends Table, N> {\n readonly table: G;\n readonly moduleId: Column<G>;\n readonly grantId: Column<G>;\n readonly subjectId: Column<G>;\n readonly identityKey: Column<G>;\n /** Nullable unique active slot; historical/disconnected records use null. */\n readonly activeIdentityKey: Column<G>;\n readonly clientKey: Column<G>;\n readonly cohortKey: Column<G>;\n readonly profileKey: Column<G>;\n readonly grantVersion: Column<G>;\n readonly tokenVersion: Column<G>;\n readonly cohortGeneration: Column<G>;\n readonly state: Column<G>;\n readonly version: Column<G>;\n readonly context: Column<G>;\n readonly sealed: Column<G>;\n /** Canonical OAuthConnectedSummary only: list never loads token envelopes. */\n readonly summary: Column<G>;\n readonly revocationJobId: Column<G>;\n readonly refreshWork: Column<G>;\n readonly refreshClaim: Column<G>;\n readonly refreshClaimExpiresAt: Column<G>;\n readonly retentionUntil: Column<G>;\n readonly encodeInsert: (input: {\n readonly grant: M.OAuthConnectedStoredGrant;\n readonly subjectId: N;\n }) => InferInsertModel<G>;\n}\n\n/** Also stores a permanent provider/issuer scope anchor. clientKey must fit 52\n * ASCII characters. Empty clientRegistrationId is reserved for that anchor;\n * actual profile registrations are nonempty. Scope counter stays zero. */\nexport interface OAuthConnectedClientRegistrationTable<C extends Table> {\n readonly table: C;\n readonly clientKey: Column<C>;\n readonly provider: Column<C>;\n readonly issuer: Column<C>;\n readonly clientRegistrationId: Column<C>;\n readonly counter: Column<C>;\n readonly version: Column<C>;\n readonly encodeInsert: (configuration: M.OAuthConnectedConfiguration) => InferInsertModel<C>;\n}\n\n/** Remote authority: no local-subject column or subject-scoped key. Retain this\n * anchor/cutoff when a legitimately released tuple gains a different local owner. */\nexport interface OAuthConnectedCohortTable<C extends Table> {\n readonly table: C;\n readonly cohortKey: Column<C>;\n readonly clientKey: Column<C>;\n readonly identityKey: Column<C>;\n readonly generation: Column<C>;\n readonly cutoff: Column<C>;\n readonly state: Column<C>;\n readonly version: Column<C>;\n readonly encodeInsert: (input: {\n readonly clientKey: string;\n readonly identityKey: string;\n }) => InferInsertModel<C>;\n}\n\nexport interface OAuthConnectedAdmissionTable<A extends Table, N> {\n readonly table: A;\n readonly admissionId: Column<A>;\n readonly moduleId: Column<A>;\n readonly grantId: Column<A>;\n readonly subjectId: Column<A>;\n readonly identityKey: Column<A>;\n readonly clientKey: Column<A>;\n readonly cohortKey: Column<A>;\n readonly snapshot: Column<A>;\n readonly admittedAt: Column<A>;\n readonly expiresAt: Column<A>;\n readonly version: Column<A>;\n readonly encodeInsert: (input: {\n readonly admissionId: string;\n readonly grant: M.OAuthConnectedStoredGrant;\n readonly authorization: M.OAuthConnectedUseAuthorization;\n readonly subjectId: N;\n }) => InferInsertModel<A>;\n}\n\nexport interface OAuthConnectedCommandTable<C extends Table, N> {\n readonly table: C;\n readonly moduleId: Column<C>;\n readonly commandId: Column<C>;\n readonly subjectId: Column<C>;\n readonly grantId: Column<C>;\n readonly intent: Column<C>;\n readonly decision: Column<C>;\n readonly retentionUntil: Column<C>;\n readonly version: Column<C>;\n readonly encodeInsert: (input: {\n readonly commandId: string;\n readonly grant: M.OAuthConnectedDisconnectGrant;\n readonly subjectId: N;\n }) => InferInsertModel<C>;\n}\n\nexport interface OAuthConnectedRevocationJobTable<J extends Table, N> {\n readonly table: J;\n readonly jobId: Column<J>;\n readonly moduleId: Column<J>;\n readonly subjectId: Column<J>;\n readonly identityKey: Column<J>;\n readonly clientKey: Column<J>;\n readonly cohortKey: Column<J>;\n readonly grantId: Column<J>;\n readonly snapshot: Column<J>;\n readonly state: Column<J>;\n readonly claimId: Column<J>;\n readonly claimedAt: Column<J>;\n readonly claimExpiresAt: Column<J>;\n readonly retentionUntil: Column<J>;\n readonly version: Column<J>;\n readonly encodeInsert: (input: {\n readonly job: M.OAuthConnectedRevocationJob;\n readonly subjectId: N;\n }) => InferInsertModel<J>;\n}\n\nexport type OAuthConnectedPolicyInput<N> = {\n readonly subjectId: N;\n readonly revision: OAuthAccountRevision;\n} & (\n | {\n readonly kind: \"action\";\n readonly operation: \"issue\" | \"claim\" | \"settle\" | \"disconnect\";\n readonly authorization: M.OAuthConnectedActionAuthorization;\n readonly configuration: M.OAuthConnectedConfiguration;\n readonly grant?: M.OAuthConnectedTokenContext;\n }\n | {\n readonly kind: \"metadata\" | \"use\";\n readonly authorization: M.OAuthConnectedUseAuthorization;\n readonly grant?: M.OAuthConnectedTokenContext;\n }\n);\n\nexport interface OAuthConnectedPolicyGuardTable<T extends Table, N> {\n readonly table: T;\n readonly orderBy: Column<T>;\n readonly condition: (input: OAuthConnectedPolicyInput<N>) => SQL;\n}\n\nexport interface OAuthConnectedPolicyGuard<N> {\n readonly table: Table;\n readonly orderBy: string;\n readonly condition: (input: OAuthConnectedPolicyInput<N>) => SQL;\n}\n\nexport const oauthConnectedPolicyGuard = <T extends Table, N>(\n input: OAuthConnectedPolicyGuardTable<T, N>,\n): OAuthConnectedPolicyGuard<N> =>\n Object.freeze({\n table: input.table,\n orderBy: input.orderBy,\n condition: input.condition,\n });\n\nexport interface OAuthConnectedSqlPolicy<N> {\n /** Up to 32 scopes, each selecting 1..64 stable rows in global lock order.\n * Omit only when subject/shared authority locks already serialize every mutable\n * policy dependency or policy is immutable. All writers follow that order. */\n readonly guards?: ReadonlyArray<OAuthConnectedPolicyGuard<N>>;\n /** Recheck exact policy revision, purpose, optional grant/profile restrictions\n * and current profile/permissions; joins and absence assumptions are authority.\n * The engine checks this before and after every owner/application write. */\n readonly condition: (input: OAuthConnectedPolicyInput<N>) => SQL;\n}\n\nexport const requiredOAuthConnectedConstraints = {\n flow: \"unique(connectedFlow.moduleId,connectedFlow.flowId)\",\n flowCommand: \"unique(connectedFlow.moduleId,connectedFlow.commandId)\",\n stateDigest: \"unique(connectedFlow.stateDigest)\",\n client: \"unique(connectedClient.clientKey)\",\n cohort: \"unique(connectedCohort.cohortKey)\",\n grant: \"unique(connectedGrant.moduleId,connectedGrant.grantId)\",\n grantIdentity:\n \"unique(connectedGrant.moduleId,connectedGrant.subjectId,connectedGrant.profileKey,connectedGrant.activeIdentityKey)\",\n admission: \"unique(connectedAdmission.admissionId)\",\n command: \"unique(connectedCommand.moduleId,connectedCommand.commandId)\",\n} as const;\n\nexport const requiredOAuthConnectedRevocationConstraints = {\n job: \"unique(connectedRevocation.jobId)\",\n} as const;\n\n/** Existing ownership only; workers never allocate or acquire a tuple. */\nexport type OAuthConnectedOwnershipRead<T extends Table, O extends Table, N> =\n | {\n readonly mode: \"integrated\";\n readonly tuple: Omit<OAuthOwnershipMutation<T, O, N>[\"tuple\"], \"encodeInsert\">;\n }\n | {\n readonly mode: \"separate\";\n readonly tuple: Omit<OAuthOwnershipMutation<T, O, N>[\"tuple\"], \"encodeInsert\">;\n readonly external: Omit<\n Extract<OAuthOwnershipMutation<T, O, N>, { readonly mode: \"separate\" }>[\"external\"],\n \"encodeInsert\"\n >;\n };\n\nexport interface OAuthConnectedAuthorityMapping<\n T extends Table,\n O extends Table,\n F extends Table,\n G extends Table,\n C extends Table,\n H extends Table,\n N,\n> {\n readonly ownership: OAuthConnectedOwnershipRead<T, O, N>;\n readonly subjectId: SubjectIdCodec<N>;\n readonly flow: Omit<OAuthConnectedFlowTable<F, N>, \"encodeInsert\">;\n readonly grant: Omit<OAuthConnectedGrantTable<G, N>, \"encodeInsert\">;\n readonly client: Omit<OAuthConnectedClientRegistrationTable<C>, \"encodeInsert\">;\n readonly cohort: Omit<OAuthConnectedCohortTable<H>, \"encodeInsert\">;\n readonly clock: OAuthClock;\n readonly order: OAuthConnectedOrderCodec;\n readonly tupleConstraints: typeof requiredOAuthTupleConstraints;\n /** Safe metadata retention, never an expiry/quiescence proof for unresolved work. */\n readonly retentionMillis: number;\n}\n\nexport interface OAuthConnectedMapping<\n S extends Table,\n AC extends Table,\n T extends Table,\n O extends Table,\n F extends Table,\n G extends Table,\n C extends Table,\n H extends Table,\n A extends Table,\n D extends Table,\n N,\n J extends Table = never,\n> extends OAuthConnectedAuthorityMapping<T, O, F, G, C, H, N> {\n readonly ownership: OAuthOwnershipMutation<T, O, N>;\n readonly flow: OAuthConnectedFlowTable<F, N>;\n readonly grant: OAuthConnectedGrantTable<G, N>;\n readonly client: OAuthConnectedClientRegistrationTable<C>;\n readonly cohort: OAuthConnectedCohortTable<H>;\n readonly subject: OAuthConnectedSubjectTable<S>;\n readonly authority: OAuthAuthorityReadTable<AC>;\n readonly admission: OAuthConnectedAdmissionTable<A, N>;\n readonly command: OAuthConnectedCommandTable<D, N>;\n readonly policy: OAuthConnectedSqlPolicy<N>;\n readonly constraints: typeof requiredOAuthConnectedConstraints;\n readonly revocation:\n | { readonly mode: \"unsupported\" }\n | {\n readonly mode: \"cohort\";\n readonly job: OAuthConnectedRevocationJobTable<J, N>;\n readonly constraints: typeof requiredOAuthConnectedRevocationConstraints;\n };\n /** Without an authoritative same-owner predicate, conservatively retain Owned.\n * Include login/other installed references; never filter away former-subject\n * work. This pure SQL constructor supports both bound values and correlated\n * column expressions with identical semantics; it performs no I/O.\n * Compare through the reference table's typed columns (for example, eq) so\n * native values use their encoders. Back the same-owner lookup with an index:\n * discovery applies this predicate before its candidate limit.\n * Connected references are checked separately. */\n readonly externalReference?: (\n input:\n | { readonly identityKey: string; readonly subjectId: N }\n | { readonly identityKey: SQL; readonly subjectId: SQL },\n ) => SQL;\n}\n\nexport interface OAuthConnectedRevocationMapping<\n T extends Table,\n O extends Table,\n F extends Table,\n G extends Table,\n C extends Table,\n H extends Table,\n J extends Table,\n N,\n> extends OAuthConnectedAuthorityMapping<T, O, F, G, C, H, N> {\n readonly job: Omit<OAuthConnectedRevocationJobTable<J, N>, \"encodeInsert\">;\n readonly constraints: typeof requiredOAuthConnectedRevocationConstraints;\n}\n"],"mappings":";AAqNA,MAAa,6BACX,UAEA,OAAO,OAAO;CACZ,OAAO,MAAM;CACb,SAAS,MAAM;CACf,WAAW,MAAM;AACnB,CAAC;AAaH,MAAa,oCAAoC;CAC/C,MAAM;CACN,aAAa;CACb,aAAa;CACb,QAAQ;CACR,QAAQ;CACR,OAAO;CACP,eACE;CACF,WAAW;CACX,SAAS;AACX;AAEA,MAAa,8CAA8C,EACzD,KAAK,oCACP"}
|
|
1
|
+
{"version":3,"file":"oauth-connected-model.mjs","names":[],"sources":["../../src/drizzle/oauth-connected-model.ts"],"sourcesContent":["import type { OAuthAccountRevision } from \"@yielded/auth/OAuth\";\nimport type * as M from \"@yielded/auth/OAuth\";\nimport type { AuthenticationRequirement } from \"@yielded/auth/Sessions\";\nimport type { InferInsertModel, InferSelectModel, SQL, Table } from \"drizzle-orm\";\n\nimport type { SubjectIdCodec } from \"./model\";\nimport type {\n OAuthAuthorityReadTable,\n OAuthClock,\n OAuthCredentialReadTable,\n OAuthFlowTable,\n OAuthOwnershipMutation,\n OAuthSubjectReadTable,\n requiredOAuthTupleConstraints,\n} from \"./oauth-model\";\n\ntype Column<T extends Table> = Extract<keyof T[\"_\"][\"columns\"], string>;\nexport type OAuthConnectedAction = M.OAuthConnectedActionChallenge[\"action\"];\n\nexport interface OAuthConnectedSubjectTable<S extends Table> extends OAuthSubjectReadTable<S> {\n readonly decodeActionRequirement: (\n row: InferSelectModel<S>,\n action: OAuthConnectedAction,\n ) => AuthenticationRequirement;\n}\n\n/** All mapped order columns use exact SQL integer comparison through 2^53-1.\n * These codecs bridge driver representations, not alternative ordering rules. */\nexport interface OAuthConnectedOrderCodec {\n readonly encode: (order: number) => unknown;\n readonly decode: (value: unknown) => number;\n}\n\nexport interface OAuthConnectedFlowTable<F extends Table, N> {\n readonly table: F;\n readonly moduleId: Column<F>;\n readonly flowId: Column<F>;\n readonly commandId: Column<F>;\n readonly subjectId: Column<F>;\n readonly clientKey: Column<F>;\n readonly cohortKey: Column<F>;\n readonly state: Column<F>;\n readonly version: Column<F>;\n readonly stateDigest: Column<F>;\n readonly snapshot: Column<F>;\n readonly claimId: Column<F>;\n readonly claimDigest: Column<F>;\n readonly claimOrder: Column<F>;\n readonly claimedAt: Column<F>;\n readonly claimExpiresAt: Column<F>;\n readonly expiresAt: Column<F>;\n readonly retentionUntil: Column<F>;\n /** None before claim, Unresolved after claim, Resolved only after definite\n * cancellation or atomic transfer to a grant/job. Never reset by cleanup. */\n readonly work: Column<F>;\n /** Exact sealed quarantine when no correctly sealed revocation job is available. */\n readonly custody: Column<F>;\n /** Required when retaining grants during sign-in. subjectId is NULL until\n * identity resolution; custom required columns are supplied by this encoder. */\n readonly encodeSignIn?: (reservation: M.OAuthSignInAccessClaim) => InferInsertModel<F>;\n readonly encodeInsert: (input: {\n readonly flow: M.OAuthConnectedPendingFlow;\n readonly subjectId: N;\n }) => InferInsertModel<F>;\n}\n\nexport interface OAuthConnectedGrantTable<G extends Table, N> {\n readonly table: G;\n readonly moduleId: Column<G>;\n readonly grantId: Column<G>;\n readonly subjectId: Column<G>;\n readonly identityKey: Column<G>;\n /** Nullable unique active slot; historical/disconnected records use null. */\n readonly activeIdentityKey: Column<G>;\n readonly clientKey: Column<G>;\n readonly cohortKey: Column<G>;\n readonly profileKey: Column<G>;\n readonly grantVersion: Column<G>;\n readonly tokenVersion: Column<G>;\n readonly cohortGeneration: Column<G>;\n readonly state: Column<G>;\n readonly version: Column<G>;\n readonly context: Column<G>;\n readonly sealed: Column<G>;\n /** Canonical OAuthConnectedSummary only: list never loads token envelopes. */\n readonly summary: Column<G>;\n readonly revocationJobId: Column<G>;\n readonly refreshWork: Column<G>;\n readonly refreshClaim: Column<G>;\n readonly refreshClaimExpiresAt: Column<G>;\n readonly retentionUntil: Column<G>;\n readonly encodeInsert: (input: {\n readonly grant: M.OAuthConnectedStoredGrant;\n readonly subjectId: N;\n }) => InferInsertModel<G>;\n}\n\n/** Also stores a permanent provider/issuer scope anchor. clientKey must fit 52\n * ASCII characters. Empty clientRegistrationId is reserved for that anchor;\n * actual profile registrations are nonempty. Scope counter stays zero. */\nexport interface OAuthConnectedClientRegistrationTable<C extends Table> {\n readonly table: C;\n readonly clientKey: Column<C>;\n readonly provider: Column<C>;\n readonly issuer: Column<C>;\n readonly clientRegistrationId: Column<C>;\n readonly counter: Column<C>;\n readonly version: Column<C>;\n readonly encodeInsert: (configuration: M.OAuthConnectedConfiguration) => InferInsertModel<C>;\n}\n\n/** Remote authority: no local-subject column or subject-scoped key. Retain this\n * anchor/cutoff when a legitimately released tuple gains a different local owner. */\nexport interface OAuthConnectedCohortTable<C extends Table> {\n readonly table: C;\n readonly cohortKey: Column<C>;\n readonly clientKey: Column<C>;\n readonly identityKey: Column<C>;\n readonly generation: Column<C>;\n readonly cutoff: Column<C>;\n readonly state: Column<C>;\n readonly version: Column<C>;\n readonly encodeInsert: (input: {\n readonly clientKey: string;\n readonly identityKey: string;\n }) => InferInsertModel<C>;\n}\n\nexport interface OAuthConnectedAdmissionTable<A extends Table, N> {\n readonly table: A;\n readonly admissionId: Column<A>;\n readonly moduleId: Column<A>;\n readonly grantId: Column<A>;\n readonly subjectId: Column<A>;\n readonly identityKey: Column<A>;\n readonly clientKey: Column<A>;\n readonly cohortKey: Column<A>;\n readonly snapshot: Column<A>;\n readonly admittedAt: Column<A>;\n readonly expiresAt: Column<A>;\n readonly version: Column<A>;\n readonly encodeInsert: (input: {\n readonly admissionId: string;\n readonly grant: M.OAuthConnectedStoredGrant;\n readonly authorization: M.OAuthConnectedUseAuthorization;\n readonly subjectId: N;\n }) => InferInsertModel<A>;\n}\n\nexport interface OAuthConnectedCommandTable<C extends Table, N> {\n readonly table: C;\n readonly moduleId: Column<C>;\n readonly commandId: Column<C>;\n readonly subjectId: Column<C>;\n readonly grantId: Column<C>;\n readonly intent: Column<C>;\n readonly decision: Column<C>;\n readonly retentionUntil: Column<C>;\n readonly version: Column<C>;\n readonly encodeInsert: (input: {\n readonly commandId: string;\n readonly grant: M.OAuthConnectedDisconnectGrant;\n readonly subjectId: N;\n }) => InferInsertModel<C>;\n}\n\nexport interface OAuthConnectedRevocationJobTable<J extends Table, N> {\n readonly table: J;\n readonly jobId: Column<J>;\n readonly moduleId: Column<J>;\n readonly subjectId: Column<J>;\n readonly identityKey: Column<J>;\n readonly clientKey: Column<J>;\n readonly cohortKey: Column<J>;\n readonly grantId: Column<J>;\n readonly snapshot: Column<J>;\n readonly state: Column<J>;\n readonly claimId: Column<J>;\n readonly claimedAt: Column<J>;\n readonly claimExpiresAt: Column<J>;\n readonly retentionUntil: Column<J>;\n readonly version: Column<J>;\n readonly encodeInsert: (input: {\n readonly job: M.OAuthConnectedRevocationJob;\n readonly subjectId: N;\n }) => InferInsertModel<J>;\n}\n\nexport type OAuthConnectedPolicyInput<N> = {\n readonly subjectId: N;\n readonly revision: OAuthAccountRevision;\n} & (\n | {\n readonly kind: \"action\";\n readonly operation: \"issue\" | \"claim\" | \"settle\" | \"disconnect\";\n readonly authorization: M.OAuthConnectedActionAuthorization;\n readonly configuration: M.OAuthConnectedConfiguration;\n readonly grant?: M.OAuthConnectedTokenContext;\n }\n | {\n readonly kind: \"sign-in\";\n readonly credential: M.OAuthCredentialSnapshot;\n readonly configuration: M.OAuthConnectedConfiguration;\n readonly grant?: M.OAuthConnectedTokenContext;\n }\n | {\n readonly kind: \"metadata\" | \"use\";\n readonly authorization: M.OAuthConnectedUseAuthorization;\n readonly grant?: M.OAuthConnectedTokenContext;\n }\n);\n\nexport interface OAuthConnectedPolicyGuardTable<T extends Table, N> {\n readonly table: T;\n readonly orderBy: Column<T>;\n readonly condition: (input: OAuthConnectedPolicyInput<N>) => SQL;\n}\n\nexport interface OAuthConnectedPolicyGuard<N> {\n readonly table: Table;\n readonly orderBy: string;\n readonly condition: (input: OAuthConnectedPolicyInput<N>) => SQL;\n}\n\nexport const oauthConnectedPolicyGuard = <T extends Table, N>(\n input: OAuthConnectedPolicyGuardTable<T, N>,\n): OAuthConnectedPolicyGuard<N> =>\n Object.freeze({\n table: input.table,\n orderBy: input.orderBy,\n condition: input.condition,\n });\n\nexport interface OAuthConnectedSqlPolicy<N> {\n /** Up to 32 scopes, each selecting 1..64 stable rows in global lock order.\n * Omit only when subject/shared authority locks already serialize every mutable\n * policy dependency or policy is immutable. All writers follow that order. */\n readonly guards?: ReadonlyArray<OAuthConnectedPolicyGuard<N>>;\n /** Recheck exact policy revision, purpose, optional grant/profile restrictions\n * and current profile/permissions; joins and absence assumptions are authority.\n * The engine checks this before and after every owner/application write. */\n readonly condition: (input: OAuthConnectedPolicyInput<N>) => SQL;\n}\n\nexport const requiredOAuthConnectedConstraints = {\n flow: \"unique(connectedFlow.moduleId,connectedFlow.flowId)\",\n flowCommand: \"unique(connectedFlow.moduleId,connectedFlow.commandId)\",\n stateDigest: \"unique(connectedFlow.stateDigest)\",\n client: \"unique(connectedClient.clientKey)\",\n cohort: \"unique(connectedCohort.cohortKey)\",\n grant: \"unique(connectedGrant.moduleId,connectedGrant.grantId)\",\n grantIdentity:\n \"unique(connectedGrant.moduleId,connectedGrant.subjectId,connectedGrant.profileKey,connectedGrant.activeIdentityKey)\",\n admission: \"unique(connectedAdmission.admissionId)\",\n command: \"unique(connectedCommand.moduleId,connectedCommand.commandId)\",\n} as const;\n\nexport const requiredOAuthConnectedRevocationConstraints = {\n job: \"unique(connectedRevocation.jobId)\",\n} as const;\n\n/** Existing ownership only; workers never allocate or acquire a tuple. */\nexport type OAuthConnectedOwnershipRead<T extends Table, O extends Table, N> =\n | {\n readonly mode: \"integrated\";\n readonly tuple: Omit<OAuthOwnershipMutation<T, O, N>[\"tuple\"], \"encodeInsert\">;\n }\n | {\n readonly mode: \"separate\";\n readonly tuple: Omit<OAuthOwnershipMutation<T, O, N>[\"tuple\"], \"encodeInsert\">;\n readonly external: Omit<\n Extract<OAuthOwnershipMutation<T, O, N>, { readonly mode: \"separate\" }>[\"external\"],\n \"encodeInsert\"\n >;\n };\n\nexport interface OAuthConnectedAuthorityMapping<\n T extends Table,\n O extends Table,\n F extends Table,\n G extends Table,\n C extends Table,\n H extends Table,\n N,\n> {\n readonly ownership: OAuthConnectedOwnershipRead<T, O, N>;\n readonly subjectId: SubjectIdCodec<N>;\n readonly flow: Omit<OAuthConnectedFlowTable<F, N>, \"encodeInsert\">;\n readonly grant: Omit<OAuthConnectedGrantTable<G, N>, \"encodeInsert\">;\n readonly client: Omit<OAuthConnectedClientRegistrationTable<C>, \"encodeInsert\">;\n readonly cohort: Omit<OAuthConnectedCohortTable<H>, \"encodeInsert\">;\n readonly clock: OAuthClock;\n readonly order: OAuthConnectedOrderCodec;\n readonly tupleConstraints: typeof requiredOAuthTupleConstraints;\n /** Safe metadata retention, never an expiry/quiescence proof for unresolved work. */\n readonly retentionMillis: number;\n}\n\nexport interface OAuthConnectedMapping<\n S extends Table,\n AC extends Table,\n T extends Table,\n O extends Table,\n F extends Table,\n G extends Table,\n C extends Table,\n H extends Table,\n A extends Table,\n D extends Table,\n N,\n J extends Table = never,\n> extends OAuthConnectedAuthorityMapping<T, O, F, G, C, H, N> {\n readonly ownership: OAuthOwnershipMutation<T, O, N>;\n readonly flow: OAuthConnectedFlowTable<F, N>;\n readonly grant: OAuthConnectedGrantTable<G, N>;\n readonly client: OAuthConnectedClientRegistrationTable<C>;\n readonly cohort: OAuthConnectedCohortTable<H>;\n readonly subject: OAuthConnectedSubjectTable<S>;\n readonly authority: OAuthAuthorityReadTable<AC>;\n readonly admission: OAuthConnectedAdmissionTable<A, N>;\n /** Same shared sign-in tables used by OAuthSignInPersistence. Enable only\n * alongside a nullable connected-flow subjectId and flow.encodeSignIn. */\n readonly signIn?: {\n readonly credential: OAuthCredentialReadTable<Table>;\n readonly flow: OAuthFlowTable<Table>;\n };\n readonly command: OAuthConnectedCommandTable<D, N>;\n readonly policy: OAuthConnectedSqlPolicy<N>;\n readonly constraints: typeof requiredOAuthConnectedConstraints;\n readonly revocation:\n | { readonly mode: \"unsupported\" }\n | {\n readonly mode: \"cohort\";\n readonly job: OAuthConnectedRevocationJobTable<J, N>;\n readonly constraints: typeof requiredOAuthConnectedRevocationConstraints;\n };\n /** Without an authoritative same-owner predicate, conservatively retain Owned.\n * Include login/other installed references; never filter away former-subject\n * work. This pure SQL constructor supports both bound values and correlated\n * column expressions with identical semantics; it performs no I/O.\n * Compare through the reference table's typed columns (for example, eq) so\n * native values use their encoders. Back the same-owner lookup with an index:\n * discovery applies this predicate before its candidate limit.\n * Connected references are checked separately. */\n readonly externalReference?: (\n input:\n | { readonly identityKey: string; readonly subjectId: N }\n | { readonly identityKey: SQL; readonly subjectId: SQL },\n ) => SQL;\n}\n\nexport interface OAuthConnectedRevocationMapping<\n T extends Table,\n O extends Table,\n F extends Table,\n G extends Table,\n C extends Table,\n H extends Table,\n J extends Table,\n N,\n> extends OAuthConnectedAuthorityMapping<T, O, F, G, C, H, N> {\n readonly job: Omit<OAuthConnectedRevocationJobTable<J, N>, \"encodeInsert\">;\n readonly constraints: typeof requiredOAuthConnectedRevocationConstraints;\n}\n"],"mappings":";AAgOA,MAAa,6BACX,UAEA,OAAO,OAAO;CACZ,OAAO,MAAM;CACb,SAAS,MAAM;CACf,WAAW,MAAM;AACnB,CAAC;AAaH,MAAa,oCAAoC;CAC/C,MAAM;CACN,aAAa;CACb,aAAa;CACb,QAAQ;CACR,QAAQ;CACR,OAAO;CACP,eACE;CACF,WAAW;CACX,SAAS;AACX;AAEA,MAAa,8CAA8C,EACzD,KAAK,oCACP"}
|
|
@@ -1,19 +1,63 @@
|
|
|
1
|
-
import { invariant, sameIdentity } from "./oauth-state.mjs";
|
|
1
|
+
import { invariant, oauthIdentityKey, sameIdentity } from "./oauth-state.mjs";
|
|
2
2
|
import { both } from "./transaction-owner.mjs";
|
|
3
3
|
import { CurrentOAuthTransaction, col, equal } from "./oauth-owner.mjs";
|
|
4
4
|
import { acquireTuple, readTuple } from "./oauth-registration.mjs";
|
|
5
|
-
import { action, client, clientKey, cohort, configuration, current, nativeCopy, orderNumber, readGrant, retainedUntil, sealedStorage, summaryStorage, tokenContextStorage } from "./oauth-connected-state.mjs";
|
|
5
|
+
import { action, client, clientKey, cohort, cohortKey, configuration, current, nativeCopy, orderNumber, readGrant, retainedUntil, sealedStorage, summaryStorage, tokenContextStorage } from "./oauth-connected-state.mjs";
|
|
6
6
|
import { fence, noFormerOwner, storeJob, summary } from "./oauth-connected-custody.mjs";
|
|
7
7
|
import { exact, expected, sameTarget, terminal } from "./oauth-connected-flow.mjs";
|
|
8
|
-
import * as M from "@yielded/auth/OAuth";
|
|
9
|
-
import { snapshotOAuthSync } from "@yielded/auth/OAuth";
|
|
10
8
|
import { ne, sql } from "drizzle-orm";
|
|
11
9
|
import { Effect } from "effect";
|
|
10
|
+
import * as M from "@yielded/auth/OAuth";
|
|
11
|
+
import { snapshotOAuthSync } from "@yielded/auth/OAuth";
|
|
12
12
|
//#region src/drizzle/oauth-connected-settlement.ts
|
|
13
13
|
const validMetadata = (context, now) => {
|
|
14
14
|
const m = context.metadata, p = context.configuration.profile;
|
|
15
15
|
return p.provider === context.identity.provider && context.configuration.provider === context.identity.provider && context.configuration.issuer === context.identity.issuer && m.obtainedAtMillis <= now && m.useUntilMillis > m.obtainedAtMillis && m.useUntilMillis <= m.obtainedAtMillis + p.maximumAccessLifetimeMillis && (m.accessExpiresAtMillis === void 0 || m.useUntilMillis <= m.accessExpiresAtMillis) && m.scopes.every((v) => p.scopes.includes(v)) && m.resources.every((v) => p.resources.includes(v)) && new Set(m.scopes).size === m.scopes.length && new Set(m.resources).size === m.resources.length && (m.refreshUseUntilMillis === void 0 || p.retention === "access-and-refresh" && p.maximumRefreshLifetimeMillis !== void 0 && m.refreshUseUntilMillis > m.obtainedAtMillis && m.refreshUseUntilMillis <= m.obtainedAtMillis + p.maximumRefreshLifetimeMillis && (m.refreshExpiresAtMillis === void 0 || m.refreshUseUntilMillis <= m.refreshExpiresAtMillis));
|
|
16
16
|
};
|
|
17
|
+
/** The only writer that activates or replaces a confirmed connected grant. */
|
|
18
|
+
const activateGrant = Effect.fn("oauthConnected.activateGrant")(function* (mapping, grant, native, now, replacing) {
|
|
19
|
+
const owner = yield* CurrentOAuthTransaction;
|
|
20
|
+
const token = grant.context;
|
|
21
|
+
const g = mapping.grant;
|
|
22
|
+
const identityKey = oauthIdentityKey(token.identity);
|
|
23
|
+
const clientKey$1 = clientKey(token.configuration);
|
|
24
|
+
const cohortKey$1 = cohortKey(clientKey$1, identityKey);
|
|
25
|
+
const values = {
|
|
26
|
+
...g.encodeInsert({
|
|
27
|
+
grant: snapshotOAuthSync(M.OAuthConnectedStoredGrant, grant),
|
|
28
|
+
subjectId: nativeCopy(native)
|
|
29
|
+
}),
|
|
30
|
+
[g.moduleId]: token.moduleId,
|
|
31
|
+
[g.grantId]: token.grantId,
|
|
32
|
+
[g.subjectId]: native,
|
|
33
|
+
[g.identityKey]: identityKey,
|
|
34
|
+
[g.activeIdentityKey]: identityKey,
|
|
35
|
+
[g.clientKey]: clientKey$1,
|
|
36
|
+
[g.cohortKey]: cohortKey$1,
|
|
37
|
+
[g.profileKey]: token.configuration.profile.key,
|
|
38
|
+
[g.grantVersion]: token.grantVersion,
|
|
39
|
+
[g.tokenVersion]: token.tokenVersion,
|
|
40
|
+
[g.cohortGeneration]: token.cohortGeneration,
|
|
41
|
+
[g.state]: "Active",
|
|
42
|
+
[g.version]: owner.marker,
|
|
43
|
+
[g.context]: tokenContextStorage.encode(token),
|
|
44
|
+
[g.sealed]: sealedStorage.encode(grant.sealed),
|
|
45
|
+
[g.summary]: summaryStorage.encode(summary(token)),
|
|
46
|
+
[g.revocationJobId]: null,
|
|
47
|
+
[g.refreshWork]: "None",
|
|
48
|
+
[g.refreshClaim]: null,
|
|
49
|
+
[g.refreshClaimExpiresAt]: null,
|
|
50
|
+
[g.retentionUntil]: mapping.clock.encodeInstant(retainedUntil(mapping, Math.max(now, token.metadata.useUntilMillis, token.metadata.refreshUseUntilMillis ?? 0)))
|
|
51
|
+
};
|
|
52
|
+
if (!replacing) yield* owner.insert(g.table, values, {
|
|
53
|
+
[g.moduleId]: token.moduleId,
|
|
54
|
+
[g.grantId]: token.grantId
|
|
55
|
+
});
|
|
56
|
+
else yield* owner.update(g.table, {
|
|
57
|
+
[g.moduleId]: token.moduleId,
|
|
58
|
+
[g.grantId]: token.grantId
|
|
59
|
+
}, values);
|
|
60
|
+
});
|
|
17
61
|
const settle = Effect.fn("oauthConnected.settle")(function* (mapping, input) {
|
|
18
62
|
const owner = yield* CurrentOAuthTransaction;
|
|
19
63
|
const c = input.claim.flow.context, out = input.outcome;
|
|
@@ -48,7 +92,7 @@ const settle = Effect.fn("oauthConnected.settle")(function* (mapping, input) {
|
|
|
48
92
|
}), ne(col(g.table, g.grantId), c.grantId)), { limit: 1 });
|
|
49
93
|
const claimed = yield* exact(mapping, input.claim);
|
|
50
94
|
if (claimed === void 0) return { _tag: "Rejected" };
|
|
51
|
-
invariant(validMetadata(token, claimed.now) && token.metadata.obtainedAtMillis >= input.claim.claimedAtMillis);
|
|
95
|
+
invariant(token.exchangeOrder === input.claim.order && validMetadata(token, claimed.now) && token.metadata.obtainedAtMillis >= input.claim.claimedAtMillis);
|
|
52
96
|
const reconnect = c.reconnect;
|
|
53
97
|
const targetOkay = reconnect === void 0 ? original === void 0 : original !== void 0 && sameTarget(reconnect, original.context) && sameIdentity(reconnect.identity, token.identity) && original.row[g.refreshWork] !== "Unresolved";
|
|
54
98
|
const previousSafe = yield* noFormerOwner(mapping, tuple.key, cl.id, native);
|
|
@@ -69,41 +113,7 @@ const settle = Effect.fn("oauthConnected.settle")(function* (mapping, input) {
|
|
|
69
113
|
return { _tag: targetOkay && other.rows.length === 0 ? "Rejected" : "Conflict" };
|
|
70
114
|
}
|
|
71
115
|
if (tuple.row[t.state] === "Unowned") yield* acquireTuple(mapping.ownership, token.identity, tuple.key, nativeCopy(native));
|
|
72
|
-
|
|
73
|
-
...g.encodeInsert({
|
|
74
|
-
grant: snapshotOAuthSync(M.OAuthConnectedStoredGrant, grant),
|
|
75
|
-
subjectId: nativeCopy(native)
|
|
76
|
-
}),
|
|
77
|
-
[g.moduleId]: c.moduleId,
|
|
78
|
-
[g.grantId]: c.grantId,
|
|
79
|
-
[g.subjectId]: native,
|
|
80
|
-
[g.identityKey]: tuple.key,
|
|
81
|
-
[g.activeIdentityKey]: tuple.key,
|
|
82
|
-
[g.clientKey]: cl.id,
|
|
83
|
-
[g.cohortKey]: co.id,
|
|
84
|
-
[g.profileKey]: c.profile.key,
|
|
85
|
-
[g.grantVersion]: token.grantVersion,
|
|
86
|
-
[g.tokenVersion]: token.tokenVersion,
|
|
87
|
-
[g.cohortGeneration]: token.cohortGeneration,
|
|
88
|
-
[g.state]: "Active",
|
|
89
|
-
[g.version]: owner.marker,
|
|
90
|
-
[g.context]: tokenContextStorage.encode(token),
|
|
91
|
-
[g.sealed]: sealedStorage.encode(grant.sealed),
|
|
92
|
-
[g.summary]: summaryStorage.encode(summary(token)),
|
|
93
|
-
[g.revocationJobId]: null,
|
|
94
|
-
[g.refreshWork]: "None",
|
|
95
|
-
[g.refreshClaim]: null,
|
|
96
|
-
[g.refreshClaimExpiresAt]: null,
|
|
97
|
-
[g.retentionUntil]: mapping.clock.encodeInstant(retainedUntil(mapping, Math.max(claimed.now, token.metadata.useUntilMillis, token.metadata.refreshUseUntilMillis ?? 0)))
|
|
98
|
-
};
|
|
99
|
-
if (original === void 0) yield* owner.insert(g.table, values, {
|
|
100
|
-
[g.moduleId]: c.moduleId,
|
|
101
|
-
[g.grantId]: c.grantId
|
|
102
|
-
});
|
|
103
|
-
else yield* owner.update(g.table, {
|
|
104
|
-
[g.moduleId]: c.moduleId,
|
|
105
|
-
[g.grantId]: c.grantId
|
|
106
|
-
}, values);
|
|
116
|
+
yield* activateGrant(mapping, grant, native, claimed.now, original !== void 0);
|
|
107
117
|
yield* terminal(mapping, input.claim, "Connected", "Resolved", { cohort: co.id });
|
|
108
118
|
owner.postconditions.push(sql`${mapping.clock.engineNowMillis} >= ${claimed.now} and ${mapping.clock.engineNowMillis} < ${Math.min(input.claim.claimExpiresAtMillis, token.metadata.useUntilMillis)}`);
|
|
109
119
|
return {
|
|
@@ -112,6 +122,6 @@ const settle = Effect.fn("oauthConnected.settle")(function* (mapping, input) {
|
|
|
112
122
|
};
|
|
113
123
|
});
|
|
114
124
|
//#endregion
|
|
115
|
-
export { settle, validMetadata };
|
|
125
|
+
export { activateGrant, settle, validMetadata };
|
|
116
126
|
|
|
117
127
|
//# sourceMappingURL=oauth-connected-settlement.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-connected-settlement.mjs","names":["S.current","S.action","F.expected","S.client","S.configuration","F.exact","F.terminal","S.clientKey","F.readTuple","S.cohort","S.readGrant","F.sameTarget","C.noFormerOwner","S.orderNumber","F.acquireTuple","S.nativeCopy","C.fence","C.storeJob","C.summary","S.retainedUntil"],"sources":["../../src/drizzle/oauth-connected-settlement.ts"],"sourcesContent":["import * as M from \"@yielded/auth/OAuth\";\nimport { snapshotOAuthSync } from \"@yielded/auth/OAuth\";\nimport { ne, sql } from \"drizzle-orm\";\nimport { Effect } from \"effect\";\n\nimport * as C from \"./oauth-connected-custody\";\nimport * as F from \"./oauth-connected-flow\";\nimport * as S from \"./oauth-connected-state\";\nimport { both, col, equal, CurrentOAuthTransaction } from \"./oauth-owner\";\nimport { invariant, sameIdentity } from \"./oauth-state\";\n\nexport const validMetadata = (context: M.OAuthConnectedTokenContext, now: number) => {\n const m = context.metadata,\n p = context.configuration.profile;\n\n return (\n p.provider === context.identity.provider &&\n context.configuration.provider === context.identity.provider &&\n context.configuration.issuer === context.identity.issuer &&\n m.obtainedAtMillis <= now &&\n m.useUntilMillis > m.obtainedAtMillis &&\n m.useUntilMillis <= m.obtainedAtMillis + p.maximumAccessLifetimeMillis &&\n (m.accessExpiresAtMillis === undefined || m.useUntilMillis <= m.accessExpiresAtMillis) &&\n m.scopes.every((v) => p.scopes.includes(v)) &&\n m.resources.every((v) => p.resources.includes(v)) &&\n new Set(m.scopes).size === m.scopes.length &&\n new Set(m.resources).size === m.resources.length &&\n (m.refreshUseUntilMillis === undefined ||\n (p.retention === \"access-and-refresh\" &&\n p.maximumRefreshLifetimeMillis !== undefined &&\n m.refreshUseUntilMillis > m.obtainedAtMillis &&\n m.refreshUseUntilMillis <= m.obtainedAtMillis + p.maximumRefreshLifetimeMillis &&\n (m.refreshExpiresAtMillis === undefined ||\n m.refreshUseUntilMillis <= m.refreshExpiresAtMillis)))\n );\n};\n\nexport const settle = Effect.fn(\"oauthConnected.settle\")(function* (\n mapping: S.Mapping,\n input: F.Input<\"settle\">,\n) {\n const owner = yield* CurrentOAuthTransaction;\n\n const c = input.claim.flow.context,\n out = input.outcome;\n\n const found = yield* S.current(mapping, c.revision.subjectId);\n\n const authorized =\n found !== undefined &&\n (yield* S.action(mapping, found, input.authorization, \"settle\", F.expected(input.claim.flow)));\n\n const cl = yield* S.client(mapping, S.configuration(input.claim.flow), false);\n\n if (cl === undefined) return { _tag: \"Rejected\" } as const;\n if (out._tag !== \"Verified\" && out._tag !== \"Quarantined\") {\n const claimed = yield* F.exact(mapping, input.claim);\n\n if (claimed === undefined) return { _tag: \"Rejected\" } as const;\n yield* F.terminal(\n mapping,\n input.claim,\n out._tag,\n out._tag === \"Cancelled\" ? \"Resolved\" : \"Unresolved\",\n );\n\n return { _tag: out._tag } as\n | { readonly _tag: \"Cancelled\" }\n | { readonly _tag: \"Rejected\" }\n | { readonly _tag: \"Conflict\" }\n | { readonly _tag: \"Ambiguous\" };\n }\n\n const grant = snapshotOAuthSync(M.OAuthConnectedStoredGrant, out.grant),\n token = grant.context;\n\n invariant(\n token.moduleId === c.moduleId &&\n token.subjectId === c.revision.subjectId &&\n token.grantId === c.grantId &&\n token.identity.provider === c.provider &&\n token.identity.issuer === c.issuer &&\n S.clientKey(token.configuration) === cl.id &&\n S.tokenContextStorage.encode({\n ...token,\n configuration: S.configuration(input.claim.flow),\n }) === S.tokenContextStorage.encode(token),\n );\n const native = yield* mapping.subjectId.toNative(c.revision.subjectId);\n\n const tuple = yield* F.readTuple(mapping.ownership, token.identity),\n t = mapping.ownership.tuple;\n\n if (\n tuple.row[t.state] === \"Reserved\" ||\n (tuple.row[t.state] === \"Owned\" && !mapping.subjectId.equals(tuple.row[t.subjectId], native))\n ) {\n const claimed = yield* F.exact(mapping, input.claim);\n\n if (claimed !== undefined) yield* F.terminal(mapping, input.claim, \"Conflict\", \"Unresolved\");\n\n return { _tag: \"Conflict\" } as const;\n }\n const co = yield* S.cohort(mapping, cl.id, tuple.key, true);\n const original = yield* S.readGrant(mapping, c.moduleId, c.grantId);\n const g = mapping.grant;\n\n const other = yield* owner.read(\n g.table,\n both(\n equal(g.table, {\n [g.moduleId]: c.moduleId,\n [g.subjectId]: native,\n [g.profileKey]: c.profile.key,\n [g.activeIdentityKey]: tuple.key,\n }),\n ne(col(g.table, g.grantId), c.grantId),\n ),\n { limit: 1 },\n );\n\n const claimed = yield* F.exact(mapping, input.claim);\n\n if (claimed === undefined) return { _tag: \"Rejected\" } as const;\n invariant(\n validMetadata(token, claimed.now) &&\n token.metadata.obtainedAtMillis >= input.claim.claimedAtMillis,\n );\n const reconnect = c.reconnect;\n\n const targetOkay =\n reconnect === undefined\n ? original === undefined\n : original !== undefined &&\n F.sameTarget(reconnect, original.context) &&\n sameIdentity(reconnect.identity, token.identity) &&\n original.row[g.refreshWork] !== \"Unresolved\";\n\n const previousSafe = yield* C.noFormerOwner(mapping, tuple.key, cl.id, native);\n\n const active =\n out._tag === \"Verified\" &&\n authorized &&\n claimed.active &&\n c.profile.issuance === \"active\" &&\n token.metadata.useUntilMillis > claimed.now &&\n !co.blocked &&\n co.generation === token.cohortGeneration &&\n S.orderNumber(input.claim.order) > co.cutoff &&\n targetOkay &&\n other.rows.length === 0 &&\n previousSafe;\n\n if (!active) {\n // Foreign historical custody never authorizes a new remote operation.\n if (!previousSafe) {\n yield* F.terminal(mapping, input.claim, \"Conflict\", \"Unresolved\");\n\n return { _tag: \"Conflict\" } as const;\n }\n if (tuple.row[t.state] === \"Unowned\")\n yield* F.acquireTuple(mapping.ownership, token.identity, tuple.key, S.nativeCopy(native));\n yield* C.fence(mapping, cl, co);\n if (out.cleanup !== undefined) {\n yield* C.storeJob(mapping, out.cleanup, token, native, claimed.now);\n yield* F.terminal(mapping, input.claim, \"Quarantined\", \"Resolved\", { cohort: co.id });\n } else\n yield* F.terminal(mapping, input.claim, \"Quarantined\", \"Unresolved\", {\n cohort: co.id,\n sealed: grant,\n });\n\n return { _tag: targetOkay && other.rows.length === 0 ? \"Rejected\" : \"Conflict\" } as\n | { readonly _tag: \"Rejected\" }\n | { readonly _tag: \"Conflict\" };\n }\n if (tuple.row[t.state] === \"Unowned\")\n yield* F.acquireTuple(mapping.ownership, token.identity, tuple.key, S.nativeCopy(native));\n\n const values = {\n ...g.encodeInsert({\n grant: snapshotOAuthSync(M.OAuthConnectedStoredGrant, grant),\n subjectId: S.nativeCopy(native),\n }),\n [g.moduleId]: c.moduleId,\n [g.grantId]: c.grantId,\n [g.subjectId]: native,\n [g.identityKey]: tuple.key,\n [g.activeIdentityKey]: tuple.key,\n [g.clientKey]: cl.id,\n [g.cohortKey]: co.id,\n [g.profileKey]: c.profile.key,\n [g.grantVersion]: token.grantVersion,\n [g.tokenVersion]: token.tokenVersion,\n [g.cohortGeneration]: token.cohortGeneration,\n [g.state]: \"Active\",\n [g.version]: owner.marker,\n [g.context]: S.tokenContextStorage.encode(token),\n [g.sealed]: S.sealedStorage.encode(grant.sealed),\n [g.summary]: S.summaryStorage.encode(C.summary(token)),\n [g.revocationJobId]: null,\n [g.refreshWork]: \"None\",\n [g.refreshClaim]: null,\n [g.refreshClaimExpiresAt]: null,\n [g.retentionUntil]: mapping.clock.encodeInstant(\n S.retainedUntil(\n mapping,\n Math.max(\n claimed.now,\n token.metadata.useUntilMillis,\n token.metadata.refreshUseUntilMillis ?? 0,\n ),\n ),\n ),\n };\n\n if (original === undefined) {\n yield* owner.insert(g.table, values, { [g.moduleId]: c.moduleId, [g.grantId]: c.grantId });\n } else yield* owner.update(g.table, { [g.moduleId]: c.moduleId, [g.grantId]: c.grantId }, values);\n yield* F.terminal(mapping, input.claim, \"Connected\", \"Resolved\", { cohort: co.id });\n owner.postconditions.push(\n sql`${mapping.clock.engineNowMillis} >= ${claimed.now} and ${mapping.clock.engineNowMillis} < ${Math.min(input.claim.claimExpiresAtMillis, token.metadata.useUntilMillis)}`,\n );\n\n return { _tag: \"Connected\", grant } as const;\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"oauth-connected-settlement.mjs","names":["clientKey","S.clientKey","cohortKey","S.cohortKey","S.nativeCopy","C.summary","S.retainedUntil","S.current","S.action","F.expected","S.client","S.configuration","F.exact","F.terminal","F.readTuple","S.cohort","S.readGrant","F.sameTarget","C.noFormerOwner","S.orderNumber","F.acquireTuple","C.fence","C.storeJob"],"sources":["../../src/drizzle/oauth-connected-settlement.ts"],"sourcesContent":["import * as M from \"@yielded/auth/OAuth\";\nimport { snapshotOAuthSync } from \"@yielded/auth/OAuth\";\nimport { ne, sql } from \"drizzle-orm\";\nimport { Effect } from \"effect\";\n\nimport * as C from \"./oauth-connected-custody\";\nimport * as F from \"./oauth-connected-flow\";\nimport * as S from \"./oauth-connected-state\";\nimport { both, col, equal, CurrentOAuthTransaction } from \"./oauth-owner\";\nimport { invariant, sameIdentity, oauthIdentityKey } from \"./oauth-state\";\n\nexport const validMetadata = (context: M.OAuthConnectedTokenContext, now: number) => {\n const m = context.metadata,\n p = context.configuration.profile;\n\n return (\n p.provider === context.identity.provider &&\n context.configuration.provider === context.identity.provider &&\n context.configuration.issuer === context.identity.issuer &&\n m.obtainedAtMillis <= now &&\n m.useUntilMillis > m.obtainedAtMillis &&\n m.useUntilMillis <= m.obtainedAtMillis + p.maximumAccessLifetimeMillis &&\n (m.accessExpiresAtMillis === undefined || m.useUntilMillis <= m.accessExpiresAtMillis) &&\n m.scopes.every((v) => p.scopes.includes(v)) &&\n m.resources.every((v) => p.resources.includes(v)) &&\n new Set(m.scopes).size === m.scopes.length &&\n new Set(m.resources).size === m.resources.length &&\n (m.refreshUseUntilMillis === undefined ||\n (p.retention === \"access-and-refresh\" &&\n p.maximumRefreshLifetimeMillis !== undefined &&\n m.refreshUseUntilMillis > m.obtainedAtMillis &&\n m.refreshUseUntilMillis <= m.obtainedAtMillis + p.maximumRefreshLifetimeMillis &&\n (m.refreshExpiresAtMillis === undefined ||\n m.refreshUseUntilMillis <= m.refreshExpiresAtMillis)))\n );\n};\n\n/** The only writer that activates or replaces a confirmed connected grant. */\nexport const activateGrant = Effect.fn(\"oauthConnected.activateGrant\")(function* (\n mapping: S.Mapping,\n grant: M.OAuthConnectedStoredGrant,\n native: unknown,\n now: number,\n replacing: boolean,\n) {\n const owner = yield* CurrentOAuthTransaction;\n const token = grant.context;\n const g = mapping.grant;\n const identityKey = oauthIdentityKey(token.identity);\n const clientKey = S.clientKey(token.configuration);\n const cohortKey = S.cohortKey(clientKey, identityKey);\n\n const values = {\n ...g.encodeInsert({\n grant: snapshotOAuthSync(M.OAuthConnectedStoredGrant, grant),\n subjectId: S.nativeCopy(native),\n }),\n [g.moduleId]: token.moduleId,\n [g.grantId]: token.grantId,\n [g.subjectId]: native,\n [g.identityKey]: identityKey,\n [g.activeIdentityKey]: identityKey,\n [g.clientKey]: clientKey,\n [g.cohortKey]: cohortKey,\n [g.profileKey]: token.configuration.profile.key,\n [g.grantVersion]: token.grantVersion,\n [g.tokenVersion]: token.tokenVersion,\n [g.cohortGeneration]: token.cohortGeneration,\n [g.state]: \"Active\",\n [g.version]: owner.marker,\n [g.context]: S.tokenContextStorage.encode(token),\n [g.sealed]: S.sealedStorage.encode(grant.sealed),\n [g.summary]: S.summaryStorage.encode(C.summary(token)),\n [g.revocationJobId]: null,\n [g.refreshWork]: \"None\",\n [g.refreshClaim]: null,\n [g.refreshClaimExpiresAt]: null,\n [g.retentionUntil]: mapping.clock.encodeInstant(\n S.retainedUntil(\n mapping,\n Math.max(now, token.metadata.useUntilMillis, token.metadata.refreshUseUntilMillis ?? 0),\n ),\n ),\n };\n\n if (!replacing) {\n yield* owner.insert(g.table, values, {\n [g.moduleId]: token.moduleId,\n [g.grantId]: token.grantId,\n });\n } else\n yield* owner.update(\n g.table,\n { [g.moduleId]: token.moduleId, [g.grantId]: token.grantId },\n values,\n );\n});\n\nexport const settle = Effect.fn(\"oauthConnected.settle\")(function* (\n mapping: S.Mapping,\n input: F.Input<\"settle\">,\n) {\n const owner = yield* CurrentOAuthTransaction;\n\n const c = input.claim.flow.context,\n out = input.outcome;\n\n const found = yield* S.current(mapping, c.revision.subjectId);\n\n const authorized =\n found !== undefined &&\n (yield* S.action(mapping, found, input.authorization, \"settle\", F.expected(input.claim.flow)));\n\n const cl = yield* S.client(mapping, S.configuration(input.claim.flow), false);\n\n if (cl === undefined) return { _tag: \"Rejected\" } as const;\n if (out._tag !== \"Verified\" && out._tag !== \"Quarantined\") {\n const claimed = yield* F.exact(mapping, input.claim);\n\n if (claimed === undefined) return { _tag: \"Rejected\" } as const;\n yield* F.terminal(\n mapping,\n input.claim,\n out._tag,\n out._tag === \"Cancelled\" ? \"Resolved\" : \"Unresolved\",\n );\n\n return { _tag: out._tag } as\n | { readonly _tag: \"Cancelled\" }\n | { readonly _tag: \"Rejected\" }\n | { readonly _tag: \"Conflict\" }\n | { readonly _tag: \"Ambiguous\" };\n }\n\n const grant = snapshotOAuthSync(M.OAuthConnectedStoredGrant, out.grant),\n token = grant.context;\n\n invariant(\n token.moduleId === c.moduleId &&\n token.subjectId === c.revision.subjectId &&\n token.grantId === c.grantId &&\n token.identity.provider === c.provider &&\n token.identity.issuer === c.issuer &&\n S.clientKey(token.configuration) === cl.id &&\n S.tokenContextStorage.encode({\n ...token,\n configuration: S.configuration(input.claim.flow),\n }) === S.tokenContextStorage.encode(token),\n );\n const native = yield* mapping.subjectId.toNative(c.revision.subjectId);\n\n const tuple = yield* F.readTuple(mapping.ownership, token.identity),\n t = mapping.ownership.tuple;\n\n if (\n tuple.row[t.state] === \"Reserved\" ||\n (tuple.row[t.state] === \"Owned\" && !mapping.subjectId.equals(tuple.row[t.subjectId], native))\n ) {\n const claimed = yield* F.exact(mapping, input.claim);\n\n if (claimed !== undefined) yield* F.terminal(mapping, input.claim, \"Conflict\", \"Unresolved\");\n\n return { _tag: \"Conflict\" } as const;\n }\n const co = yield* S.cohort(mapping, cl.id, tuple.key, true);\n const original = yield* S.readGrant(mapping, c.moduleId, c.grantId);\n const g = mapping.grant;\n\n const other = yield* owner.read(\n g.table,\n both(\n equal(g.table, {\n [g.moduleId]: c.moduleId,\n [g.subjectId]: native,\n [g.profileKey]: c.profile.key,\n [g.activeIdentityKey]: tuple.key,\n }),\n ne(col(g.table, g.grantId), c.grantId),\n ),\n { limit: 1 },\n );\n\n const claimed = yield* F.exact(mapping, input.claim);\n\n if (claimed === undefined) return { _tag: \"Rejected\" } as const;\n invariant(\n token.exchangeOrder === input.claim.order &&\n validMetadata(token, claimed.now) &&\n token.metadata.obtainedAtMillis >= input.claim.claimedAtMillis,\n );\n const reconnect = c.reconnect;\n\n const targetOkay =\n reconnect === undefined\n ? original === undefined\n : original !== undefined &&\n F.sameTarget(reconnect, original.context) &&\n sameIdentity(reconnect.identity, token.identity) &&\n original.row[g.refreshWork] !== \"Unresolved\";\n\n const previousSafe = yield* C.noFormerOwner(mapping, tuple.key, cl.id, native);\n\n const active =\n out._tag === \"Verified\" &&\n authorized &&\n claimed.active &&\n c.profile.issuance === \"active\" &&\n token.metadata.useUntilMillis > claimed.now &&\n !co.blocked &&\n co.generation === token.cohortGeneration &&\n S.orderNumber(input.claim.order) > co.cutoff &&\n targetOkay &&\n other.rows.length === 0 &&\n previousSafe;\n\n if (!active) {\n // Foreign historical custody never authorizes a new remote operation.\n if (!previousSafe) {\n yield* F.terminal(mapping, input.claim, \"Conflict\", \"Unresolved\");\n\n return { _tag: \"Conflict\" } as const;\n }\n if (tuple.row[t.state] === \"Unowned\")\n yield* F.acquireTuple(mapping.ownership, token.identity, tuple.key, S.nativeCopy(native));\n yield* C.fence(mapping, cl, co);\n if (out.cleanup !== undefined) {\n yield* C.storeJob(mapping, out.cleanup, token, native, claimed.now);\n yield* F.terminal(mapping, input.claim, \"Quarantined\", \"Resolved\", { cohort: co.id });\n } else\n yield* F.terminal(mapping, input.claim, \"Quarantined\", \"Unresolved\", {\n cohort: co.id,\n sealed: grant,\n });\n\n return { _tag: targetOkay && other.rows.length === 0 ? \"Rejected\" : \"Conflict\" } as\n | { readonly _tag: \"Rejected\" }\n | { readonly _tag: \"Conflict\" };\n }\n if (tuple.row[t.state] === \"Unowned\")\n yield* F.acquireTuple(mapping.ownership, token.identity, tuple.key, S.nativeCopy(native));\n\n yield* activateGrant(mapping, grant, native, claimed.now, original !== undefined);\n yield* F.terminal(mapping, input.claim, \"Connected\", \"Resolved\", { cohort: co.id });\n owner.postconditions.push(\n sql`${mapping.clock.engineNowMillis} >= ${claimed.now} and ${mapping.clock.engineNowMillis} < ${Math.min(input.claim.claimExpiresAtMillis, token.metadata.useUntilMillis)}`,\n );\n\n return { _tag: \"Connected\", grant } as const;\n});\n"],"mappings":"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"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { digest, invariant, oauthIdentityKey, sameIdentity, sameRevision, storage, unavailable } from "./oauth-state.mjs";
|
|
2
|
+
import { both } from "./transaction-owner.mjs";
|
|
3
|
+
import { CurrentOAuthTransaction, equal } from "./oauth-owner.mjs";
|
|
4
|
+
import { exactClaim } from "./oauth-flow.mjs";
|
|
5
|
+
import { readTuple } from "./oauth-registration.mjs";
|
|
6
|
+
import { client, clientKey, cohort, current, nativeOrder, nextOrder, orderNumber, policy, readGrant, touchScope } from "./oauth-connected-state.mjs";
|
|
7
|
+
import { fence, noFormerOwner, storeJob } from "./oauth-connected-custody.mjs";
|
|
8
|
+
import { inspectTuple, sameTarget } from "./oauth-connected-flow.mjs";
|
|
9
|
+
import { activateGrant, validMetadata } from "./oauth-connected-settlement.mjs";
|
|
10
|
+
import { sql } from "drizzle-orm";
|
|
11
|
+
import { Effect, Schema } from "effect";
|
|
12
|
+
import * as M from "@yielded/auth/OAuth";
|
|
13
|
+
import { snapshotOAuthSync } from "@yielded/auth/OAuth";
|
|
14
|
+
//#region src/drizzle/oauth-connected-sign-in.ts
|
|
15
|
+
const reservationStorage = storage(M.OAuthSignInAccessClaim);
|
|
16
|
+
const configurationStorage = storage(M.OAuthConnectedConfiguration);
|
|
17
|
+
/** Reservation rows share the connected-work ledger, but have no browser
|
|
18
|
+
* snapshot or subject. They can never enter the authenticated connect flow. */
|
|
19
|
+
const claimSignIn = Effect.fn("oauthConnected.claimSignIn")(function* (mapping, input) {
|
|
20
|
+
const owner = yield* CurrentOAuthTransaction;
|
|
21
|
+
const signIn = mapping.signIn;
|
|
22
|
+
const encode = mapping.flow.encodeSignIn;
|
|
23
|
+
if (signIn === void 0 || encode === void 0) return yield* unavailable();
|
|
24
|
+
const { claim, configuration } = input;
|
|
25
|
+
const c = claim.flow.context;
|
|
26
|
+
invariant(c.access !== void 0 && configurationStorage.encode(configuration) === configurationStorage.encode({
|
|
27
|
+
...c,
|
|
28
|
+
profile: c.access
|
|
29
|
+
}));
|
|
30
|
+
if (configuration.profile.revocation === "cohort") invariant(mapping.revocation.mode === "cohort");
|
|
31
|
+
const client$1 = yield* client(mapping, configuration, true);
|
|
32
|
+
invariant(client$1 !== void 0);
|
|
33
|
+
if ((yield* exactClaim({
|
|
34
|
+
...mapping,
|
|
35
|
+
flow: signIn.flow
|
|
36
|
+
}, claim, "sign-in")) === void 0) return yield* unavailable();
|
|
37
|
+
const f = mapping.flow;
|
|
38
|
+
const key = {
|
|
39
|
+
[f.moduleId]: c.moduleId,
|
|
40
|
+
[f.flowId]: c.flowId
|
|
41
|
+
};
|
|
42
|
+
const prior = yield* owner.read(f.table, equal(f.table, key), { limit: 1 });
|
|
43
|
+
const command = yield* owner.read(f.table, equal(f.table, {
|
|
44
|
+
[f.moduleId]: c.moduleId,
|
|
45
|
+
[f.commandId]: c.commandId
|
|
46
|
+
}), { limit: 1 });
|
|
47
|
+
if (prior.rows.length || command.rows.length) return yield* unavailable();
|
|
48
|
+
yield* touchScope(mapping, client$1.scope);
|
|
49
|
+
const order = yield* nextOrder(mapping, client$1);
|
|
50
|
+
const reservation = snapshotOAuthSync(M.OAuthSignInAccessClaim, {
|
|
51
|
+
claim,
|
|
52
|
+
configuration,
|
|
53
|
+
order: String(order)
|
|
54
|
+
});
|
|
55
|
+
const inserted = yield* owner.insert(f.table, {
|
|
56
|
+
...encode(reservation),
|
|
57
|
+
...key,
|
|
58
|
+
[f.commandId]: c.commandId,
|
|
59
|
+
[f.subjectId]: null,
|
|
60
|
+
[f.clientKey]: client$1.id,
|
|
61
|
+
[f.cohortKey]: null,
|
|
62
|
+
[f.state]: "Claimed",
|
|
63
|
+
[f.version]: owner.marker,
|
|
64
|
+
[f.stateDigest]: c.stateDigest,
|
|
65
|
+
[f.snapshot]: null,
|
|
66
|
+
[f.claimId]: claim.claimId,
|
|
67
|
+
[f.claimDigest]: digest(reservationStorage.encode(reservation)),
|
|
68
|
+
[f.claimOrder]: mapping.order.encode(order),
|
|
69
|
+
[f.claimedAt]: mapping.clock.encodeInstant(claim.claimedAtMillis),
|
|
70
|
+
[f.claimExpiresAt]: mapping.clock.encodeInstant(claim.claimExpiresAtMillis),
|
|
71
|
+
[f.expiresAt]: mapping.clock.encodeInstant(c.expiresAtMillis),
|
|
72
|
+
[f.retentionUntil]: mapping.clock.encodeInstant(claim.flow.retentionUntilMillis),
|
|
73
|
+
[f.work]: "Unresolved",
|
|
74
|
+
[f.custody]: null
|
|
75
|
+
}, key);
|
|
76
|
+
prior.rows = inserted.rows;
|
|
77
|
+
command.rows = inserted.rows;
|
|
78
|
+
return reservation;
|
|
79
|
+
});
|
|
80
|
+
const exact = Effect.fn("oauthConnected.exactSignIn")(function* (mapping, reservation) {
|
|
81
|
+
const owner = yield* CurrentOAuthTransaction;
|
|
82
|
+
const f = mapping.flow;
|
|
83
|
+
const c = reservation.claim.flow.context;
|
|
84
|
+
const key = {
|
|
85
|
+
[f.moduleId]: c.moduleId,
|
|
86
|
+
[f.flowId]: c.flowId
|
|
87
|
+
};
|
|
88
|
+
const row = (yield* owner.read(f.table, equal(f.table, key), { limit: 1 })).rows[0];
|
|
89
|
+
if (row === void 0 || row[f.work] !== "Unresolved" || row[f.claimId] !== reservation.claim.claimId || row[f.claimDigest] !== digest(reservationStorage.encode(reservation))) return void 0;
|
|
90
|
+
invariant(row[f.subjectId] === null && row[f.commandId] === c.commandId && row[f.clientKey] === clientKey(reservation.configuration) && nativeOrder(mapping, row[f.claimOrder]) === orderNumber(reservation.order));
|
|
91
|
+
const now = yield* owner.now(mapping.clock);
|
|
92
|
+
invariant(now >= reservation.claim.claimedAtMillis);
|
|
93
|
+
return {
|
|
94
|
+
key,
|
|
95
|
+
row,
|
|
96
|
+
now,
|
|
97
|
+
active: row[f.state] === "Claimed" && now < reservation.claim.claimExpiresAtMillis
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
const credential = Effect.fn("oauthConnected.signInCredential")(function* (mapping, reservation, input) {
|
|
101
|
+
const owner = yield* CurrentOAuthTransaction;
|
|
102
|
+
const signIn = mapping.signIn;
|
|
103
|
+
if (signIn === void 0) return void 0;
|
|
104
|
+
const context = reservation.claim.flow.context;
|
|
105
|
+
if (input.moduleId !== context.moduleId || input.identity.provider !== context.provider || input.identity.issuer !== context.issuer) return void 0;
|
|
106
|
+
const found = yield* current(mapping, input.revision.subjectId);
|
|
107
|
+
if (found === void 0 || !sameRevision(found.revision, input.revision)) return void 0;
|
|
108
|
+
const c = signIn.credential;
|
|
109
|
+
const loginWhere = both(equal(c.table, {
|
|
110
|
+
[c.moduleId]: context.moduleId,
|
|
111
|
+
[c.credentialId]: input.credentialId,
|
|
112
|
+
[c.subjectId]: found.nativeId,
|
|
113
|
+
[c.identityKey]: oauthIdentityKey(input.identity),
|
|
114
|
+
[c.credentialRevision]: input.credentialRevision
|
|
115
|
+
}), c.activeCondition);
|
|
116
|
+
const login = yield* owner.read(c.table, loginWhere, { limit: 1 });
|
|
117
|
+
if (login.rows.length !== 1 || !c.isActiveStatus(login.rows[0][c.status])) return void 0;
|
|
118
|
+
const f = signIn.flow;
|
|
119
|
+
const verifiedWhere = equal(f.table, {
|
|
120
|
+
[f.moduleId]: context.moduleId,
|
|
121
|
+
[f.flowId]: context.flowId,
|
|
122
|
+
[f.state]: "Verified",
|
|
123
|
+
[f.claimId]: reservation.claim.claimId,
|
|
124
|
+
[f.stateDigest]: context.stateDigest,
|
|
125
|
+
[f.claimedAt]: mapping.clock.encodeInstant(reservation.claim.claimedAtMillis),
|
|
126
|
+
[f.claimExpiresAt]: mapping.clock.encodeInstant(reservation.claim.claimExpiresAtMillis)
|
|
127
|
+
});
|
|
128
|
+
if ((yield* owner.read(f.table, verifiedWhere, { limit: 1 })).rows.length !== 1) return void 0;
|
|
129
|
+
owner.postconditions.push(sql`exists(select 1 from ${c.table} where ${loginWhere})`, sql`exists(select 1 from ${f.table} where ${verifiedWhere})`);
|
|
130
|
+
if (!(yield* policy(mapping, {
|
|
131
|
+
kind: "sign-in",
|
|
132
|
+
subjectId: found.nativeId,
|
|
133
|
+
revision: found.revision,
|
|
134
|
+
credential: snapshotOAuthSync(M.OAuthCredentialSnapshot, input),
|
|
135
|
+
configuration: reservation.configuration
|
|
136
|
+
}))) return void 0;
|
|
137
|
+
return found;
|
|
138
|
+
});
|
|
139
|
+
const slot = Effect.fn("oauthConnected.signInSlot")(function* (mapping, moduleId, native, identityKey, profileKey) {
|
|
140
|
+
const owner = yield* CurrentOAuthTransaction;
|
|
141
|
+
const g = mapping.grant;
|
|
142
|
+
const row = (yield* owner.read(g.table, equal(g.table, {
|
|
143
|
+
[g.moduleId]: moduleId,
|
|
144
|
+
[g.subjectId]: native,
|
|
145
|
+
[g.profileKey]: profileKey,
|
|
146
|
+
[g.activeIdentityKey]: identityKey
|
|
147
|
+
}), {
|
|
148
|
+
limit: 1,
|
|
149
|
+
admissionOnly: true
|
|
150
|
+
})).rows[0];
|
|
151
|
+
if (row === void 0) return void 0;
|
|
152
|
+
return yield* readGrant(mapping, moduleId, yield* Schema.decodeUnknownEffect(M.OAuthGrantId)(row[g.grantId]).pipe(Effect.mapError(unavailable)));
|
|
153
|
+
});
|
|
154
|
+
/** Orders belong to one client registration. Moving a profile to another client
|
|
155
|
+
* requires a newer profile generation; retired clients cannot replace it later. */
|
|
156
|
+
const canReplace = (reservation, previous) => {
|
|
157
|
+
const generation = reservation.configuration.profile.generation;
|
|
158
|
+
const oldGeneration = previous.configuration.profile.generation;
|
|
159
|
+
return clientKey(reservation.configuration) === clientKey(previous.configuration) ? generation >= oldGeneration && orderNumber(reservation.order) > orderNumber(previous.exchangeOrder) : generation > oldGeneration;
|
|
160
|
+
};
|
|
161
|
+
const inspectSignIn = Effect.fn("oauthConnected.inspectSignIn")(function* (mapping, input) {
|
|
162
|
+
const found = yield* credential(mapping, input.reservation, input.credential);
|
|
163
|
+
if (found === void 0) return { _tag: "Rejected" };
|
|
164
|
+
const r = input.reservation;
|
|
165
|
+
const cl = yield* client(mapping, r.configuration, false);
|
|
166
|
+
if (cl === void 0) return { _tag: "Rejected" };
|
|
167
|
+
const tuple = yield* inspectTuple(mapping, input.credential.identity);
|
|
168
|
+
const t = mapping.ownership.tuple;
|
|
169
|
+
if (tuple.row?.[t.state] !== "Owned" || !mapping.subjectId.equals(tuple.row[t.subjectId], found.nativeId)) return { _tag: "Rejected" };
|
|
170
|
+
const co = yield* cohort(mapping, cl.id, tuple.key, false);
|
|
171
|
+
const previous = yield* slot(mapping, r.claim.flow.context.moduleId, found.nativeId, tuple.key, r.configuration.profile.key);
|
|
172
|
+
const held = yield* exact(mapping, r);
|
|
173
|
+
if (held === void 0) return { _tag: "Rejected" };
|
|
174
|
+
const quarantine = !held.active || co.blocked || orderNumber(r.order) <= co.cutoff || previous?.row[mapping.grant.refreshWork] === "Unresolved" || previous !== void 0 && !canReplace(r, previous.context);
|
|
175
|
+
return snapshotOAuthSync(M.OAuthSignInAccessInspection, {
|
|
176
|
+
_tag: quarantine ? "Quarantine" : "Target",
|
|
177
|
+
grantId: previous?.context.grantId ?? input.grantId,
|
|
178
|
+
cohortGeneration: co.generation,
|
|
179
|
+
...previous === void 0 ? {} : { previous: snapshotOAuthSync(M.OAuthConnectedTarget, previous.context) }
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
const settleSignIn = Effect.fn("oauthConnected.settleSignIn")(function* (mapping, input) {
|
|
183
|
+
const owner = yield* CurrentOAuthTransaction;
|
|
184
|
+
const r = input.reservation;
|
|
185
|
+
const out = input.outcome;
|
|
186
|
+
const found = out._tag === "Verified" ? yield* credential(mapping, r, out.credential) : void 0;
|
|
187
|
+
const cl = yield* client(mapping, r.configuration, false);
|
|
188
|
+
if (cl === void 0) return { _tag: "Rejected" };
|
|
189
|
+
const f = mapping.flow;
|
|
190
|
+
if (out._tag !== "Verified") {
|
|
191
|
+
const held = yield* exact(mapping, r);
|
|
192
|
+
if (held === void 0) return { _tag: "Rejected" };
|
|
193
|
+
yield* owner.update(f.table, held.key, {
|
|
194
|
+
[f.state]: out._tag,
|
|
195
|
+
[f.work]: out._tag === "Cancelled" ? "Resolved" : "Unresolved",
|
|
196
|
+
[f.version]: owner.marker
|
|
197
|
+
});
|
|
198
|
+
return { _tag: out._tag };
|
|
199
|
+
}
|
|
200
|
+
const token = out.grant.context;
|
|
201
|
+
invariant(token.moduleId === r.claim.flow.context.moduleId && token.subjectId === out.credential.revision.subjectId && sameIdentity(token.identity, out.credential.identity) && configurationStorage.encode(token.configuration) === configurationStorage.encode(r.configuration));
|
|
202
|
+
const native = yield* mapping.subjectId.toNative(token.subjectId);
|
|
203
|
+
const tuple = yield* readTuple(mapping.ownership, token.identity);
|
|
204
|
+
const t = mapping.ownership.tuple;
|
|
205
|
+
const owned = tuple.row[t.state] === "Owned" && mapping.subjectId.equals(tuple.row[t.subjectId], native);
|
|
206
|
+
const co = yield* cohort(mapping, cl.id, tuple.key, true);
|
|
207
|
+
const previous = yield* readGrant(mapping, token.moduleId, token.grantId);
|
|
208
|
+
const activeSlot = yield* slot(mapping, token.moduleId, native, tuple.key, token.configuration.profile.key);
|
|
209
|
+
const held = yield* exact(mapping, r);
|
|
210
|
+
if (held === void 0) return { _tag: "Rejected" };
|
|
211
|
+
invariant(token.exchangeOrder === r.order && validMetadata(token, held.now) && token.metadata.obtainedAtMillis >= r.claim.claimedAtMillis);
|
|
212
|
+
const target = out.previous === void 0 ? previous === void 0 : previous !== void 0 && sameTarget(out.previous, previous.context) && sameIdentity(previous.context.identity, token.identity) && previous.row[mapping.grant.refreshWork] !== "Unresolved" && canReplace(r, previous.context);
|
|
213
|
+
const previousSafe = yield* noFormerOwner(mapping, tuple.key, cl.id, native);
|
|
214
|
+
if (!owned || !previousSafe) {
|
|
215
|
+
yield* owner.update(f.table, held.key, {
|
|
216
|
+
[f.state]: "Conflict",
|
|
217
|
+
[f.version]: owner.marker
|
|
218
|
+
});
|
|
219
|
+
return { _tag: "Conflict" };
|
|
220
|
+
}
|
|
221
|
+
if (out.quarantine || found === void 0 || !held.active || !target || activeSlot !== void 0 && activeSlot.context.grantId !== token.grantId || co.blocked || orderNumber(r.order) <= co.cutoff || co.generation !== token.cohortGeneration || token.metadata.useUntilMillis <= held.now || token.configuration.profile.issuance !== "active") {
|
|
222
|
+
if (token.configuration.profile.revocation === "unsupported") yield* owner.update(f.table, held.key, {
|
|
223
|
+
[f.state]: "Rejected",
|
|
224
|
+
[f.work]: "Resolved",
|
|
225
|
+
[f.subjectId]: native,
|
|
226
|
+
[f.cohortKey]: co.id,
|
|
227
|
+
[f.custody]: null,
|
|
228
|
+
[f.version]: owner.marker
|
|
229
|
+
});
|
|
230
|
+
else {
|
|
231
|
+
yield* fence(mapping, cl, co);
|
|
232
|
+
if (out.cleanup === void 0) return yield* unavailable();
|
|
233
|
+
yield* storeJob(mapping, out.cleanup, token, native, held.now);
|
|
234
|
+
yield* owner.update(f.table, held.key, {
|
|
235
|
+
[f.state]: "Quarantined",
|
|
236
|
+
[f.work]: "Resolved",
|
|
237
|
+
[f.subjectId]: native,
|
|
238
|
+
[f.cohortKey]: co.id,
|
|
239
|
+
[f.custody]: null,
|
|
240
|
+
[f.version]: owner.marker
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
return { _tag: "Rejected" };
|
|
244
|
+
}
|
|
245
|
+
yield* activateGrant(mapping, out.grant, native, held.now, previous !== void 0);
|
|
246
|
+
yield* owner.update(f.table, held.key, {
|
|
247
|
+
[f.state]: "Connected",
|
|
248
|
+
[f.work]: "Resolved",
|
|
249
|
+
[f.subjectId]: native,
|
|
250
|
+
[f.cohortKey]: co.id,
|
|
251
|
+
[f.custody]: null,
|
|
252
|
+
[f.version]: owner.marker
|
|
253
|
+
});
|
|
254
|
+
owner.postconditions.push(sql`${mapping.clock.engineNowMillis} >= ${held.now} and ${mapping.clock.engineNowMillis} < ${Math.min(r.claim.claimExpiresAtMillis, token.metadata.useUntilMillis)}`);
|
|
255
|
+
return {
|
|
256
|
+
_tag: "Connected",
|
|
257
|
+
grant: out.grant
|
|
258
|
+
};
|
|
259
|
+
});
|
|
260
|
+
//#endregion
|
|
261
|
+
export { claimSignIn, inspectSignIn, settleSignIn };
|
|
262
|
+
|
|
263
|
+
//# sourceMappingURL=oauth-connected-sign-in.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-connected-sign-in.mjs","names":["client","S.client","S.touchScope","S.nextOrder","S.clientKey","S.nativeOrder","S.orderNumber","S.current","S.policy","S.readGrant","F.inspectTuple","S.cohort","F.readTuple","F.sameTarget","C.noFormerOwner","C.fence","C.storeJob"],"sources":["../../src/drizzle/oauth-connected-sign-in.ts"],"sourcesContent":["import * as M from \"@yielded/auth/OAuth\";\nimport { snapshotOAuthSync } from \"@yielded/auth/OAuth\";\nimport { sql } from \"drizzle-orm\";\nimport { Effect, Schema } from \"effect\";\n\nimport * as C from \"./oauth-connected-custody\";\nimport * as F from \"./oauth-connected-flow\";\nimport { activateGrant, validMetadata } from \"./oauth-connected-settlement\";\nimport * as S from \"./oauth-connected-state\";\nimport { exactClaim } from \"./oauth-flow\";\nimport { both, equal, CurrentOAuthTransaction } from \"./oauth-owner\";\nimport {\n digest,\n invariant,\n oauthIdentityKey,\n sameIdentity,\n sameRevision,\n storage,\n unavailable,\n} from \"./oauth-state\";\n\nconst reservationStorage = storage(M.OAuthSignInAccessClaim);\nconst configurationStorage = storage(M.OAuthConnectedConfiguration);\n\n/** Reservation rows share the connected-work ledger, but have no browser\n * snapshot or subject. They can never enter the authenticated connect flow. */\nexport const claimSignIn = Effect.fn(\"oauthConnected.claimSignIn\")(function* (\n mapping: S.Mapping,\n input: F.Input<\"claimSignIn\">,\n) {\n const owner = yield* CurrentOAuthTransaction;\n const signIn = mapping.signIn;\n const encode = mapping.flow.encodeSignIn;\n\n if (signIn === undefined || encode === undefined) return yield* unavailable();\n const { claim, configuration } = input;\n const c = claim.flow.context;\n\n invariant(\n c.access !== undefined &&\n configurationStorage.encode(configuration) ===\n configurationStorage.encode({ ...c, profile: c.access }),\n );\n if (configuration.profile.revocation === \"cohort\")\n invariant(mapping.revocation.mode === \"cohort\");\n const client = yield* S.client(mapping, configuration, true);\n\n invariant(client !== undefined);\n const exact = yield* exactClaim({ ...mapping, flow: signIn.flow }, claim, \"sign-in\");\n\n if (exact === undefined) return yield* unavailable();\n const f = mapping.flow;\n const key = { [f.moduleId]: c.moduleId, [f.flowId]: c.flowId };\n const prior = yield* owner.read(f.table, equal(f.table, key), { limit: 1 });\n\n const command = yield* owner.read(\n f.table,\n equal(f.table, { [f.moduleId]: c.moduleId, [f.commandId]: c.commandId }),\n { limit: 1 },\n );\n\n if (prior.rows.length || command.rows.length) return yield* unavailable();\n yield* S.touchScope(mapping, client.scope);\n const order = yield* S.nextOrder(mapping, client);\n\n const reservation = snapshotOAuthSync(M.OAuthSignInAccessClaim, {\n claim,\n configuration,\n order: String(order),\n });\n\n const inserted = yield* owner.insert(\n f.table,\n {\n ...encode(reservation),\n ...key,\n [f.commandId]: c.commandId,\n [f.subjectId]: null,\n [f.clientKey]: client.id,\n [f.cohortKey]: null,\n [f.state]: \"Claimed\",\n [f.version]: owner.marker,\n [f.stateDigest]: c.stateDigest,\n [f.snapshot]: null,\n [f.claimId]: claim.claimId,\n [f.claimDigest]: digest(reservationStorage.encode(reservation)),\n [f.claimOrder]: mapping.order.encode(order),\n [f.claimedAt]: mapping.clock.encodeInstant(claim.claimedAtMillis),\n [f.claimExpiresAt]: mapping.clock.encodeInstant(claim.claimExpiresAtMillis),\n [f.expiresAt]: mapping.clock.encodeInstant(c.expiresAtMillis),\n [f.retentionUntil]: mapping.clock.encodeInstant(claim.flow.retentionUntilMillis),\n [f.work]: \"Unresolved\",\n [f.custody]: null,\n },\n key,\n );\n\n prior.rows = inserted.rows;\n command.rows = inserted.rows;\n\n return reservation;\n});\n\nconst exact = Effect.fn(\"oauthConnected.exactSignIn\")(function* (\n mapping: S.Mapping,\n reservation: M.OAuthSignInAccessClaim,\n) {\n const owner = yield* CurrentOAuthTransaction;\n const f = mapping.flow;\n const c = reservation.claim.flow.context;\n const key = { [f.moduleId]: c.moduleId, [f.flowId]: c.flowId };\n const read = yield* owner.read(f.table, equal(f.table, key), { limit: 1 });\n const row = read.rows[0];\n\n if (\n row === undefined ||\n row[f.work] !== \"Unresolved\" ||\n row[f.claimId] !== reservation.claim.claimId ||\n row[f.claimDigest] !== digest(reservationStorage.encode(reservation))\n )\n return undefined;\n invariant(\n row[f.subjectId] === null &&\n row[f.commandId] === c.commandId &&\n row[f.clientKey] === S.clientKey(reservation.configuration) &&\n S.nativeOrder(mapping, row[f.claimOrder]) === S.orderNumber(reservation.order),\n );\n const now = yield* owner.now(mapping.clock);\n\n invariant(now >= reservation.claim.claimedAtMillis);\n\n return {\n key,\n row,\n now,\n active: row[f.state] === \"Claimed\" && now < reservation.claim.claimExpiresAtMillis,\n };\n});\n\nconst credential = Effect.fn(\"oauthConnected.signInCredential\")(function* (\n mapping: S.Mapping,\n reservation: M.OAuthSignInAccessClaim,\n input: M.OAuthCredentialSnapshot,\n) {\n const owner = yield* CurrentOAuthTransaction;\n const signIn = mapping.signIn;\n\n if (signIn === undefined) return undefined;\n const context = reservation.claim.flow.context;\n\n if (\n input.moduleId !== context.moduleId ||\n input.identity.provider !== context.provider ||\n input.identity.issuer !== context.issuer\n )\n return undefined;\n const found = yield* S.current(mapping, input.revision.subjectId);\n\n if (found === undefined || !sameRevision(found.revision, input.revision)) return undefined;\n const c = signIn.credential;\n\n const loginWhere = both(\n equal(c.table, {\n [c.moduleId]: context.moduleId,\n [c.credentialId]: input.credentialId,\n [c.subjectId]: found.nativeId,\n [c.identityKey]: oauthIdentityKey(input.identity),\n [c.credentialRevision]: input.credentialRevision,\n }),\n c.activeCondition,\n );\n\n const login = yield* owner.read(c.table, loginWhere, { limit: 1 });\n\n if (login.rows.length !== 1 || !c.isActiveStatus(login.rows[0]![c.status])) return undefined;\n const f = signIn.flow;\n\n const verifiedWhere = equal(f.table, {\n [f.moduleId]: context.moduleId,\n [f.flowId]: context.flowId,\n [f.state]: \"Verified\",\n [f.claimId]: reservation.claim.claimId,\n [f.stateDigest]: context.stateDigest,\n [f.claimedAt]: mapping.clock.encodeInstant(reservation.claim.claimedAtMillis),\n [f.claimExpiresAt]: mapping.clock.encodeInstant(reservation.claim.claimExpiresAtMillis),\n });\n\n const verified = yield* owner.read(f.table, verifiedWhere, { limit: 1 });\n\n if (verified.rows.length !== 1) return undefined;\n owner.postconditions.push(\n sql`exists(select 1 from ${c.table} where ${loginWhere})`,\n sql`exists(select 1 from ${f.table} where ${verifiedWhere})`,\n );\n if (\n !(yield* S.policy(mapping, {\n kind: \"sign-in\",\n subjectId: found.nativeId,\n revision: found.revision,\n credential: snapshotOAuthSync(M.OAuthCredentialSnapshot, input),\n configuration: reservation.configuration,\n }))\n )\n return undefined;\n\n return found;\n});\n\nconst slot = Effect.fn(\"oauthConnected.signInSlot\")(function* (\n mapping: S.Mapping,\n moduleId: string,\n native: unknown,\n identityKey: string,\n profileKey: string,\n) {\n const owner = yield* CurrentOAuthTransaction;\n const g = mapping.grant;\n\n const rows = yield* owner.read(\n g.table,\n equal(g.table, {\n [g.moduleId]: moduleId,\n [g.subjectId]: native,\n [g.profileKey]: profileKey,\n [g.activeIdentityKey]: identityKey,\n }),\n { limit: 1, admissionOnly: true },\n );\n\n const row = rows.rows[0];\n\n if (row === undefined) return undefined;\n\n return yield* S.readGrant(\n mapping,\n moduleId,\n yield* Schema.decodeUnknownEffect(M.OAuthGrantId)(row[g.grantId]).pipe(\n Effect.mapError(unavailable),\n ),\n );\n});\n\n/** Orders belong to one client registration. Moving a profile to another client\n * requires a newer profile generation; retired clients cannot replace it later. */\nconst canReplace = (\n reservation: M.OAuthSignInAccessClaim,\n previous: M.OAuthConnectedTokenContext,\n) => {\n const generation = reservation.configuration.profile.generation;\n const oldGeneration = previous.configuration.profile.generation;\n\n return S.clientKey(reservation.configuration) === S.clientKey(previous.configuration)\n ? generation >= oldGeneration &&\n S.orderNumber(reservation.order) > S.orderNumber(previous.exchangeOrder)\n : generation > oldGeneration;\n};\n\nexport const inspectSignIn = Effect.fn(\"oauthConnected.inspectSignIn\")(function* (\n mapping: S.Mapping,\n input: F.Input<\"inspectSignIn\">,\n) {\n const found = yield* credential(mapping, input.reservation, input.credential);\n\n if (found === undefined) return { _tag: \"Rejected\" } as const;\n const r = input.reservation;\n const cl = yield* S.client(mapping, r.configuration, false);\n\n if (cl === undefined) return { _tag: \"Rejected\" } as const;\n const tuple = yield* F.inspectTuple(mapping, input.credential.identity);\n const t = mapping.ownership.tuple;\n\n if (\n tuple.row?.[t.state] !== \"Owned\" ||\n !mapping.subjectId.equals(tuple.row[t.subjectId], found.nativeId)\n )\n return { _tag: \"Rejected\" } as const;\n const co = yield* S.cohort(mapping, cl.id, tuple.key, false);\n\n const previous = yield* slot(\n mapping,\n r.claim.flow.context.moduleId,\n found.nativeId,\n tuple.key,\n r.configuration.profile.key,\n );\n\n const held = yield* exact(mapping, r);\n\n if (held === undefined) return { _tag: \"Rejected\" } as const;\n\n const quarantine =\n !held.active ||\n co.blocked ||\n S.orderNumber(r.order) <= co.cutoff ||\n previous?.row[mapping.grant.refreshWork] === \"Unresolved\" ||\n (previous !== undefined && !canReplace(r, previous.context));\n\n return snapshotOAuthSync(M.OAuthSignInAccessInspection, {\n _tag: quarantine ? \"Quarantine\" : \"Target\",\n grantId: previous?.context.grantId ?? input.grantId,\n cohortGeneration: co.generation,\n ...(previous === undefined\n ? {}\n : { previous: snapshotOAuthSync(M.OAuthConnectedTarget, previous.context) }),\n });\n});\n\nexport const settleSignIn = Effect.fn(\"oauthConnected.settleSignIn\")(function* (\n mapping: S.Mapping,\n input: F.Input<\"settleSignIn\">,\n) {\n const owner = yield* CurrentOAuthTransaction;\n const r = input.reservation;\n const out = input.outcome;\n const found = out._tag === \"Verified\" ? yield* credential(mapping, r, out.credential) : undefined;\n const cl = yield* S.client(mapping, r.configuration, false);\n\n if (cl === undefined) return { _tag: \"Rejected\" } as const;\n const f = mapping.flow;\n\n if (out._tag !== \"Verified\") {\n const held = yield* exact(mapping, r);\n\n if (held === undefined) return { _tag: \"Rejected\" } as const;\n yield* owner.update(f.table, held.key, {\n [f.state]: out._tag,\n [f.work]: out._tag === \"Cancelled\" ? \"Resolved\" : \"Unresolved\",\n [f.version]: owner.marker,\n });\n\n return { _tag: out._tag };\n }\n const token = out.grant.context;\n\n invariant(\n token.moduleId === r.claim.flow.context.moduleId &&\n token.subjectId === out.credential.revision.subjectId &&\n sameIdentity(token.identity, out.credential.identity) &&\n configurationStorage.encode(token.configuration) ===\n configurationStorage.encode(r.configuration),\n );\n const native = yield* mapping.subjectId.toNative(token.subjectId);\n const tuple = yield* F.readTuple(mapping.ownership, token.identity);\n const t = mapping.ownership.tuple;\n\n const owned =\n tuple.row[t.state] === \"Owned\" && mapping.subjectId.equals(tuple.row[t.subjectId], native);\n\n const co = yield* S.cohort(mapping, cl.id, tuple.key, true);\n const previous = yield* S.readGrant(mapping, token.moduleId, token.grantId);\n\n const activeSlot = yield* slot(\n mapping,\n token.moduleId,\n native,\n tuple.key,\n token.configuration.profile.key,\n );\n\n const held = yield* exact(mapping, r);\n\n if (held === undefined) return { _tag: \"Rejected\" } as const;\n invariant(\n token.exchangeOrder === r.order &&\n validMetadata(token, held.now) &&\n token.metadata.obtainedAtMillis >= r.claim.claimedAtMillis,\n );\n\n const target =\n out.previous === undefined\n ? previous === undefined\n : previous !== undefined &&\n F.sameTarget(out.previous, previous.context) &&\n sameIdentity(previous.context.identity, token.identity) &&\n previous.row[mapping.grant.refreshWork] !== \"Unresolved\" &&\n canReplace(r, previous.context);\n\n const previousSafe = yield* C.noFormerOwner(mapping, tuple.key, cl.id, native);\n\n if (!owned || !previousSafe) {\n yield* owner.update(f.table, held.key, { [f.state]: \"Conflict\", [f.version]: owner.marker });\n\n return { _tag: \"Conflict\" } as const;\n }\n if (\n out.quarantine ||\n found === undefined ||\n !held.active ||\n !target ||\n (activeSlot !== undefined && activeSlot.context.grantId !== token.grantId) ||\n co.blocked ||\n S.orderNumber(r.order) <= co.cutoff ||\n co.generation !== token.cohortGeneration ||\n token.metadata.useUntilMillis <= held.now ||\n token.configuration.profile.issuance !== \"active\"\n ) {\n if (token.configuration.profile.revocation === \"unsupported\") {\n // No remote cleanup can run for this profile. Discard the losing material;\n // it must neither replace nor revoke the currently installed grant.\n yield* owner.update(f.table, held.key, {\n [f.state]: \"Rejected\",\n [f.work]: \"Resolved\",\n [f.subjectId]: native,\n [f.cohortKey]: co.id,\n [f.custody]: null,\n [f.version]: owner.marker,\n });\n } else {\n yield* C.fence(mapping, cl, co);\n if (out.cleanup === undefined) return yield* unavailable();\n yield* C.storeJob(mapping, out.cleanup, token, native, held.now);\n yield* owner.update(f.table, held.key, {\n [f.state]: \"Quarantined\",\n [f.work]: \"Resolved\",\n [f.subjectId]: native,\n [f.cohortKey]: co.id,\n [f.custody]: null,\n [f.version]: owner.marker,\n });\n }\n\n return { _tag: \"Rejected\" } as const;\n }\n yield* activateGrant(mapping, out.grant, native, held.now, previous !== undefined);\n yield* owner.update(f.table, held.key, {\n [f.state]: \"Connected\",\n [f.work]: \"Resolved\",\n [f.subjectId]: native,\n [f.cohortKey]: co.id,\n [f.custody]: null,\n [f.version]: owner.marker,\n });\n owner.postconditions.push(\n sql`${mapping.clock.engineNowMillis} >= ${held.now} and ${mapping.clock.engineNowMillis} < ${Math.min(r.claim.claimExpiresAtMillis, token.metadata.useUntilMillis)}`,\n );\n\n return { _tag: \"Connected\", grant: out.grant } as const;\n});\n"],"mappings":"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"}
|