@yielded/auth-persistence 0.1.0-beta.30 → 0.1.0-beta.32
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 +11 -7
- package/dist/Adapter.d.mts +1 -1
- package/dist/Adapter.mjs +8 -8
- package/dist/OAuthPersistence.mjs +10 -5
- package/dist/OAuthPersistence.mjs.map +1 -1
- package/dist/internal/configuration.d.mts +49 -13
- package/dist/internal/configuration.mjs.map +1 -1
- package/dist/internal/email-native.mjs +10 -2
- package/dist/internal/email-native.mjs.map +1 -1
- package/dist/internal/email-policy.mjs +3 -2
- package/dist/internal/email-policy.mjs.map +1 -1
- package/dist/internal/models/oauth-model.d.mts +4 -2
- package/dist/internal/models/oauth-model.mjs.map +1 -1
- package/dist/internal/models/password-model.d.mts +2 -1
- package/dist/internal/models/password-model.mjs.map +1 -1
- package/dist/internal/oauth/native-accounts.d.mts +1 -1
- package/dist/internal/oauth/native-accounts.mjs +6 -6
- package/dist/internal/oauth/native-accounts.mjs.map +1 -1
- package/dist/internal/oauth/native-connected.d.mts +1 -1
- package/dist/internal/oauth/native-connected.mjs +1 -1
- package/dist/internal/oauth/native-registration.mjs +19 -8
- package/dist/internal/oauth/native-registration.mjs.map +1 -1
- package/dist/internal/oauth/native-sign-in.d.mts +1 -1
- package/dist/internal/oauth/native-sign-in.mjs +1 -1
- package/dist/internal/oauth/native-state.mjs +21 -11
- package/dist/internal/oauth/native-state.mjs.map +1 -1
- package/dist/internal/oauth/state.d.mts +1 -1
- package/dist/internal/oauth/state.mjs +1 -1
- package/dist/internal/oauth-managed.mjs +182 -0
- package/dist/internal/oauth-managed.mjs.map +1 -0
- package/dist/internal/passkey-managed.mjs +5 -3
- package/dist/internal/passkey-managed.mjs.map +1 -1
- package/dist/internal/passkey-native-registration.mjs +1 -1
- package/dist/internal/passkey-native-state.mjs +33 -23
- package/dist/internal/passkey-native-state.mjs.map +1 -1
- package/dist/internal/passkey-native-write.mjs +3 -3
- package/dist/internal/passkey-native-write.mjs.map +1 -1
- package/dist/internal/passkey-native.d.mts +15 -7
- package/dist/internal/passkey-native.mjs +2 -1
- package/dist/internal/passkey-native.mjs.map +1 -1
- package/dist/internal/passkey-policy.mjs +9 -7
- package/dist/internal/passkey-policy.mjs.map +1 -1
- package/dist/internal/password-native.mjs +22 -5
- package/dist/internal/password-native.mjs.map +1 -1
- package/dist/internal/password-policy.mjs +7 -6
- package/dist/internal/password-policy.mjs.map +1 -1
- package/dist/internal/password-registration-native.d.mts +1 -1
- package/dist/internal/persistence.d.mts +3 -0
- package/dist/internal/persistence.mjs +99 -42
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/phone-native.mjs +1 -1
- package/dist/internal/phone-native.mjs.map +1 -1
- package/dist/internal/registration.mjs +17 -8
- package/dist/internal/registration.mjs.map +1 -1
- package/dist/internal/session-native-authority.mjs +8 -6
- package/dist/internal/session-native-authority.mjs.map +1 -1
- package/dist/internal/session-native-login.d.mts +2 -2
- package/dist/internal/session-native-login.mjs +4 -2
- package/dist/internal/session-native-login.mjs.map +1 -1
- package/dist/internal/session-native-pending-read.mjs +26 -20
- package/dist/internal/session-native-pending-read.mjs.map +1 -1
- package/dist/internal/session-native-record.mjs +6 -4
- package/dist/internal/session-native-record.mjs.map +1 -1
- package/dist/internal/session-native-state.mjs +24 -8
- package/dist/internal/session-native-state.mjs.map +1 -1
- package/dist/internal/session-native-stateful.d.mts +2 -2
- package/dist/internal/session-native-stateful.mjs +67 -38
- package/dist/internal/session-native-stateful.mjs.map +1 -1
- package/dist/internal/session-native-step-up.d.mts +2 -2
- package/dist/internal/session-native-step-up.mjs +17 -19
- package/dist/internal/session-native-step-up.mjs.map +1 -1
- package/dist/internal/session-native-validity.mjs +3 -1
- package/dist/internal/session-native-validity.mjs.map +1 -1
- package/dist/internal/sql-authority.mjs +5 -1
- package/dist/internal/sql-authority.mjs.map +1 -1
- package/dist/internal/sql-storage-validation.mjs +2 -1
- package/dist/internal/sql-storage-validation.mjs.map +1 -1
- package/dist/internal/sql-table.d.mts +1 -0
- package/dist/internal/sql-table.mjs.map +1 -1
- package/dist/internal/sqlite-policy-guard.mjs +23 -0
- package/dist/internal/sqlite-policy-guard.mjs.map +1 -0
- package/dist/internal/step-up-state.mjs +19 -6
- package/dist/internal/step-up-state.mjs.map +1 -1
- package/dist/internal/storage-mapping.d.mts +9 -5
- package/dist/internal/storage-mapping.mjs +17 -5
- package/dist/internal/storage-mapping.mjs.map +1 -1
- package/dist/internal/storage-tables.d.mts +237 -0
- package/dist/internal/storage-tables.mjs +68 -0
- package/dist/internal/storage-tables.mjs.map +1 -1
- package/dist/internal/storage-validation.d.mts +3 -1
- package/dist/internal/storage-validation.mjs +70 -17
- package/dist/internal/storage-validation.mjs.map +1 -1
- package/dist/internal/totp-native.mjs +1 -1
- package/package.json +1 -1
- package/src/OAuthPersistence.ts +7 -2
- package/src/internal/configuration.ts +72 -15
- package/src/internal/email-native.ts +16 -6
- package/src/internal/email-policy.ts +12 -3
- package/src/internal/models/oauth-model.ts +7 -1
- package/src/internal/models/password-model.ts +2 -1
- package/src/internal/oauth/native-accounts.ts +3 -3
- package/src/internal/oauth/native-registration.ts +17 -6
- package/src/internal/oauth/native-state.ts +38 -18
- package/src/internal/oauth-managed.ts +277 -0
- package/src/internal/passkey-managed.ts +8 -2
- package/src/internal/passkey-native-state.ts +59 -52
- package/src/internal/passkey-native-write.ts +2 -2
- package/src/internal/passkey-native.ts +4 -2
- package/src/internal/passkey-policy.ts +13 -9
- package/src/internal/password-native.ts +45 -11
- package/src/internal/password-policy.ts +16 -8
- package/src/internal/persistence.ts +360 -204
- package/src/internal/phone-native.ts +1 -1
- package/src/internal/registration.ts +53 -40
- package/src/internal/session-native-authority.ts +26 -8
- package/src/internal/session-native-login.ts +9 -4
- package/src/internal/session-native-pending-read.ts +46 -33
- package/src/internal/session-native-record.ts +6 -4
- package/src/internal/session-native-state.ts +32 -10
- package/src/internal/session-native-stateful.ts +147 -52
- package/src/internal/session-native-step-up.ts +31 -24
- package/src/internal/session-native-validity.ts +3 -1
- package/src/internal/sql-authority.ts +5 -1
- package/src/internal/sql-storage-validation.ts +53 -46
- package/src/internal/sql-table.ts +1 -0
- package/src/internal/sqlite-policy-guard.ts +34 -0
- package/src/internal/step-up-state.ts +43 -6
- package/src/internal/storage-mapping.ts +40 -4
- package/src/internal/storage-tables.ts +76 -0
- package/src/internal/storage-validation.ts +127 -27
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-table.mjs","names":[],"sources":["../../src/internal/sql-table.ts"],"sourcesContent":["import { Option, Schema, SchemaGetter } from \"effect\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\n\nconst SqlInteger = Schema.Union([\n Schema.Int,\n Schema.BigInt,\n Schema.String.check(Schema.isPattern(/^-?\\d+$/)),\n]).pipe(\n Schema.decodeTo(Schema.Int, {\n decode: SchemaGetter.transform(Number),\n encode: SchemaGetter.passthrough(),\n }),\n);\n\nconst decodeInteger = Schema.decodeUnknownOption(SqlInteger);\n\nexport type Dialect = \"pg\" | \"sqlite\";\nexport type Row = Readonly<Record<string, unknown>>;\n\nexport const identifier = (name: string) => '\"' + name.replaceAll('\"', '\"\"') + '\"';\n\nexport interface ColumnOptions {\n readonly name: string;\n readonly type: \"text\" | \"integer\" | \"boolean\";\n readonly nullable?: boolean;\n}\n\nexport class Column {\n constructor(\n readonly tableName: string,\n readonly options: ColumnOptions,\n readonly schema?: string,\n ) {}\n\n decode(value: unknown): unknown {\n if (value === null) return value;\n if (this.options.type === \"boolean\") {\n if (value === true || value === 1) return true;\n if (value === false || value === 0) return false;\n throw PersistenceMappingError.make({ operation: \"decode\", cause: \"Invalid SQL boolean\" });\n }\n if (this.options.type === \"integer\") {\n const number = decodeInteger(value);\n\n if (Option.isSome(number)) return number.value;\n throw PersistenceMappingError.make({\n operation: \"decode\",\n cause: \"SQL integer is outside the safe range\",\n });\n }\n\n return value;\n }\n}\n\n/** Plain SQL identifiers and column representations, independent of an ORM. */\nexport class Table {\n readonly columns: Readonly<Record<string, Column>>;\n\n constructor(\n readonly name: string,\n columns: Readonly<Record<string, ColumnOptions>>,\n readonly unique: ReadonlyArray<ReadonlyArray<string>>,\n readonly schema?: string,\n ) {\n this.columns = Object.fromEntries(\n Object.entries(columns).map(([key, options]) => [key, new Column(name, options, schema)]),\n );\n }\n}\n"],"mappings":";;;AAIA,MAAM,aAAa,OAAO,MAAM;CAC9B,OAAO;CACP,OAAO;CACP,OAAO,OAAO,MAAM,OAAO,UAAU,SAAS,CAAC;AACjD,CAAC,CAAC,CAAC,KACD,OAAO,SAAS,OAAO,KAAK;CAC1B,QAAQ,aAAa,UAAU,MAAM;CACrC,QAAQ,aAAa,YAAY;AACnC,CAAC,CACH;AAEA,MAAM,gBAAgB,OAAO,oBAAoB,UAAU;AAK3D,MAAa,cAAc,SAAiB,OAAM,KAAK,WAAW,MAAK,MAAI,IAAI;
|
|
1
|
+
{"version":3,"file":"sql-table.mjs","names":[],"sources":["../../src/internal/sql-table.ts"],"sourcesContent":["import { Option, Schema, SchemaGetter } from \"effect\";\n\nimport { PersistenceMappingError } from \"./mapping-error\";\n\nconst SqlInteger = Schema.Union([\n Schema.Int,\n Schema.BigInt,\n Schema.String.check(Schema.isPattern(/^-?\\d+$/)),\n]).pipe(\n Schema.decodeTo(Schema.Int, {\n decode: SchemaGetter.transform(Number),\n encode: SchemaGetter.passthrough(),\n }),\n);\n\nconst decodeInteger = Schema.decodeUnknownOption(SqlInteger);\n\nexport type Dialect = \"pg\" | \"sqlite\";\nexport type Row = Readonly<Record<string, unknown>>;\n\nexport const identifier = (name: string) => '\"' + name.replaceAll('\"', '\"\"') + '\"';\n\nexport interface ColumnOptions {\n readonly name: string;\n /** Driver value representation, not the database column's physical SQL type. */\n readonly type: \"text\" | \"integer\" | \"boolean\";\n readonly nullable?: boolean;\n}\n\nexport class Column {\n constructor(\n readonly tableName: string,\n readonly options: ColumnOptions,\n readonly schema?: string,\n ) {}\n\n decode(value: unknown): unknown {\n if (value === null) return value;\n if (this.options.type === \"boolean\") {\n if (value === true || value === 1) return true;\n if (value === false || value === 0) return false;\n throw PersistenceMappingError.make({ operation: \"decode\", cause: \"Invalid SQL boolean\" });\n }\n if (this.options.type === \"integer\") {\n const number = decodeInteger(value);\n\n if (Option.isSome(number)) return number.value;\n throw PersistenceMappingError.make({\n operation: \"decode\",\n cause: \"SQL integer is outside the safe range\",\n });\n }\n\n return value;\n }\n}\n\n/** Plain SQL identifiers and column representations, independent of an ORM. */\nexport class Table {\n readonly columns: Readonly<Record<string, Column>>;\n\n constructor(\n readonly name: string,\n columns: Readonly<Record<string, ColumnOptions>>,\n readonly unique: ReadonlyArray<ReadonlyArray<string>>,\n readonly schema?: string,\n ) {\n this.columns = Object.fromEntries(\n Object.entries(columns).map(([key, options]) => [key, new Column(name, options, schema)]),\n );\n }\n}\n"],"mappings":";;;AAIA,MAAM,aAAa,OAAO,MAAM;CAC9B,OAAO;CACP,OAAO;CACP,OAAO,OAAO,MAAM,OAAO,UAAU,SAAS,CAAC;AACjD,CAAC,CAAC,CAAC,KACD,OAAO,SAAS,OAAO,KAAK;CAC1B,QAAQ,aAAa,UAAU,MAAM;CACrC,QAAQ,aAAa,YAAY;AACnC,CAAC,CACH;AAEA,MAAM,gBAAgB,OAAO,oBAAoB,UAAU;AAK3D,MAAa,cAAc,SAAiB,OAAM,KAAK,WAAW,MAAK,MAAI,IAAI;AAS/E,IAAa,SAAb,MAAoB;CAEP;CACA;CACA;CAHX,YACE,WACA,SACA,QACA;EAHS,KAAA,YAAA;EACA,KAAA,UAAA;EACA,KAAA,SAAA;CACR;CAEH,OAAO,OAAyB;EAC9B,IAAI,UAAU,MAAM,OAAO;EAC3B,IAAI,KAAK,QAAQ,SAAS,WAAW;GACnC,IAAI,UAAU,QAAQ,UAAU,GAAG,OAAO;GAC1C,IAAI,UAAU,SAAS,UAAU,GAAG,OAAO;GAC3C,MAAM,wBAAwB,KAAK;IAAE,WAAW;IAAU,OAAO;GAAsB,CAAC;EAC1F;EACA,IAAI,KAAK,QAAQ,SAAS,WAAW;GACnC,MAAM,SAAS,cAAc,KAAK;GAElC,IAAI,OAAO,OAAO,MAAM,GAAG,OAAO,OAAO;GACzC,MAAM,wBAAwB,KAAK;IACjC,WAAW;IACX,OAAO;GACT,CAAC;EACH;EAEA,OAAO;CACT;AACF;;AAGA,IAAa,QAAb,MAAmB;CAIN;CAEA;CACA;CANX;CAEA,YACE,MACA,SACA,QACA,QACA;EAJS,KAAA,OAAA;EAEA,KAAA,SAAA;EACA,KAAA,SAAA;EAET,KAAK,UAAU,OAAO,YACpB,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,KAAK,aAAa,CAAC,KAAK,IAAI,OAAO,MAAM,SAAS,MAAM,CAAC,CAAC,CAC1F;CACF;AACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { appendSqlBatchStatement, registerSqlBatchPostcondition } from "./sql-commit.mjs";
|
|
2
|
+
import { Array, Effect } from "effect";
|
|
3
|
+
import { SqlClient } from "effect/sql/SqlClient";
|
|
4
|
+
//#region src/internal/sqlite-policy-guard.ts
|
|
5
|
+
/** Check original values before writes and at the end of the same atomic batch.
|
|
6
|
+
* Small groups bound parameters and expression depth without expanding retained
|
|
7
|
+
* values into hex/JSON. BINARY preserves text bytes; IS also compares NULLs. */
|
|
8
|
+
const guardSqlitePolicy = Effect.fnUntraced(function* (input) {
|
|
9
|
+
const sql = (yield* SqlClient).withoutTransforms();
|
|
10
|
+
for (const values of Array.chunksOf(input.values, 32)) {
|
|
11
|
+
const matches = sql.and(values.map(({ column, value }) => sql`${column} collate binary is ${value}`));
|
|
12
|
+
const statement = sql`select case when ${sql`exists(select 1 from ${input.table} where ${input.owner} and ${matches})`} then 1 else json_extract('[]', '$[auth-batch-precondition]') end as valid`;
|
|
13
|
+
yield* appendSqlBatchStatement(statement);
|
|
14
|
+
yield* registerSqlBatchPostcondition({
|
|
15
|
+
name: input.name,
|
|
16
|
+
statement
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
//#endregion
|
|
21
|
+
export { guardSqlitePolicy };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=sqlite-policy-guard.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-policy-guard.mjs","names":[],"sources":["../../src/internal/sqlite-policy-guard.ts"],"sourcesContent":["import { Array, Effect } from \"effect\";\nimport { SqlClient } from \"effect/sql/SqlClient\";\nimport type { Fragment } from \"effect/sql/Statement\";\n\nimport { appendSqlBatchStatement, registerSqlBatchPostcondition } from \"./sql-commit\";\n\nexport interface SqlitePolicyValue {\n readonly column: Fragment;\n readonly value: Fragment;\n}\n\n/** Check original values before writes and at the end of the same atomic batch.\n * Small groups bound parameters and expression depth without expanding retained\n * values into hex/JSON. BINARY preserves text bytes; IS also compares NULLs. */\nexport const guardSqlitePolicy = Effect.fnUntraced(function* (input: {\n readonly name: string;\n readonly table: Fragment;\n readonly owner: Fragment;\n readonly values: ReadonlyArray<SqlitePolicyValue>;\n}) {\n const sql = (yield* SqlClient).withoutTransforms();\n\n for (const values of Array.chunksOf(input.values, 32)) {\n const matches = sql.and(\n values.map(({ column, value }) => sql`${column} collate binary is ${value}`),\n );\n\n const condition = sql`exists(select 1 from ${input.table} where ${input.owner} and ${matches})`;\n const statement = sql`select case when ${condition} then 1 else json_extract('[]', '$[auth-batch-precondition]') end as valid`;\n\n yield* appendSqlBatchStatement(statement);\n yield* registerSqlBatchPostcondition({ name: input.name, statement });\n }\n});\n"],"mappings":";;;;;;;AAcA,MAAa,oBAAoB,OAAO,WAAW,WAAW,OAK3D;CACD,MAAM,OAAO,OAAO,UAAA,CAAW,kBAAkB;CAEjD,KAAK,MAAM,UAAU,MAAM,SAAS,MAAM,QAAQ,EAAE,GAAG;EACrD,MAAM,UAAU,IAAI,IAClB,OAAO,KAAK,EAAE,QAAQ,YAAY,GAAG,GAAG,OAAO,qBAAqB,OAAO,CAC7E;EAGA,MAAM,YAAY,GAAG,oBAAoB,GADpB,wBAAwB,MAAM,MAAM,SAAS,MAAM,MAAM,OAAO,QAAQ,GAC1C;EAEnD,OAAO,wBAAwB,SAAS;EACxC,OAAO,8BAA8B;GAAE,MAAM,MAAM;GAAM;EAAU,CAAC;CACtE;AACF,CAAC"}
|
|
@@ -1,27 +1,40 @@
|
|
|
1
|
-
import { sameSessionRevision } from "./session-native-state.mjs";
|
|
1
|
+
import { assessSessionAt, sameSessionRevision } from "./session-native-state.mjs";
|
|
2
2
|
import { DateTime, Effect, Schema } from "effect";
|
|
3
|
-
import { AuthenticationEvidence, SessionMetadata, SessionStepUpIntent, SessionStepUpInvalid, SessionStepUpRequirement, SessionUnavailable } from "@yielded/auth/Sessions";
|
|
3
|
+
import { AuthenticationEvidence, AuthenticationProof, SessionMetadata, SessionStepUpIntent, SessionStepUpInvalid, SessionStepUpRequirement, SessionUnavailable } from "@yielded/auth/Sessions";
|
|
4
|
+
import { AuthenticationAssurance, AuthenticationClock } from "@yielded/auth/Operations";
|
|
4
5
|
//#region src/internal/step-up-state.ts
|
|
5
6
|
const stepUpIntentCodec = Schema.fromJsonString(SessionStepUpIntent);
|
|
6
7
|
const BoundedSnapshot = Schema.String.check(Schema.isMaxLength(131072));
|
|
7
8
|
const encodeStepUpIntent = (intent) => Schema.encodeEffect(stepUpIntentCodec)(intent).pipe(Effect.flatMap(Schema.decodeEffect(BoundedSnapshot)), Effect.mapError(() => SessionUnavailable.make({})));
|
|
8
9
|
const decodeStepUpIntent = (snapshot) => Schema.decodeEffect(BoundedSnapshot)(snapshot).pipe(Effect.flatMap(Schema.decodeEffect(stepUpIntentCodec)), Effect.mapError(() => SessionUnavailable.make({})));
|
|
9
|
-
const stepUpIntentLive = (intent, kind, now)
|
|
10
|
+
const stepUpIntentLive = Effect.fnUntraced(function* (intent, kind, now) {
|
|
11
|
+
const { futureToleranceMillis } = yield* AuthenticationClock;
|
|
10
12
|
const n = DateTime.toEpochMillis(now), authenticated = DateTime.toEpochMillis(intent.sourceAuthenticatedAt), idle = DateTime.toEpochMillis(intent.sourceExpiresAt), absolute = DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt), expires = DateTime.toEpochMillis(intent.expiresAt);
|
|
11
|
-
return intent.sourceKind === kind && authenticated
|
|
12
|
-
};
|
|
13
|
+
return intent.sourceKind === kind && authenticated - n <= futureToleranceMillis && authenticated < idle && n < expires && expires <= idle && idle <= absolute;
|
|
14
|
+
});
|
|
13
15
|
const evidenceEncoding = Schema.encodeSync(Schema.fromJsonString(AuthenticationEvidence));
|
|
14
16
|
const requirementEncoding = Schema.encodeSync(Schema.fromJsonString(SessionStepUpRequirement));
|
|
15
17
|
Schema.encodeSync(Schema.fromJsonString(SessionMetadata));
|
|
18
|
+
const proofEncoding = Schema.encodeSync(Schema.fromJsonString(Schema.Array(AuthenticationProof)));
|
|
19
|
+
const assuranceEncoding = Schema.encodeSync(Schema.fromJsonString(SessionMetadata.fields.assurance));
|
|
16
20
|
/** Validate core-owned plans before driver allocation/SQL expansion. No bearer or Claims decoding. */
|
|
17
21
|
const validateStepUpPlan = Effect.fn("DrizzleStepUp.validatePlan")(function* (plan) {
|
|
18
22
|
const { intent, source, replacement, evidence } = plan;
|
|
19
23
|
const original = source.inspection;
|
|
24
|
+
const issuedAt = DateTime.toEpochMillis(replacement.inspection.session.issuedAt);
|
|
25
|
+
if (issuedAt < DateTime.toEpochMillis(original.session.issuedAt) || evidence.proofs.some((proof) => DateTime.toEpochMillis(proof.verifiedAt) > issuedAt) || issuedAt >= DateTime.toEpochMillis(replacement.inspection.session.expiresAt)) return yield* SessionStepUpInvalid.make({});
|
|
20
26
|
if (requirementEncoding(plan.profileRequirement) !== requirementEncoding(intent.requirement) || replacement.inspection.session.subjectId !== evidence.revision.subjectId || replacement.inspection.session.securityRevision !== evidence.revision.securityRevision || intent.revision.credentials.some((item) => !evidence.revision.credentials.some((actual) => actual.credentialId === item.credentialId && actual.revision === item.revision))) return yield* SessionStepUpInvalid.make({});
|
|
21
|
-
if (intent.sourceKind !== source.guard._tag || intent.sourceKind !== replacement._tag || intent.sourceSessionId !== original.session.sessionId || intent.sourceCredentialVersion !== original.credentialVersion || !sameSessionRevision(intent.revision, original.provenance.evidence.revision) || intent.revision.subjectId !== evidence.revision.subjectId || intent.revision.securityRevision !== evidence.revision.securityRevision || intent.flowId !== evidence.flowId || intent.bindingDigest !== evidence.bindingDigest || DateTime.toEpochMillis(intent.sourceAuthenticatedAt) !== DateTime.toEpochMillis(original.session.assurance.authenticatedAt) || DateTime.toEpochMillis(intent.sourceExpiresAt) !== DateTime.toEpochMillis(original.session.expiresAt) || DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !== DateTime.toEpochMillis(original.session.absoluteExpiresAt) || DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !== DateTime.toEpochMillis(replacement.inspection.session.absoluteExpiresAt) || evidenceEncoding(replacement.inspection.provenance.evidence) !== evidenceEncoding(evidence)) return yield* SessionStepUpInvalid.make({});
|
|
27
|
+
if (intent.sourceKind !== source.guard._tag || intent.sourceKind !== replacement._tag || intent.sourceSessionId !== original.session.sessionId || intent.sourceCredentialVersion !== original.credentialVersion || !sameSessionRevision(intent.revision, original.provenance.evidence.revision) || intent.revision.subjectId !== evidence.revision.subjectId || intent.revision.securityRevision !== evidence.revision.securityRevision || intent.flowId !== evidence.flowId || intent.bindingDigest !== evidence.bindingDigest || DateTime.toEpochMillis(intent.sourceAuthenticatedAt) !== DateTime.toEpochMillis(original.session.assurance.authenticatedAt) || DateTime.toEpochMillis(intent.sourceExpiresAt) !== DateTime.toEpochMillis(original.session.expiresAt) || DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !== DateTime.toEpochMillis(original.session.absoluteExpiresAt) || DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !== DateTime.toEpochMillis(replacement.inspection.session.absoluteExpiresAt) || evidenceEncoding(replacement.inspection.provenance.evidence) !== evidenceEncoding(evidence) || proofEncoding(evidence.proofs.slice(0, original.provenance.evidence.proofs.length)) !== proofEncoding(original.provenance.evidence.proofs)) return yield* SessionStepUpInvalid.make({});
|
|
22
28
|
if (replacement._tag === "Stateful" && (source.guard._tag !== "Stateful" || source.guard.digest !== replacement.expectedDigest || replacement.inspection.session.sessionId !== intent.sourceSessionId)) return yield* SessionStepUpInvalid.make({});
|
|
23
29
|
if (replacement._tag === "StateAssistedSigned" && (replacement.tombstoneSessionId !== intent.sourceSessionId || DateTime.toEpochMillis(replacement.tombstoneUntil) !== DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt))) return yield* SessionStepUpInvalid.make({});
|
|
24
30
|
if (replacement._tag !== "Stateful" && replacement.inspection.session.sessionId === intent.sourceSessionId) return yield* SessionStepUpInvalid.make({});
|
|
31
|
+
const at = DateTime.toEpochMillis(plan.now);
|
|
32
|
+
const base = yield* assessSessionAt(evidence, plan.baseRequirement, at);
|
|
33
|
+
const profile = yield* assessSessionAt(evidence, plan.profileRequirement, at);
|
|
34
|
+
if (!base.satisfied || !profile.satisfied || assuranceEncoding(AuthenticationAssurance.make({
|
|
35
|
+
...base.assurance,
|
|
36
|
+
authenticatedAt: profile.assurance.authenticatedAt
|
|
37
|
+
})) !== assuranceEncoding(replacement.inspection.session.assurance)) return yield* SessionStepUpInvalid.make({});
|
|
25
38
|
});
|
|
26
39
|
//#endregion
|
|
27
40
|
export { decodeStepUpIntent, encodeStepUpIntent, stepUpIntentCodec, stepUpIntentLive, validateStepUpPlan };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"step-up-state.mjs","names":[],"sources":["../../src/internal/step-up-state.ts"],"sourcesContent":["import {\n SessionStepUpInvalid,\n SessionUnavailable,\n AuthenticationEvidence,\n SessionMetadata,\n SessionStepUpIntent,\n SessionStepUpRequirement,\n type SessionStepUpCompletionPlan,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Schema } from \"effect\";\n\nimport { sameSessionRevision } from \"./session-native-state\";\n\nexport const stepUpIntentCodec = Schema.fromJsonString(SessionStepUpIntent);\nconst BoundedSnapshot = Schema.String.check(Schema.isMaxLength(131072));\n\nexport const encodeStepUpIntent = (intent: SessionStepUpIntent) =>\n Schema.encodeEffect(stepUpIntentCodec)(intent).pipe(\n Effect.flatMap(Schema.decodeEffect(BoundedSnapshot)),\n Effect.mapError(() => SessionUnavailable.make({})),\n );\n\nexport const decodeStepUpIntent = (snapshot: string) =>\n Schema.decodeEffect(BoundedSnapshot)(snapshot).pipe(\n Effect.flatMap(Schema.decodeEffect(stepUpIntentCodec)),\n Effect.mapError(() => SessionUnavailable.make({})),\n );\n\nexport const stepUpIntentLive = (\n intent: Omit<SessionStepUpIntent, \"version\">,\n kind: SessionStepUpIntent[\"sourceKind\"],\n now: DateTime.Utc,\n) => {\n const n = DateTime.toEpochMillis(now),\n authenticated = DateTime.toEpochMillis(intent.sourceAuthenticatedAt),\n idle = DateTime.toEpochMillis(intent.sourceExpiresAt),\n absolute = DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt),\n expires = DateTime.toEpochMillis(intent.expiresAt);\n\n return (\n intent.sourceKind === kind &&\n authenticated <= n &&\n n < expires &&\n expires <= idle &&\n idle <= absolute\n );\n};\n\nconst evidenceEncoding = Schema.encodeSync(Schema.fromJsonString(AuthenticationEvidence));\nconst requirementEncoding = Schema.encodeSync(Schema.fromJsonString(SessionStepUpRequirement));\nconst metadataEncoding = Schema.encodeSync(Schema.fromJsonString(SessionMetadata));\n\n/** Validate core-owned plans before driver allocation/SQL expansion. No bearer or Claims decoding. */\nexport const validateStepUpPlan = Effect.fn(\"DrizzleStepUp.validatePlan\")(function* <Claims>(\n plan: SessionStepUpCompletionPlan<Claims>,\n) {\n const { intent, source, replacement, evidence } = plan;\n const original = source.inspection;\n\n if (\n requirementEncoding(plan.profileRequirement) !== requirementEncoding(intent.requirement) ||\n replacement.inspection.session.subjectId !== evidence.revision.subjectId ||\n replacement.inspection.session.securityRevision !== evidence.revision.securityRevision ||\n intent.revision.credentials.some(\n (item) =>\n !evidence.revision.credentials.some(\n (actual) =>\n actual.credentialId === item.credentialId && actual.revision === item.revision,\n ),\n )\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n intent.sourceKind !== source.guard._tag ||\n intent.sourceKind !== replacement._tag ||\n intent.sourceSessionId !== original.session.sessionId ||\n intent.sourceCredentialVersion !== original.credentialVersion ||\n !sameSessionRevision(intent.revision, original.provenance.evidence.revision) ||\n intent.revision.subjectId !== evidence.revision.subjectId ||\n intent.revision.securityRevision !== evidence.revision.securityRevision ||\n intent.flowId !== evidence.flowId ||\n intent.bindingDigest !== evidence.bindingDigest ||\n DateTime.toEpochMillis(intent.sourceAuthenticatedAt) !==\n DateTime.toEpochMillis(original.session.assurance.authenticatedAt) ||\n DateTime.toEpochMillis(intent.sourceExpiresAt) !==\n DateTime.toEpochMillis(original.session.expiresAt) ||\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !==\n DateTime.toEpochMillis(original.session.absoluteExpiresAt) ||\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !==\n DateTime.toEpochMillis(replacement.inspection.session.absoluteExpiresAt) ||\n evidenceEncoding(replacement.inspection.provenance.evidence) !== evidenceEncoding(evidence)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag === \"Stateful\" &&\n (source.guard._tag !== \"Stateful\" ||\n source.guard.digest !== replacement.expectedDigest ||\n replacement.inspection.session.sessionId !== intent.sourceSessionId)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag === \"StateAssistedSigned\" &&\n (replacement.tombstoneSessionId !== intent.sourceSessionId ||\n DateTime.toEpochMillis(replacement.tombstoneUntil) !==\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt))\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag !== \"Stateful\" &&\n replacement.inspection.session.sessionId === intent.sourceSessionId\n )\n return yield* SessionStepUpInvalid.make({});\n});\n\n/** Compare a consumer-decoded encoded update/readback with the intended rotation.\n * Claims are consumer-owned; their lossless round trip remains the codec contract. */\nexport const stepUpRotationMatches = <Claims>(\n plan: SessionStepUpCompletionPlan<Claims>,\n record: StatefulSessionRecord<Claims>,\n) =>\n plan.replacement._tag === \"Stateful\" &&\n record.digest === plan.replacement.nextDigest &&\n record.credentialVersion === plan.replacement.inspection.credentialVersion &&\n metadataEncoding(record) === metadataEncoding(plan.replacement.inspection.session) &&\n evidenceEncoding(record.provenance.evidence) === evidenceEncoding(plan.evidence);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"step-up-state.mjs","names":[],"sources":["../../src/internal/step-up-state.ts"],"sourcesContent":["import { AuthenticationAssurance, AuthenticationClock } from \"@yielded/auth/Operations\";\nimport {\n SessionStepUpInvalid,\n SessionUnavailable,\n AuthenticationEvidence,\n AuthenticationProof,\n SessionMetadata,\n SessionStepUpIntent,\n SessionStepUpRequirement,\n type SessionStepUpCompletionPlan,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { DateTime, Effect, Schema } from \"effect\";\n\nimport { assessSessionAt, sameSessionRevision } from \"./session-native-state\";\n\nexport const stepUpIntentCodec = Schema.fromJsonString(SessionStepUpIntent);\nconst BoundedSnapshot = Schema.String.check(Schema.isMaxLength(131072));\n\nexport const encodeStepUpIntent = (intent: SessionStepUpIntent) =>\n Schema.encodeEffect(stepUpIntentCodec)(intent).pipe(\n Effect.flatMap(Schema.decodeEffect(BoundedSnapshot)),\n Effect.mapError(() => SessionUnavailable.make({})),\n );\n\nexport const decodeStepUpIntent = (snapshot: string) =>\n Schema.decodeEffect(BoundedSnapshot)(snapshot).pipe(\n Effect.flatMap(Schema.decodeEffect(stepUpIntentCodec)),\n Effect.mapError(() => SessionUnavailable.make({})),\n );\n\nexport const stepUpIntentLive = Effect.fnUntraced(function* (\n intent: Omit<SessionStepUpIntent, \"version\">,\n kind: SessionStepUpIntent[\"sourceKind\"],\n now: DateTime.Utc,\n): Effect.fn.Return<boolean> {\n const { futureToleranceMillis } = yield* AuthenticationClock;\n\n const n = DateTime.toEpochMillis(now),\n authenticated = DateTime.toEpochMillis(intent.sourceAuthenticatedAt),\n idle = DateTime.toEpochMillis(intent.sourceExpiresAt),\n absolute = DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt),\n expires = DateTime.toEpochMillis(intent.expiresAt);\n\n return (\n intent.sourceKind === kind &&\n authenticated - n <= futureToleranceMillis &&\n authenticated < idle &&\n n < expires &&\n expires <= idle &&\n idle <= absolute\n );\n});\n\nconst evidenceEncoding = Schema.encodeSync(Schema.fromJsonString(AuthenticationEvidence));\nconst requirementEncoding = Schema.encodeSync(Schema.fromJsonString(SessionStepUpRequirement));\nconst metadataEncoding = Schema.encodeSync(Schema.fromJsonString(SessionMetadata));\nconst proofEncoding = Schema.encodeSync(Schema.fromJsonString(Schema.Array(AuthenticationProof)));\n\nconst assuranceEncoding = Schema.encodeSync(\n Schema.fromJsonString(SessionMetadata.fields.assurance),\n);\n\n/** Validate core-owned plans before driver allocation/SQL expansion. No bearer or Claims decoding. */\nexport const validateStepUpPlan = Effect.fn(\"DrizzleStepUp.validatePlan\")(function* <Claims>(\n plan: SessionStepUpCompletionPlan<Claims>,\n) {\n const { intent, source, replacement, evidence } = plan;\n const original = source.inspection;\n const issuedAt = DateTime.toEpochMillis(replacement.inspection.session.issuedAt);\n\n if (\n issuedAt < DateTime.toEpochMillis(original.session.issuedAt) ||\n evidence.proofs.some((proof) => DateTime.toEpochMillis(proof.verifiedAt) > issuedAt) ||\n issuedAt >= DateTime.toEpochMillis(replacement.inspection.session.expiresAt)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n requirementEncoding(plan.profileRequirement) !== requirementEncoding(intent.requirement) ||\n replacement.inspection.session.subjectId !== evidence.revision.subjectId ||\n replacement.inspection.session.securityRevision !== evidence.revision.securityRevision ||\n intent.revision.credentials.some(\n (item) =>\n !evidence.revision.credentials.some(\n (actual) =>\n actual.credentialId === item.credentialId && actual.revision === item.revision,\n ),\n )\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n intent.sourceKind !== source.guard._tag ||\n intent.sourceKind !== replacement._tag ||\n intent.sourceSessionId !== original.session.sessionId ||\n intent.sourceCredentialVersion !== original.credentialVersion ||\n !sameSessionRevision(intent.revision, original.provenance.evidence.revision) ||\n intent.revision.subjectId !== evidence.revision.subjectId ||\n intent.revision.securityRevision !== evidence.revision.securityRevision ||\n intent.flowId !== evidence.flowId ||\n intent.bindingDigest !== evidence.bindingDigest ||\n DateTime.toEpochMillis(intent.sourceAuthenticatedAt) !==\n DateTime.toEpochMillis(original.session.assurance.authenticatedAt) ||\n DateTime.toEpochMillis(intent.sourceExpiresAt) !==\n DateTime.toEpochMillis(original.session.expiresAt) ||\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !==\n DateTime.toEpochMillis(original.session.absoluteExpiresAt) ||\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt) !==\n DateTime.toEpochMillis(replacement.inspection.session.absoluteExpiresAt) ||\n evidenceEncoding(replacement.inspection.provenance.evidence) !== evidenceEncoding(evidence) ||\n proofEncoding(evidence.proofs.slice(0, original.provenance.evidence.proofs.length)) !==\n proofEncoding(original.provenance.evidence.proofs)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag === \"Stateful\" &&\n (source.guard._tag !== \"Stateful\" ||\n source.guard.digest !== replacement.expectedDigest ||\n replacement.inspection.session.sessionId !== intent.sourceSessionId)\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag === \"StateAssistedSigned\" &&\n (replacement.tombstoneSessionId !== intent.sourceSessionId ||\n DateTime.toEpochMillis(replacement.tombstoneUntil) !==\n DateTime.toEpochMillis(intent.sourceAbsoluteExpiresAt))\n )\n return yield* SessionStepUpInvalid.make({});\n if (\n replacement._tag !== \"Stateful\" &&\n replacement.inspection.session.sessionId === intent.sourceSessionId\n )\n return yield* SessionStepUpInvalid.make({});\n\n const at = DateTime.toEpochMillis(plan.now);\n const base = yield* assessSessionAt(evidence, plan.baseRequirement, at);\n\n const profile = yield* assessSessionAt(evidence, plan.profileRequirement, at);\n\n // Freshness can change metadata at commit; validate the exact preparation snapshot here.\n if (\n !base.satisfied ||\n !profile.satisfied ||\n assuranceEncoding(\n AuthenticationAssurance.make({\n ...base.assurance,\n authenticatedAt: profile.assurance.authenticatedAt,\n }),\n ) !== assuranceEncoding(replacement.inspection.session.assurance)\n )\n return yield* SessionStepUpInvalid.make({});\n});\n\n/** Compare a consumer-decoded encoded update/readback with the intended rotation.\n * Claims are consumer-owned; their lossless round trip remains the codec contract. */\nexport const stepUpRotationMatches = <Claims>(\n plan: SessionStepUpCompletionPlan<Claims>,\n record: StatefulSessionRecord<Claims>,\n) =>\n plan.replacement._tag === \"Stateful\" &&\n record.digest === plan.replacement.nextDigest &&\n record.credentialVersion === plan.replacement.inspection.credentialVersion &&\n metadataEncoding(record) === metadataEncoding(plan.replacement.inspection.session) &&\n evidenceEncoding(record.provenance.evidence) === evidenceEncoding(plan.evidence);\n"],"mappings":"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"}
|
|
@@ -6,17 +6,19 @@ import { AnyPasswordPersistenceMapping } from "./models/password-model.mjs";
|
|
|
6
6
|
import { PendingAuthenticationMapping, StatefulSessionMapping } from "./models/session-model.mjs";
|
|
7
7
|
import { StorageRole } from "./storage-tables.mjs";
|
|
8
8
|
import { MappingInput } from "./configuration.mjs";
|
|
9
|
+
import { NativeSqlTables } from "./native-sql-table.mjs";
|
|
9
10
|
import { Crypto, Effect, Schema } from "effect";
|
|
11
|
+
import { SqlClient } from "effect/sql";
|
|
10
12
|
import { SecurityRevision } from "@yielded/auth/Sessions";
|
|
11
13
|
//#region src/internal/storage-mapping.d.ts
|
|
12
14
|
declare const string: (value: unknown) => string;
|
|
13
15
|
/** Derive shared row mappings from a managed or custom storage layout.
|
|
14
16
|
* Acquire Effect Crypto once; the returned allocators retain that implementation.
|
|
15
|
-
* Each factory requires its role tables to exist.
|
|
16
|
-
*
|
|
17
|
-
* authority policy for explicit services.
|
|
17
|
+
* Each factory requires its role tables to exist. Supplying native tables derives
|
|
18
|
+
* batch commit predicates and subject policy columns. Adapter authors may refine
|
|
19
|
+
* clocks and authority policy for explicit services.
|
|
18
20
|
*/
|
|
19
|
-
export declare const makeMappings: (input: MappingInput) => Effect.Effect<{
|
|
21
|
+
export declare const makeMappings: (input: MappingInput, tables?: NativeSqlTables | undefined) => Effect.Effect<{
|
|
20
22
|
table: (role: StorageRole) => object;
|
|
21
23
|
authority: () => {
|
|
22
24
|
subjectId: {
|
|
@@ -29,6 +31,7 @@ export declare const makeMappings: (input: MappingInput) => Effect.Effect<{
|
|
|
29
31
|
id: string;
|
|
30
32
|
status: string;
|
|
31
33
|
securityRevision: string;
|
|
34
|
+
requirementColumns?: string[];
|
|
32
35
|
isActiveStatus: (value: unknown) => boolean;
|
|
33
36
|
d1ActiveStatusValue: unknown;
|
|
34
37
|
activeStatusValue: unknown;
|
|
@@ -43,6 +46,7 @@ export declare const makeMappings: (input: MappingInput) => Effect.Effect<{
|
|
|
43
46
|
revision: string;
|
|
44
47
|
status: string;
|
|
45
48
|
activeStatusValue: boolean;
|
|
49
|
+
d1ActiveStatusValue: boolean;
|
|
46
50
|
isActiveStatus: (value: unknown) => value is true;
|
|
47
51
|
encodeInsert: (row: object) => {
|
|
48
52
|
active: boolean;
|
|
@@ -58,6 +62,6 @@ export declare const makeMappings: (input: MappingInput) => Effect.Effect<{
|
|
|
58
62
|
sessions: <C extends Schema.Codec<unknown, unknown, never, never>>(claims: C, moduleId: string) => StatefulSessionMapping<C["Type"], TableModel, TableModel, TableModel, TableModel, unknown, unknown>;
|
|
59
63
|
pending: <C extends Schema.Codec<unknown, unknown, never, never>>(claims: C, moduleId: string) => PendingAuthenticationMapping<C["Type"], TableModel, TableModel, TableModel, unknown>;
|
|
60
64
|
string: typeof string;
|
|
61
|
-
}, PersistenceMappingError, Crypto.Crypto |
|
|
65
|
+
}, PersistenceMappingError, Crypto.Crypto | SqlClient.SqlClient>;
|
|
62
66
|
//#endregion
|
|
63
67
|
//# sourceMappingURL=storage-mapping.d.mts.map
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { PersistenceMappingError } from "./mapping-error.mjs";
|
|
2
|
+
import { exactSqlText } from "./sql-change.mjs";
|
|
2
3
|
import { randomId } from "./crypto.mjs";
|
|
4
|
+
import { makeStorageClock } from "./storage-clock.mjs";
|
|
3
5
|
import { storageTables } from "./storage-tables.mjs";
|
|
4
6
|
import { requiredEmailAddressConstraints } from "./models/email-model.mjs";
|
|
5
7
|
import { requiredPasswordConstraints } from "./models/password-model.mjs";
|
|
6
8
|
import { requiredProofConstraints } from "./models/proof-model.mjs";
|
|
7
|
-
import { makeStorageClock } from "./storage-clock.mjs";
|
|
8
9
|
import { Crypto, DateTime, Effect, Redacted, Schema, Struct } from "effect";
|
|
10
|
+
import { SqlClient } from "effect/sql";
|
|
9
11
|
import { EmailCredentialSnapshot } from "@yielded/auth/Email";
|
|
10
12
|
import { EncodedPasswordHash, PasswordCredentialSnapshot } from "@yielded/auth/Password";
|
|
11
13
|
import { AuthenticationEvidence, SecurityRevision, SessionAuthenticationProvenance, SessionCredentialVersion, SessionId, SessionMetadata } from "@yielded/auth/Sessions";
|
|
@@ -20,12 +22,13 @@ const decode = (schema, value) => Schema.decodeUnknownEffect(schema)(value).pipe
|
|
|
20
22
|
const string = (value) => Schema.decodeUnknownSync(Schema.String)(value);
|
|
21
23
|
/** Derive shared row mappings from a managed or custom storage layout.
|
|
22
24
|
* Acquire Effect Crypto once; the returned allocators retain that implementation.
|
|
23
|
-
* Each factory requires its role tables to exist.
|
|
24
|
-
*
|
|
25
|
-
* authority policy for explicit services.
|
|
25
|
+
* Each factory requires its role tables to exist. Supplying native tables derives
|
|
26
|
+
* batch commit predicates and subject policy columns. Adapter authors may refine
|
|
27
|
+
* clocks and authority policy for explicit services.
|
|
26
28
|
*/
|
|
27
|
-
const makeMappings = Effect.fnUntraced(function* (input) {
|
|
29
|
+
const makeMappings = Effect.fnUntraced(function* (input, tables) {
|
|
28
30
|
const crypto = yield* Crypto.Crypto;
|
|
31
|
+
const sql = (yield* SqlClient.SqlClient).withoutTransforms();
|
|
29
32
|
const allocate = randomId.pipe(Effect.provideService(Crypto.Crypto, crypto), Effect.mapError(failure));
|
|
30
33
|
const table = (role) => {
|
|
31
34
|
const found = input.tables[role];
|
|
@@ -42,11 +45,14 @@ const makeMappings = Effect.fnUntraced(function* (input) {
|
|
|
42
45
|
toSubject: s.toSubject,
|
|
43
46
|
equals: Object.is
|
|
44
47
|
};
|
|
48
|
+
const identifierTable = tables?.(table("identifiers"));
|
|
49
|
+
const activeIdentifier = identifierTable === void 0 ? void 0 : sql`${identifierTable.column("active")} = ${identifierTable.value("active", true)}`;
|
|
45
50
|
const subject = {
|
|
46
51
|
table: s.table,
|
|
47
52
|
id: s.id,
|
|
48
53
|
status: s.status,
|
|
49
54
|
securityRevision: s.securityRevision,
|
|
55
|
+
...tables === void 0 ? {} : { requirementColumns: tables(s.table).keys.filter((key) => key !== s.id && key !== s.status && key !== s.securityRevision) },
|
|
50
56
|
isActiveStatus: (value) => Object.is(value, s.activeValue),
|
|
51
57
|
d1ActiveStatusValue: s.activeValue,
|
|
52
58
|
activeStatusValue: s.activeValue,
|
|
@@ -61,6 +67,7 @@ const makeMappings = Effect.fnUntraced(function* (input) {
|
|
|
61
67
|
revision: "revision",
|
|
62
68
|
status: "active",
|
|
63
69
|
activeStatusValue: true,
|
|
70
|
+
d1ActiveStatusValue: true,
|
|
64
71
|
isActiveStatus: (value) => value === true,
|
|
65
72
|
encodeInsert: (row) => ({
|
|
66
73
|
...row,
|
|
@@ -120,6 +127,7 @@ const makeMappings = Effect.fnUntraced(function* (input) {
|
|
|
120
127
|
verifiedAt: "verifiedAt",
|
|
121
128
|
bindingRevision: "revision",
|
|
122
129
|
isCurrent: (row) => row.active === true,
|
|
130
|
+
...activeIdentifier === void 0 ? {} : { d1CurrentCondition: () => activeIdentifier },
|
|
123
131
|
encodeInitialInsert: (identifier, subjectId, revision) => ({
|
|
124
132
|
...identifier,
|
|
125
133
|
subjectId,
|
|
@@ -216,6 +224,10 @@ const makeMappings = Effect.fnUntraced(function* (input) {
|
|
|
216
224
|
bindingRevision: "revision",
|
|
217
225
|
isCurrent: (row) => row.active === true && row.verifiedAt !== null,
|
|
218
226
|
isMutableTarget: (row) => row.active === true && row.verifiedAt === null,
|
|
227
|
+
...identifierTable === void 0 || activeIdentifier === void 0 ? {} : {
|
|
228
|
+
d1CurrentCondition: ({ bindingRevision }) => sql`${activeIdentifier} and ${identifierTable.column("verifiedAt")} is not null and ${exactSqlText(sql, identifierTable.column("revision"), identifierTable.value("revision", bindingRevision))}`,
|
|
229
|
+
d1MutableTargetCondition: () => sql`${activeIdentifier} and ${identifierTable.column("verifiedAt")} is null`
|
|
230
|
+
},
|
|
219
231
|
encodeVerifiedInsert: ({ identifier, subjectId, verifiedAtMillis, bindingRevision }) => ({
|
|
220
232
|
...identifier,
|
|
221
233
|
subjectId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage-mapping.mjs","names":[],"sources":["../../src/internal/storage-mapping.ts"],"sourcesContent":["import { EmailCredentialSnapshot } from \"@yielded/auth/Email\";\nimport { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport {\n PasswordCredentialSnapshot,\n EncodedPasswordHash,\n type PasswordReplacement,\n} from \"@yielded/auth/Password\";\nimport { TokenDigest } from \"@yielded/auth/Schema\";\nimport {\n SecurityRevision,\n AuthenticationEvidence,\n SessionId,\n SessionMetadata,\n SessionAuthenticationProvenance,\n SessionCredentialVersion,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { Crypto, DateTime, Effect, Redacted, Schema, Struct } from \"effect\";\n\nimport type { MappingInput } from \"./configuration\";\nimport { randomId } from \"./crypto\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport { requiredEmailAddressConstraints, type AnyEmailAddressMapping } from \"./models/email-model\";\nimport {\n requiredPasswordConstraints,\n type AnyPasswordPersistenceMapping,\n} from \"./models/password-model\";\nimport { requiredProofConstraints, type AnyProofPersistenceMapping } from \"./models/proof-model\";\nimport type { StatefulSessionMapping, PendingAuthenticationMapping } from \"./models/session-model\";\nimport { makeStorageClock } from \"./storage-clock\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\nimport type { TableModel, SqlExpression } from \"./table-model\";\n\nconst failure = (cause: unknown) => PersistenceMappingError.make({ operation: \"decode\", cause });\n\nconst decode = <S extends Schema.Codec<unknown, unknown>>(schema: S, value: unknown) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(failure));\n\nconst string = (value: unknown) => Schema.decodeUnknownSync(Schema.String)(value);\n\n/** Derive shared row mappings from a managed or custom storage layout.\n * Acquire Effect Crypto once; the returned allocators retain that implementation.\n * Each factory requires its role tables to exist. Adapter authors supply typed\n * table handles and dialect-specific clocks/commit predicates, and may refine\n * authority policy for explicit services. This does not alter composed defaults.\n */\nexport const makeMappings = Effect.fnUntraced(function* (input: MappingInput) {\n const crypto = yield* Crypto.Crypto;\n\n const allocate = randomId.pipe(\n Effect.provideService(Crypto.Crypto, crypto),\n Effect.mapError(failure),\n );\n\n const table = (role: StorageRole) => {\n const found = input.tables[role];\n\n if (found === undefined) throw failure(`Missing ${role} mapping`);\n\n return found;\n };\n\n const mapped = (role: StorageRole) => ({\n table: table(role),\n ...Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])),\n });\n\n const { subjects: s, encodeInstant: instant, decodeInstant: readInstant } = input;\n const subjectId = { toNative: s.toNative, toSubject: s.toSubject, equals: Object.is };\n\n const subject = {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n isActiveStatus: (value: unknown) => Object.is(value, s.activeValue),\n d1ActiveStatusValue: s.activeValue,\n activeStatusValue: s.activeValue,\n decodeRequirement: s.requirements,\n decodeActionRequirement: s.actionRequirements,\n nextSecurityRevision: () => allocate.pipe(Effect.map(SecurityRevision.make)),\n };\n\n const authorityCredential = () => ({\n ...mapped(\"credentials\"),\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n revision: \"revision\",\n status: \"active\",\n activeStatusValue: true,\n isActiveStatus: (value: unknown) => value === true,\n encodeInsert: (row: object) => ({ ...row, active: true }),\n encodeRevision: (revision: SecurityRevision) => ({ revision }),\n });\n\n const authority = () => ({ subjectId, subject, credential: authorityCredential() });\n\n const clock = yield* makeStorageClock(input);\n\n const proofs = (): AnyProofPersistenceMapping<SqlExpression> => ({\n constraints: requiredProofConstraints,\n subjectId,\n subject: { table: s.table, id: s.id },\n clock,\n proof: {\n ...mapped(\"proofs\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n seriesKey: \"seriesKey\",\n proofId: \"proofId\",\n binding: \"binding\",\n verifierKeyId: \"verifierKeyId\",\n verifierDigest: \"verifierDigest\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n failedAttempts: \"failedAttempts\",\n encodeInsert: () => ({}),\n },\n });\n\n const passwords = (): AnyPasswordPersistenceMapping<SqlExpression> => {\n const verifier = (replacement: PasswordReplacement) => ({\n verifier: Redacted.value(replacement.verifier),\n normalization: replacement.normalization,\n });\n\n return {\n subjectId,\n subject,\n authorityCredential: authorityCredential(),\n constraints: requiredPasswordConstraints,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n sessionInvalidation: \"same-authority-immediate\",\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true,\n encodeInitialInsert: (identifier, subjectId, revision) => ({\n ...identifier,\n subjectId,\n revision,\n verifiedAt: null,\n active: true,\n }),\n },\n credential: {\n ...mapped(\"passwords\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n credentialRevision: \"credentialRevision\",\n verifierVersion: \"verifierVersion\",\n verifier: \"verifier\",\n normalization: \"normalization\",\n encodeInsert: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n encodeVerifier: (hash, verifierVersion) => ({\n verifier: Redacted.value(hash),\n verifierVersion,\n }),\n encodeReplacement: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n const id = yield* s.toSubject(row[s.id]);\n const hash = yield* decode(EncodedPasswordHash, credential.verifier);\n\n return yield* decode(PasswordCredentialSnapshot, {\n moduleId,\n revision: {\n subjectId: id,\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n verifierVersion: credential.verifierVersion,\n verifier: hash,\n normalization: credential.normalization,\n identifier: yield* decode(LoginIdentifier, {\n namespace: identifier.namespace,\n value: identifier.value,\n }),\n identifierBindingRevision: identifier.revision,\n ...(identifier.verifiedAt === null\n ? {}\n : { identifierVerifiedAtMillis: yield* readInstant(identifier.verifiedAt) }),\n });\n }),\n },\n clock,\n };\n };\n\n const emails = (): AnyEmailAddressMapping<SqlExpression> => ({\n subjectId,\n subject: { ...subject, activeStatusValue: s.activeValue },\n constraints: requiredEmailAddressConstraints,\n addressCardinality: \"single\",\n changeDisposition: \"retire-source\",\n clock,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n sessionInvalidation: \"same-authority-immediate\",\n isIdentifierConflict: () => false,\n isCredentialConflict: () => false,\n authorityCredential: {\n ...authorityCredential(),\n encodeActivation: (revision) => ({ revision, active: true }),\n encodeRetirement: (revision) => ({ revision, active: false }),\n },\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true && row.verifiedAt !== null,\n isMutableTarget: (row) => row.active === true && row.verifiedAt === null,\n encodeVerifiedInsert: ({ identifier, subjectId, verifiedAtMillis, bindingRevision }) => ({\n ...identifier,\n subjectId,\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeVerification: ({ verifiedAtMillis, bindingRevision }) => ({\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeRetirement: ({ bindingRevision }) => ({ revision: bindingRevision, active: false }),\n },\n credential: {\n activeStatusValue: true,\n table: table(\"emailCredentials\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n identifierNamespace: \"identifierNamespace\",\n identifierValue: \"identifierValue\",\n credentialRevision: \"credentialRevision\",\n status: \"active\",\n isActiveStatus: (value) => value === true,\n encodeVerifiedInsert: ({ identifier, ...row }) => ({\n ...row,\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n active: true,\n }),\n encodeActivation: ({ identifier, credentialRevision }) => ({\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n credentialRevision,\n active: true,\n }),\n encodeRetirement: ({ credentialRevision }) => ({ credentialRevision, active: false }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n return yield* decode(\n Schema.Struct(Struct.omit(EmailCredentialSnapshot.fields, [\"requirement\"])),\n {\n moduleId,\n identifier: { namespace: identifier.namespace, value: identifier.value },\n identifierRevision: identifier.revision,\n verifiedAtMillis: yield* readInstant(identifier.verifiedAt),\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n revision: {\n subjectId: yield* s.toSubject(row[s.id]),\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n },\n );\n }),\n },\n });\n\n const pending = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n moduleId: string,\n ): PendingAuthenticationMapping<C[\"Type\"], TableModel, TableModel, TableModel, unknown> => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n digest: TokenDigest,\n version: SecurityRevision,\n evidence: AuthenticationEvidence,\n expiresAt: Schema.DateTimeUtcFromMillis,\n attemptLimit: Schema.Int.check(Schema.isGreaterThan(0)),\n claims: Schema.Unknown,\n }),\n );\n\n return {\n ...authority(),\n moduleId,\n clock,\n isConstraintConflict: () => false,\n constraints: { pendingDigest: \"unique(pending.digest)\" },\n pending: {\n table: table(\"pending\"),\n moduleId: \"moduleId\",\n kind: \"kind\",\n digest: \"digest\",\n version: \"version\",\n flowId: \"flowId\",\n subjectId: \"subjectId\",\n bindingDigest: \"bindingDigest\",\n snapshot: \"snapshot\",\n expiresAt: \"expiresAt\",\n attemptLimit: \"attemptLimit\",\n failedAttempts: \"failedAttempts\",\n consumed: \"consumed\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateVersion: allocate.pipe(Effect.map(SecurityRevision.make)),\n encodeInsert: (value) => ({\n ...value,\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n failedAttempts: 0,\n consumed: false,\n }),\n },\n login: {\n encode: (value) =>\n Schema.encodeEffect(claims)(value.claims).pipe(\n Effect.flatMap((saved) => Schema.encodeEffect(record)({ ...value, claims: saved })),\n Effect.mapError(failure),\n ),\n decode: (value) =>\n decode(record, value).pipe(\n Effect.flatMap((saved) =>\n decode(claims, saved.claims).pipe(\n Effect.map((decoded) => ({ ...saved, claims: decoded })),\n ),\n ),\n ),\n },\n };\n };\n\n const sessions = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n moduleId: string,\n ): StatefulSessionMapping<\n C[\"Type\"],\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n unknown,\n unknown\n > => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n ...SessionMetadata.fields,\n claims: Schema.Unknown,\n digest: TokenDigest,\n provenance: SessionAuthenticationProvenance,\n credentialVersion: SessionCredentialVersion,\n }),\n );\n\n const encodeRow = (value: StatefulSessionRecord<C[\"Type\"]>) => ({\n digest: value.digest,\n securityRevision: value.securityRevision,\n issuedAt: instant(DateTime.toEpochMillis(value.issuedAt)),\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n absoluteExpiresAt: instant(DateTime.toEpochMillis(value.absoluteExpiresAt)),\n record: Schema.encodeSync(record)({\n ...value,\n claims: Schema.encodeSync(claims)(value.claims),\n }),\n });\n\n const login = pending(claims, moduleId);\n\n return {\n ...authority(),\n moduleId,\n clock,\n isConstraintConflict: () => false,\n pending: { pending: login.pending, login: login.login },\n constraints: {\n sessionDigest: \"unique(session.digest)\",\n pendingDigest: \"unique(pending.digest)\",\n },\n sessionId: {\n toNative: (id) => Effect.succeed(id),\n toSession: (id) => decode(SessionId, id),\n equals: Object.is,\n },\n session: {\n table: table(\"sessions\"),\n sessionId: \"sessionId\",\n subjectId: \"subjectId\",\n digest: \"digest\",\n securityRevision: \"securityRevision\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n absoluteExpiresAt: \"absoluteExpiresAt\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateId: allocate,\n encodeInsert: (value, ids) => ({ ...encodeRow(value), ...ids }),\n encodeRotation: encodeRow,\n decode: (row) =>\n Effect.gen(function* () {\n const saved = yield* decode(record, row.record),\n decodedClaims = yield* decode(claims, saved.claims);\n\n return {\n ...saved,\n issuedAt: DateTime.makeUnsafe(yield* readInstant(row.issuedAt)),\n claims: decodedClaims,\n };\n }),\n },\n };\n };\n\n return { table, authority, proofs, passwords, emails, sessions, pending, string };\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"storage-mapping.mjs","names":[],"sources":["../../src/internal/storage-mapping.ts"],"sourcesContent":["import { EmailCredentialSnapshot } from \"@yielded/auth/Email\";\nimport { LoginIdentifier } from \"@yielded/auth/Identity\";\nimport {\n PasswordCredentialSnapshot,\n EncodedPasswordHash,\n type PasswordReplacement,\n} from \"@yielded/auth/Password\";\nimport { TokenDigest } from \"@yielded/auth/Schema\";\nimport {\n SecurityRevision,\n AuthenticationEvidence,\n SessionId,\n SessionMetadata,\n SessionAuthenticationProvenance,\n SessionCredentialVersion,\n type StatefulSessionRecord,\n} from \"@yielded/auth/Sessions\";\nimport { Crypto, DateTime, Effect, Redacted, Schema, Struct } from \"effect\";\nimport { SqlClient } from \"effect/sql\";\n\nimport type { MappingInput } from \"./configuration\";\nimport { randomId } from \"./crypto\";\nimport { PersistenceMappingError } from \"./mapping-error\";\nimport { requiredEmailAddressConstraints, type AnyEmailAddressMapping } from \"./models/email-model\";\nimport {\n requiredPasswordConstraints,\n type AnyPasswordPersistenceMapping,\n} from \"./models/password-model\";\nimport { requiredProofConstraints, type AnyProofPersistenceMapping } from \"./models/proof-model\";\nimport type { StatefulSessionMapping, PendingAuthenticationMapping } from \"./models/session-model\";\nimport type { NativeSqlTables } from \"./native-sql-table\";\nimport { exactSqlText } from \"./sql-change\";\nimport { makeStorageClock } from \"./storage-clock\";\nimport { storageTables, type StorageRole } from \"./storage-tables\";\nimport type { TableModel, SqlExpression } from \"./table-model\";\n\nconst failure = (cause: unknown) => PersistenceMappingError.make({ operation: \"decode\", cause });\n\nconst decode = <S extends Schema.Codec<unknown, unknown>>(schema: S, value: unknown) =>\n Schema.decodeUnknownEffect(schema)(value).pipe(Effect.mapError(failure));\n\nconst string = (value: unknown) => Schema.decodeUnknownSync(Schema.String)(value);\n\n/** Derive shared row mappings from a managed or custom storage layout.\n * Acquire Effect Crypto once; the returned allocators retain that implementation.\n * Each factory requires its role tables to exist. Supplying native tables derives\n * batch commit predicates and subject policy columns. Adapter authors may refine\n * clocks and authority policy for explicit services.\n */\nexport const makeMappings = Effect.fnUntraced(function* (\n input: MappingInput,\n tables?: NativeSqlTables,\n) {\n const crypto = yield* Crypto.Crypto;\n const sql = (yield* SqlClient.SqlClient).withoutTransforms();\n\n const allocate = randomId.pipe(\n Effect.provideService(Crypto.Crypto, crypto),\n Effect.mapError(failure),\n );\n\n const table = (role: StorageRole) => {\n const found = input.tables[role];\n\n if (found === undefined) throw failure(`Missing ${role} mapping`);\n\n return found;\n };\n\n const mapped = (role: StorageRole) => ({\n table: table(role),\n ...Object.fromEntries(Object.keys(storageTables[role].columns).map((key) => [key, key])),\n });\n\n const { subjects: s, encodeInstant: instant, decodeInstant: readInstant } = input;\n const subjectId = { toNative: s.toNative, toSubject: s.toSubject, equals: Object.is };\n const identifierTable = tables?.(table(\"identifiers\"));\n\n const activeIdentifier =\n identifierTable === undefined\n ? undefined\n : sql`${identifierTable.column(\"active\")} = ${identifierTable.value(\"active\", true)}`;\n\n const subject = {\n table: s.table,\n id: s.id,\n status: s.status,\n securityRevision: s.securityRevision,\n // Policy callbacks may read any application column. Identity, status and\n // security revision have their own final checks and can be intentionally revised.\n ...(tables === undefined\n ? {}\n : {\n requirementColumns: tables(s.table).keys.filter(\n (key) => key !== s.id && key !== s.status && key !== s.securityRevision,\n ),\n }),\n isActiveStatus: (value: unknown) => Object.is(value, s.activeValue),\n d1ActiveStatusValue: s.activeValue,\n activeStatusValue: s.activeValue,\n decodeRequirement: s.requirements,\n decodeActionRequirement: s.actionRequirements,\n nextSecurityRevision: () => allocate.pipe(Effect.map(SecurityRevision.make)),\n };\n\n const authorityCredential = () => ({\n ...mapped(\"credentials\"),\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n revision: \"revision\",\n status: \"active\",\n activeStatusValue: true,\n d1ActiveStatusValue: true,\n isActiveStatus: (value: unknown) => value === true,\n encodeInsert: (row: object) => ({ ...row, active: true }),\n encodeRevision: (revision: SecurityRevision) => ({ revision }),\n });\n\n const authority = () => ({ subjectId, subject, credential: authorityCredential() });\n\n const clock = yield* makeStorageClock(input);\n\n const proofs = (): AnyProofPersistenceMapping<SqlExpression> => ({\n constraints: requiredProofConstraints,\n subjectId,\n subject: { table: s.table, id: s.id },\n clock,\n proof: {\n ...mapped(\"proofs\"),\n moduleId: \"moduleId\",\n purpose: \"purpose\",\n seriesKey: \"seriesKey\",\n proofId: \"proofId\",\n binding: \"binding\",\n verifierKeyId: \"verifierKeyId\",\n verifierDigest: \"verifierDigest\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n failedAttempts: \"failedAttempts\",\n encodeInsert: () => ({}),\n },\n });\n\n const passwords = (): AnyPasswordPersistenceMapping<SqlExpression> => {\n const verifier = (replacement: PasswordReplacement) => ({\n verifier: Redacted.value(replacement.verifier),\n normalization: replacement.normalization,\n });\n\n return {\n subjectId,\n subject,\n authorityCredential: authorityCredential(),\n constraints: requiredPasswordConstraints,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n sessionInvalidation: \"same-authority-immediate\",\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true,\n ...(activeIdentifier === undefined\n ? {}\n : {\n d1CurrentCondition: () => activeIdentifier,\n }),\n encodeInitialInsert: (identifier, subjectId, revision) => ({\n ...identifier,\n subjectId,\n revision,\n verifiedAt: null,\n active: true,\n }),\n },\n credential: {\n ...mapped(\"passwords\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n credentialRevision: \"credentialRevision\",\n verifierVersion: \"verifierVersion\",\n verifier: \"verifier\",\n normalization: \"normalization\",\n encodeInsert: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n encodeVerifier: (hash, verifierVersion) => ({\n verifier: Redacted.value(hash),\n verifierVersion,\n }),\n encodeReplacement: ({ replacement, ...row }) => ({ ...row, ...verifier(replacement) }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n const id = yield* s.toSubject(row[s.id]);\n const hash = yield* decode(EncodedPasswordHash, credential.verifier);\n\n return yield* decode(PasswordCredentialSnapshot, {\n moduleId,\n revision: {\n subjectId: id,\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n verifierVersion: credential.verifierVersion,\n verifier: hash,\n normalization: credential.normalization,\n identifier: yield* decode(LoginIdentifier, {\n namespace: identifier.namespace,\n value: identifier.value,\n }),\n identifierBindingRevision: identifier.revision,\n ...(identifier.verifiedAt === null\n ? {}\n : { identifierVerifiedAtMillis: yield* readInstant(identifier.verifiedAt) }),\n });\n }),\n },\n clock,\n };\n };\n\n const emails = (): AnyEmailAddressMapping<SqlExpression> => ({\n subjectId,\n subject: { ...subject, activeStatusValue: s.activeValue },\n constraints: requiredEmailAddressConstraints,\n addressCardinality: \"single\",\n changeDisposition: \"retire-source\",\n clock,\n encodeInstant: instant,\n decodeInstant: readInstant,\n allocateCredentialId: allocate,\n allocateRevision: allocate.pipe(Effect.map(SecurityRevision.make)),\n sessionInvalidation: \"same-authority-immediate\",\n isIdentifierConflict: () => false,\n isCredentialConflict: () => false,\n authorityCredential: {\n ...authorityCredential(),\n encodeActivation: (revision) => ({ revision, active: true }),\n encodeRetirement: (revision) => ({ revision, active: false }),\n },\n identifier: {\n table: table(\"identifiers\"),\n namespace: \"namespace\",\n value: \"value\",\n subjectId: \"subjectId\",\n verifiedAt: \"verifiedAt\",\n bindingRevision: \"revision\",\n isCurrent: (row) => row.active === true && row.verifiedAt !== null,\n isMutableTarget: (row) => row.active === true && row.verifiedAt === null,\n ...(identifierTable === undefined || activeIdentifier === undefined\n ? {}\n : {\n d1CurrentCondition: ({ bindingRevision }) =>\n sql`${activeIdentifier} and ${identifierTable.column(\"verifiedAt\")} is not null and ${exactSqlText(sql, identifierTable.column(\"revision\"), identifierTable.value(\"revision\", bindingRevision))}`,\n d1MutableTargetCondition: () =>\n sql`${activeIdentifier} and ${identifierTable.column(\"verifiedAt\")} is null`,\n }),\n encodeVerifiedInsert: ({ identifier, subjectId, verifiedAtMillis, bindingRevision }) => ({\n ...identifier,\n subjectId,\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeVerification: ({ verifiedAtMillis, bindingRevision }) => ({\n verifiedAt: instant(verifiedAtMillis),\n revision: bindingRevision,\n active: true,\n }),\n encodeRetirement: ({ bindingRevision }) => ({ revision: bindingRevision, active: false }),\n },\n credential: {\n activeStatusValue: true,\n table: table(\"emailCredentials\"),\n moduleId: \"moduleId\",\n subjectId: \"subjectId\",\n credentialId: \"credentialId\",\n identifierNamespace: \"identifierNamespace\",\n identifierValue: \"identifierValue\",\n credentialRevision: \"credentialRevision\",\n status: \"active\",\n isActiveStatus: (value) => value === true,\n encodeVerifiedInsert: ({ identifier, ...row }) => ({\n ...row,\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n active: true,\n }),\n encodeActivation: ({ identifier, credentialRevision }) => ({\n identifierNamespace: identifier.namespace,\n identifierValue: identifier.value,\n credentialRevision,\n active: true,\n }),\n encodeRetirement: ({ credentialRevision }) => ({ credentialRevision, active: false }),\n decode: ({ moduleId, subject: row, identifier, credential }) =>\n Effect.gen(function* () {\n return yield* decode(\n Schema.Struct(Struct.omit(EmailCredentialSnapshot.fields, [\"requirement\"])),\n {\n moduleId,\n identifier: { namespace: identifier.namespace, value: identifier.value },\n identifierRevision: identifier.revision,\n verifiedAtMillis: yield* readInstant(identifier.verifiedAt),\n credentialId: credential.credentialId,\n credentialRevision: credential.credentialRevision,\n revision: {\n subjectId: yield* s.toSubject(row[s.id]),\n securityRevision: row[s.securityRevision],\n credentials: [\n {\n credentialId: credential.credentialId,\n revision: credential.credentialRevision,\n },\n ],\n },\n },\n );\n }),\n },\n });\n\n const pending = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n moduleId: string,\n ): PendingAuthenticationMapping<C[\"Type\"], TableModel, TableModel, TableModel, unknown> => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n digest: TokenDigest,\n version: SecurityRevision,\n evidence: AuthenticationEvidence,\n expiresAt: Schema.DateTimeUtcFromMillis,\n attemptLimit: Schema.Int.check(Schema.isGreaterThan(0)),\n claims: Schema.Unknown,\n }),\n );\n\n return {\n ...authority(),\n moduleId,\n clock,\n isConstraintConflict: () => false,\n constraints: { pendingDigest: \"unique(pending.digest)\" },\n pending: {\n table: table(\"pending\"),\n moduleId: \"moduleId\",\n kind: \"kind\",\n digest: \"digest\",\n version: \"version\",\n flowId: \"flowId\",\n subjectId: \"subjectId\",\n bindingDigest: \"bindingDigest\",\n snapshot: \"snapshot\",\n expiresAt: \"expiresAt\",\n attemptLimit: \"attemptLimit\",\n failedAttempts: \"failedAttempts\",\n consumed: \"consumed\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateVersion: allocate.pipe(Effect.map(SecurityRevision.make)),\n encodeInsert: (value) => ({\n ...value,\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n failedAttempts: 0,\n consumed: false,\n }),\n },\n login: {\n encode: (value) =>\n Schema.encodeEffect(claims)(value.claims).pipe(\n Effect.flatMap((saved) => Schema.encodeEffect(record)({ ...value, claims: saved })),\n Effect.mapError(failure),\n ),\n decode: (value) =>\n decode(record, value).pipe(\n Effect.flatMap((saved) =>\n decode(claims, saved.claims).pipe(\n Effect.map((decoded) => ({ ...saved, claims: decoded })),\n ),\n ),\n ),\n },\n };\n };\n\n const sessions = <C extends Schema.Codec<unknown, unknown, never, never>>(\n claims: C,\n moduleId: string,\n ): StatefulSessionMapping<\n C[\"Type\"],\n TableModel,\n TableModel,\n TableModel,\n TableModel,\n unknown,\n unknown\n > => {\n const record = Schema.fromJsonString(\n Schema.Struct({\n ...SessionMetadata.fields,\n claims: Schema.Unknown,\n digest: TokenDigest,\n provenance: SessionAuthenticationProvenance,\n credentialVersion: SessionCredentialVersion,\n }),\n );\n\n const encodeRow = (value: StatefulSessionRecord<C[\"Type\"]>) => ({\n digest: value.digest,\n securityRevision: value.securityRevision,\n issuedAt: instant(DateTime.toEpochMillis(value.issuedAt)),\n expiresAt: instant(DateTime.toEpochMillis(value.expiresAt)),\n absoluteExpiresAt: instant(DateTime.toEpochMillis(value.absoluteExpiresAt)),\n record: Schema.encodeSync(record)({\n ...value,\n claims: Schema.encodeSync(claims)(value.claims),\n }),\n });\n\n const login = pending(claims, moduleId);\n\n return {\n ...authority(),\n moduleId,\n clock,\n isConstraintConflict: () => false,\n pending: { pending: login.pending, login: login.login },\n constraints: {\n sessionDigest: \"unique(session.digest)\",\n pendingDigest: \"unique(pending.digest)\",\n },\n sessionId: {\n toNative: (id) => Effect.succeed(id),\n toSession: (id) => decode(SessionId, id),\n equals: Object.is,\n },\n session: {\n table: table(\"sessions\"),\n sessionId: \"sessionId\",\n subjectId: \"subjectId\",\n digest: \"digest\",\n securityRevision: \"securityRevision\",\n issuedAt: \"issuedAt\",\n expiresAt: \"expiresAt\",\n absoluteExpiresAt: \"absoluteExpiresAt\",\n encodeInstant: (date) => instant(DateTime.toEpochMillis(date)),\n allocateId: allocate,\n encodeInsert: (value, ids) => ({ ...encodeRow(value), ...ids }),\n encodeRotation: encodeRow,\n decode: (row) =>\n Effect.gen(function* () {\n const saved = yield* decode(record, row.record),\n decodedClaims = yield* decode(claims, saved.claims);\n\n return {\n ...saved,\n issuedAt: DateTime.makeUnsafe(yield* readInstant(row.issuedAt)),\n claims: decodedClaims,\n };\n }),\n },\n };\n };\n\n return { table, authority, proofs, passwords, emails, sessions, pending, string };\n});\n"],"mappings":"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"}
|