@yielded/auth-persistence 0.1.0-beta.10
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 +27 -0
- package/dist/Adapter.d.mts +30 -0
- package/dist/Adapter.mjs +25 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +7 -0
- package/dist/internal/PasswordPreparedPostconditions.d.mts +22 -0
- package/dist/internal/PasswordPreparedPostconditions.mjs +12 -0
- package/dist/internal/PasswordPreparedPostconditions.mjs.map +1 -0
- package/dist/internal/configuration.d.mts +174 -0
- package/dist/internal/configuration.mjs +7 -0
- package/dist/internal/configuration.mjs.map +1 -0
- package/dist/internal/email-kernel.d.mts +275 -0
- package/dist/internal/email-kernel.mjs +487 -0
- package/dist/internal/email-kernel.mjs.map +1 -0
- package/dist/internal/mapping-error.d.mts +10 -0
- package/dist/internal/mapping-error.mjs +30 -0
- package/dist/internal/mapping-error.mjs.map +1 -0
- package/dist/internal/models/common.d.mts +12 -0
- package/dist/internal/models/email-model.d.mts +353 -0
- package/dist/internal/models/email-model.mjs +21 -0
- package/dist/internal/models/email-model.mjs.map +1 -0
- package/dist/internal/models/passkey-model.d.mts +266 -0
- package/dist/internal/models/passkey-model.mjs +34 -0
- package/dist/internal/models/passkey-model.mjs.map +1 -0
- package/dist/internal/models/passkey-registration-ceremony-model.d.mts +49 -0
- package/dist/internal/models/passkey-registration-ceremony-model.mjs +18 -0
- package/dist/internal/models/passkey-registration-ceremony-model.mjs.map +1 -0
- package/dist/internal/models/passkey-write-model.d.mts +189 -0
- package/dist/internal/models/passkey-write-model.mjs +13 -0
- package/dist/internal/models/passkey-write-model.mjs.map +1 -0
- package/dist/internal/models/password-model.d.mts +328 -0
- package/dist/internal/models/password-model.mjs +23 -0
- package/dist/internal/models/password-model.mjs.map +1 -0
- package/dist/internal/models/proof-model.d.mts +285 -0
- package/dist/internal/models/proof-model.mjs +17 -0
- package/dist/internal/models/proof-model.mjs.map +1 -0
- package/dist/internal/models/session-model.d.mts +199 -0
- package/dist/internal/models/session-model.mjs +20 -0
- package/dist/internal/models/session-model.mjs.map +1 -0
- package/dist/internal/models/step-up-model.d.mts +66 -0
- package/dist/internal/models/step-up-model.mjs +9 -0
- package/dist/internal/models/step-up-model.mjs.map +1 -0
- package/dist/internal/oauth-app.d.mts +11 -0
- package/dist/internal/oauth-app.mjs +57 -0
- package/dist/internal/oauth-app.mjs.map +1 -0
- package/dist/internal/oauth-server.d.mts +10 -0
- package/dist/internal/oauth-server.mjs +66 -0
- package/dist/internal/oauth-server.mjs.map +1 -0
- package/dist/internal/passkey/admission.d.mts +6 -0
- package/dist/internal/passkey/admission.mjs +216 -0
- package/dist/internal/passkey/admission.mjs.map +1 -0
- package/dist/internal/passkey/credentials.d.mts +14 -0
- package/dist/internal/passkey/credentials.mjs +249 -0
- package/dist/internal/passkey/credentials.mjs.map +1 -0
- package/dist/internal/passkey/enrollment.mjs +138 -0
- package/dist/internal/passkey/enrollment.mjs.map +1 -0
- package/dist/internal/passkey/flow.d.mts +21 -0
- package/dist/internal/passkey/flow.mjs +452 -0
- package/dist/internal/passkey/flow.mjs.map +1 -0
- package/dist/internal/passkey/management.mjs +231 -0
- package/dist/internal/passkey/management.mjs.map +1 -0
- package/dist/internal/passkey/registration-ceremony.d.mts +9 -0
- package/dist/internal/passkey/registration-ceremony.mjs +129 -0
- package/dist/internal/passkey/registration-ceremony.mjs.map +1 -0
- package/dist/internal/passkey/registration-custody.d.mts +2 -0
- package/dist/internal/passkey/registration-custody.mjs +52 -0
- package/dist/internal/passkey/registration-custody.mjs.map +1 -0
- package/dist/internal/passkey/registration-write.mjs +211 -0
- package/dist/internal/passkey/registration-write.mjs.map +1 -0
- package/dist/internal/passkey/state.d.mts +10 -0
- package/dist/internal/passkey/state.mjs +234 -0
- package/dist/internal/passkey/state.mjs.map +1 -0
- package/dist/internal/passkey/target.d.mts +19 -0
- package/dist/internal/passkey/target.mjs +190 -0
- package/dist/internal/passkey/target.mjs.map +1 -0
- package/dist/internal/passkey/write-state.d.mts +17 -0
- package/dist/internal/passkey/write-state.mjs +345 -0
- package/dist/internal/passkey/write-state.mjs.map +1 -0
- package/dist/internal/passkey/write-target.mjs +192 -0
- package/dist/internal/passkey/write-target.mjs.map +1 -0
- package/dist/internal/passkey-kernel.d.mts +5592 -0
- package/dist/internal/passkey-kernel.mjs +49 -0
- package/dist/internal/passkey-kernel.mjs.map +1 -0
- package/dist/internal/passkeys.d.mts +7 -0
- package/dist/internal/passkeys.mjs +368 -0
- package/dist/internal/passkeys.mjs.map +1 -0
- package/dist/internal/password-kernel.d.mts +290 -0
- package/dist/internal/password-kernel.mjs +704 -0
- package/dist/internal/password-kernel.mjs.map +1 -0
- package/dist/internal/persistence.d.mts +25 -0
- package/dist/internal/persistence.mjs +291 -0
- package/dist/internal/persistence.mjs.map +1 -0
- package/dist/internal/phone-kernel.d.mts +162 -0
- package/dist/internal/phone-kernel.mjs +434 -0
- package/dist/internal/phone-kernel.mjs.map +1 -0
- package/dist/internal/proof-kernel.d.mts +93 -0
- package/dist/internal/proof-kernel.mjs +619 -0
- package/dist/internal/proof-kernel.mjs.map +1 -0
- package/dist/internal/query-operations.d.mts +61 -0
- package/dist/internal/registration-contract.d.mts +16 -0
- package/dist/internal/registration.mjs +66 -0
- package/dist/internal/registration.mjs.map +1 -0
- package/dist/internal/session-kernel.d.mts +48 -0
- package/dist/internal/session-kernel.mjs +737 -0
- package/dist/internal/session-kernel.mjs.map +1 -0
- package/dist/internal/sql-persistence.d.mts +10 -0
- package/dist/internal/sql-persistence.mjs +37 -0
- package/dist/internal/sql-persistence.mjs.map +1 -0
- package/dist/internal/sql-query.mjs +207 -0
- package/dist/internal/sql-query.mjs.map +1 -0
- package/dist/internal/sql-table.d.mts +36 -0
- package/dist/internal/sql-table.mjs +81 -0
- package/dist/internal/sql-table.mjs.map +1 -0
- package/dist/internal/standalone.d.mts +9 -0
- package/dist/internal/standalone.mjs +13 -0
- package/dist/internal/standalone.mjs.map +1 -0
- package/dist/internal/step-up-state.d.mts +49 -0
- package/dist/internal/step-up-state.mjs +32 -0
- package/dist/internal/step-up-state.mjs.map +1 -0
- package/dist/internal/storage-mapping.mjs +686 -0
- package/dist/internal/storage-mapping.mjs.map +1 -0
- package/dist/internal/storage-tables.d.mts +913 -0
- package/dist/internal/storage-tables.mjs +387 -0
- package/dist/internal/storage-tables.mjs.map +1 -0
- package/dist/internal/storage-validation.mjs +58 -0
- package/dist/internal/storage-validation.mjs.map +1 -0
- package/dist/internal/transaction-execution-kernel.d.mts +38 -0
- package/dist/internal/transaction-execution-kernel.mjs +116 -0
- package/dist/internal/transaction-execution-kernel.mjs.map +1 -0
- package/dist/internal/transaction-kernel.d.mts +77 -0
- package/dist/internal/transaction-kernel.mjs +260 -0
- package/dist/internal/transaction-kernel.mjs.map +1 -0
- package/package.json +1 -0
- package/src/Adapter.ts +291 -0
- package/src/index.ts +6 -0
- package/src/internal/PasswordPreparedPostconditions.ts +30 -0
- package/src/internal/configuration.ts +282 -0
- package/src/internal/email-kernel.ts +1535 -0
- package/src/internal/mapping-error.ts +36 -0
- package/src/internal/models/common.ts +11 -0
- package/src/internal/models/email-model.ts +559 -0
- package/src/internal/models/passkey-model.ts +390 -0
- package/src/internal/models/passkey-registration-ceremony-model.ts +72 -0
- package/src/internal/models/passkey-write-model.ts +297 -0
- package/src/internal/models/password-model.ts +513 -0
- package/src/internal/models/proof-model.ts +424 -0
- package/src/internal/models/session-model.ts +375 -0
- package/src/internal/models/step-up-model.ts +135 -0
- package/src/internal/oauth-app.ts +82 -0
- package/src/internal/oauth-server.ts +89 -0
- package/src/internal/passkey/admission.ts +361 -0
- package/src/internal/passkey/credentials.ts +511 -0
- package/src/internal/passkey/enrollment.ts +326 -0
- package/src/internal/passkey/flow.ts +1062 -0
- package/src/internal/passkey/management.ts +421 -0
- package/src/internal/passkey/registration-ceremony.ts +283 -0
- package/src/internal/passkey/registration-custody.ts +91 -0
- package/src/internal/passkey/registration-write.ts +426 -0
- package/src/internal/passkey/state.ts +353 -0
- package/src/internal/passkey/target.ts +586 -0
- package/src/internal/passkey/write-state.ts +681 -0
- package/src/internal/passkey/write-target.ts +452 -0
- package/src/internal/passkey-kernel.ts +104 -0
- package/src/internal/passkeys.ts +606 -0
- package/src/internal/password-kernel.ts +2124 -0
- package/src/internal/persistence.ts +588 -0
- package/src/internal/phone-kernel.ts +846 -0
- package/src/internal/proof-kernel.ts +1954 -0
- package/src/internal/query-operations.ts +81 -0
- package/src/internal/registration-contract.ts +22 -0
- package/src/internal/registration.ts +161 -0
- package/src/internal/session-kernel.ts +2217 -0
- package/src/internal/sql-persistence.ts +45 -0
- package/src/internal/sql-query.ts +298 -0
- package/src/internal/sql-table.ts +104 -0
- package/src/internal/standalone.ts +30 -0
- package/src/internal/step-up-state.ts +139 -0
- package/src/internal/storage-mapping.ts +708 -0
- package/src/internal/storage-tables.ts +408 -0
- package/src/internal/storage-validation.ts +117 -0
- package/src/internal/transaction-execution-kernel.ts +313 -0
- package/src/internal/transaction-kernel.ts +593 -0
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
/* oxlint-disable no-explicit-any -- existing mapped-row bridge; public adapters preserve table, ID, and Effect types. */
|
|
2
|
+
|
|
3
|
+
// oxlint-disable-next-line import/extensions -- Noble exposes its explicit .js subpaths.
|
|
4
|
+
import { sha256 } from "@noble/hashes/sha2.js";
|
|
5
|
+
// oxlint-disable-next-line import/extensions -- Noble exposes its explicit .js subpaths.
|
|
6
|
+
import { randomBytes } from "@noble/hashes/utils.js";
|
|
7
|
+
import {
|
|
8
|
+
PasskeyConfigurationError,
|
|
9
|
+
PasskeyUnavailable,
|
|
10
|
+
PasskeyCeremony,
|
|
11
|
+
PasskeyCredential,
|
|
12
|
+
PasskeyModuleId,
|
|
13
|
+
PasskeyProfile,
|
|
14
|
+
PasskeyRevision,
|
|
15
|
+
PasskeyMethodPolicy,
|
|
16
|
+
snapshotPasskeySync,
|
|
17
|
+
} from "@yielded/auth/Passkey";
|
|
18
|
+
import { Context, Effect, Encoding, Schema } from "effect";
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
requiredPasskeyCredentialConstraints,
|
|
22
|
+
requiredPasskeyPersistenceConstraints,
|
|
23
|
+
} from "../models/passkey-model";
|
|
24
|
+
import { requiredPasskeyRegistrationCeremonyConstraints } from "../models/passkey-registration-ceremony-model";
|
|
25
|
+
import type { SqlExpression as SQL, QueryOperations } from "../query-operations";
|
|
26
|
+
import type { TransactionOwner, makeTransactionKernel } from "../transaction-kernel";
|
|
27
|
+
|
|
28
|
+
export type PasskeyOwner = TransactionOwner<PasskeyUnavailable>;
|
|
29
|
+
|
|
30
|
+
export class CurrentPasskeyTransaction extends Context.Service<
|
|
31
|
+
CurrentPasskeyTransaction,
|
|
32
|
+
PasskeyOwner
|
|
33
|
+
>()("effect-auth/drizzle/CurrentPasskeyTransaction") {}
|
|
34
|
+
|
|
35
|
+
export const makePasskeyStateKernel = (
|
|
36
|
+
operations: QueryOperations,
|
|
37
|
+
transactions: Pick<ReturnType<typeof makeTransactionKernel>, "both" | "makeTransactionRows">,
|
|
38
|
+
) => {
|
|
39
|
+
const { getTableColumns, sql } = operations;
|
|
40
|
+
const { both, makeTransactionRows } = transactions;
|
|
41
|
+
|
|
42
|
+
const unavailable = () => PasskeyUnavailable.make({});
|
|
43
|
+
|
|
44
|
+
const invariant: (value: unknown) => asserts value = (value) => {
|
|
45
|
+
if (!value) throw unavailable();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const { col, equal, copiedRow, matchesNativeRow } = makeTransactionRows(unavailable);
|
|
49
|
+
|
|
50
|
+
const nonce = () => Encoding.encodeBase64Url(randomBytes(32));
|
|
51
|
+
|
|
52
|
+
const encoder = new TextEncoder();
|
|
53
|
+
|
|
54
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
55
|
+
|
|
56
|
+
/** RP tuples and admission scopes have no collation-dependent string identity. */
|
|
57
|
+
const key = (kind: string, fields: ReadonlyArray<string>) => {
|
|
58
|
+
const values = ["effect-auth/passkey/" + kind + "/v1", ...fields].map((field) => {
|
|
59
|
+
const bytes = encoder.encode(field);
|
|
60
|
+
|
|
61
|
+
invariant(decoder.decode(bytes) === field);
|
|
62
|
+
|
|
63
|
+
return bytes;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const packed = new Uint8Array(values.reduce((size, bytes) => size + 4 + bytes.length, 0));
|
|
67
|
+
const view = new DataView(packed.buffer);
|
|
68
|
+
let offset = 0;
|
|
69
|
+
|
|
70
|
+
for (const bytes of values) {
|
|
71
|
+
view.setUint32(offset, bytes.length, false);
|
|
72
|
+
packed.set(bytes, offset + 4);
|
|
73
|
+
offset += 4 + bytes.length;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
"v1:" + Array.from(sha256(packed), (value) => value.toString(16).padStart(2, "0")).join("")
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const credentialKey = (rpId: string, id: string) => key("credential", [rpId, id]);
|
|
82
|
+
|
|
83
|
+
const handleKey = (rpId: string, handle: string) => key("handle", [rpId, handle]);
|
|
84
|
+
|
|
85
|
+
const subjectScope = (subjectId: string) => key("subject", [subjectId]);
|
|
86
|
+
|
|
87
|
+
const targetScope = (ceremony: PasskeyCeremony) => {
|
|
88
|
+
const context = ceremony.context;
|
|
89
|
+
|
|
90
|
+
if (!("target" in context)) return null;
|
|
91
|
+
const target = context.target;
|
|
92
|
+
|
|
93
|
+
return key("target", [
|
|
94
|
+
target.moduleId,
|
|
95
|
+
target.kind,
|
|
96
|
+
target.flowId,
|
|
97
|
+
target.commandId,
|
|
98
|
+
target.bindingDigest,
|
|
99
|
+
target.revision.subjectId,
|
|
100
|
+
]);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/** Private storage is decoded Type data, never a consumer wire transform. */
|
|
104
|
+
const storage = <S extends Schema.Codec<unknown, unknown, never, never>>(
|
|
105
|
+
schema: S,
|
|
106
|
+
maximumBytes: number,
|
|
107
|
+
) => {
|
|
108
|
+
const codec = Schema.fromJsonString(Schema.toCodecJson(Schema.toType(schema)));
|
|
109
|
+
|
|
110
|
+
const encode = (value: S["Type"]) => {
|
|
111
|
+
const text = "v1:" + Schema.encodeSync(codec)(snapshotPasskeySync(schema, value));
|
|
112
|
+
|
|
113
|
+
invariant(encoder.encode(text).length <= maximumBytes);
|
|
114
|
+
|
|
115
|
+
return text;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
encode,
|
|
120
|
+
decode: (text: unknown): S["Type"] => {
|
|
121
|
+
invariant(typeof text === "string" && text.startsWith("v1:"));
|
|
122
|
+
invariant(encoder.encode(text).length <= maximumBytes);
|
|
123
|
+
const value = snapshotPasskeySync(schema, Schema.decodeSync(codec)(text.slice(3)));
|
|
124
|
+
|
|
125
|
+
invariant(encode(value) === text);
|
|
126
|
+
|
|
127
|
+
return value;
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// Legal 32 x 16 x 2048 origins require more than one MiB before JSON escaping.
|
|
133
|
+
const policyStorage = storage(PasskeyMethodPolicy, 8 * 1024 * 1024);
|
|
134
|
+
|
|
135
|
+
const ceremonyStorage = storage(PasskeyCeremony, 512 * 1024);
|
|
136
|
+
|
|
137
|
+
const credentialStorage = storage(PasskeyCredential, 512 * 1024);
|
|
138
|
+
|
|
139
|
+
const profileStorage = storage(PasskeyProfile, 256 * 1024);
|
|
140
|
+
|
|
141
|
+
const revisionStorage = storage(PasskeyRevision, 256 * 1024);
|
|
142
|
+
|
|
143
|
+
const sameCredential = (left: PasskeyCredential, right: PasskeyCredential) => {
|
|
144
|
+
const semantic = (value: PasskeyCredential) => ({
|
|
145
|
+
...value,
|
|
146
|
+
counter: 0,
|
|
147
|
+
maximumCounter: 0,
|
|
148
|
+
backupState: false,
|
|
149
|
+
revision: { ...value.revision, credentials: [] },
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return credentialStorage.encode(semantic(left)) === credentialStorage.encode(semantic(right));
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const sameRevision = (left: typeof PasskeyRevision.Type, right: typeof PasskeyRevision.Type) => {
|
|
156
|
+
const pairs = new Map(left.credentials.map((item) => [item.credentialId, item.revision]));
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
left.subjectId === right.subjectId &&
|
|
160
|
+
left.securityRevision === right.securityRevision &&
|
|
161
|
+
pairs.size === left.credentials.length &&
|
|
162
|
+
new Set(right.credentials.map((item) => item.credentialId)).size ===
|
|
163
|
+
right.credentials.length &&
|
|
164
|
+
pairs.size === right.credentials.length &&
|
|
165
|
+
right.credentials.every((item) => pairs.get(item.credentialId) === item.revision)
|
|
166
|
+
);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
const semanticCredentialColumns = (mapping: any): string[] =>
|
|
170
|
+
[
|
|
171
|
+
"credentialId",
|
|
172
|
+
"subjectId",
|
|
173
|
+
"rpId",
|
|
174
|
+
"protocolCredentialId",
|
|
175
|
+
"credentialKey",
|
|
176
|
+
"handleKey",
|
|
177
|
+
"userHandle",
|
|
178
|
+
"publicKey",
|
|
179
|
+
"algorithm",
|
|
180
|
+
"profile",
|
|
181
|
+
"credentialRevision",
|
|
182
|
+
"status",
|
|
183
|
+
"primarySignIn",
|
|
184
|
+
"enrollmentUserVerified",
|
|
185
|
+
"backupEligible",
|
|
186
|
+
].map((name) => mapping[name]);
|
|
187
|
+
|
|
188
|
+
const mappedColumns = (mapping: any): string[] =>
|
|
189
|
+
Object.entries(mapping)
|
|
190
|
+
.filter(
|
|
191
|
+
([name, value]) =>
|
|
192
|
+
!["table", "reservedState", "pendingState", "acceptedState", "rejectedState"].includes(
|
|
193
|
+
name,
|
|
194
|
+
) && typeof value === "string",
|
|
195
|
+
)
|
|
196
|
+
.map(([, value]) => value as string);
|
|
197
|
+
|
|
198
|
+
const observeSemantic = (mapping: any, where: SQL, row: any) =>
|
|
199
|
+
Effect.map(CurrentPasskeyTransaction, (owner) => {
|
|
200
|
+
owner.observations.push({
|
|
201
|
+
table: mapping.table,
|
|
202
|
+
where,
|
|
203
|
+
rows: [
|
|
204
|
+
Object.fromEntries(
|
|
205
|
+
semanticCredentialColumns(mapping).map((column) => [column, row[column]]),
|
|
206
|
+
),
|
|
207
|
+
],
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
/** Capture descriptor graphs, preserving native SQL expressions and table objects. */
|
|
212
|
+
const captureMapping = <A>(input: A): A => {
|
|
213
|
+
const seen = new Map<object, unknown>();
|
|
214
|
+
|
|
215
|
+
const copy = (value: unknown): unknown => {
|
|
216
|
+
if (value === null || typeof value !== "object" || Effect.isEffect(value)) return value;
|
|
217
|
+
if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype) return value;
|
|
218
|
+
const prior = seen.get(value);
|
|
219
|
+
|
|
220
|
+
if (prior !== undefined) return prior;
|
|
221
|
+
const result: any = Array.isArray(value) ? [] : {};
|
|
222
|
+
|
|
223
|
+
seen.set(value, result);
|
|
224
|
+
for (const [name, child] of Object.entries(value)) result[name] = copy(child);
|
|
225
|
+
|
|
226
|
+
return Object.freeze(result);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
return copy(input) as A;
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const constraints = (actual: any, expected: any) => {
|
|
233
|
+
for (const [name, columns] of Object.entries(expected))
|
|
234
|
+
// oxlint-disable-next-line no-restricted-properties -- exact configured constraint descriptor fingerprint.
|
|
235
|
+
invariant(JSON.stringify(actual?.[name]) === JSON.stringify(columns));
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const descriptor = (value: any) => {
|
|
239
|
+
const columns = getTableColumns(value.table);
|
|
240
|
+
|
|
241
|
+
for (const name of mappedColumns(value)) invariant(columns[name] !== undefined);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const validateMapping = (
|
|
245
|
+
mapping: any,
|
|
246
|
+
kind: "read" | "context" | "assertion" | "registration",
|
|
247
|
+
) => {
|
|
248
|
+
try {
|
|
249
|
+
const read = kind === "read" ? mapping : kind !== "registration" ? mapping.read : undefined;
|
|
250
|
+
|
|
251
|
+
if (read !== undefined) {
|
|
252
|
+
for (const name of [
|
|
253
|
+
"subject",
|
|
254
|
+
"credential",
|
|
255
|
+
"authority",
|
|
256
|
+
"credentialOwnership",
|
|
257
|
+
"handleOwnership",
|
|
258
|
+
])
|
|
259
|
+
descriptor(read[name]);
|
|
260
|
+
constraints(read.constraints, requiredPasskeyCredentialConstraints);
|
|
261
|
+
}
|
|
262
|
+
if (kind === "read") return;
|
|
263
|
+
// oxlint-disable-next-line no-restricted-properties -- validate the untyped captured configuration boundary.
|
|
264
|
+
Schema.decodeUnknownSync(PasskeyModuleId)(mapping.moduleId);
|
|
265
|
+
descriptor(mapping.module);
|
|
266
|
+
invariant(mapping.module.policyColumns.length > 0);
|
|
267
|
+
for (const name of mapping.module.policyColumns) col(mapping.module.table, name);
|
|
268
|
+
const scopes = new Set<string>();
|
|
269
|
+
|
|
270
|
+
for (const guard of mapping.module.guards ?? []) {
|
|
271
|
+
invariant(
|
|
272
|
+
typeof guard.scope === "string" && guard.scope.length > 0 && guard.scope.length <= 256,
|
|
273
|
+
);
|
|
274
|
+
invariant(!scopes.has(guard.scope) && guard.columns.length > 0);
|
|
275
|
+
scopes.add(guard.scope);
|
|
276
|
+
for (const name of guard.columns) col(guard.table, name);
|
|
277
|
+
}
|
|
278
|
+
invariant(scopes.size <= 32);
|
|
279
|
+
if (kind === "context") return;
|
|
280
|
+
invariant(
|
|
281
|
+
typeof mapping.authorityScope === "string" &&
|
|
282
|
+
mapping.authorityScope.length > 0 &&
|
|
283
|
+
mapping.authorityScope.length <= 256,
|
|
284
|
+
);
|
|
285
|
+
for (const name of ["flow", "admission", "charge"]) descriptor(mapping[name]);
|
|
286
|
+
if (kind === "registration") {
|
|
287
|
+
descriptor(mapping.intent);
|
|
288
|
+
descriptor(mapping.handle);
|
|
289
|
+
} else col(mapping.read.credential.table, mapping.telemetry.lastUsedAt);
|
|
290
|
+
constraints(
|
|
291
|
+
mapping.constraints,
|
|
292
|
+
kind === "registration"
|
|
293
|
+
? requiredPasskeyRegistrationCeremonyConstraints
|
|
294
|
+
: requiredPasskeyPersistenceConstraints,
|
|
295
|
+
);
|
|
296
|
+
const states = Object.values(mapping.flow.states);
|
|
297
|
+
|
|
298
|
+
// oxlint-disable-next-line no-restricted-properties -- configured native state identity fingerprints.
|
|
299
|
+
invariant(
|
|
300
|
+
// oxlint-disable-next-line no-restricted-properties -- configured native state identity fingerprints.
|
|
301
|
+
states.length === 7 && new Set(states.map((value) => JSON.stringify(value))).size === 7,
|
|
302
|
+
);
|
|
303
|
+
for (const value of [0, 1, 123456789, 8640000000000])
|
|
304
|
+
invariant(mapping.clock.decodeInstant(mapping.clock.encodeInstant(value)) === value);
|
|
305
|
+
} catch {
|
|
306
|
+
throw PasskeyConfigurationError.make({});
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const assertNow = (condition: SQL) =>
|
|
311
|
+
Effect.flatMap(CurrentPasskeyTransaction, (owner) =>
|
|
312
|
+
Effect.gen(function* () {
|
|
313
|
+
invariant(yield* owner.check(condition));
|
|
314
|
+
owner.postconditions.push(condition);
|
|
315
|
+
}),
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
const existsExact = (table: any, values: any, condition?: SQL) =>
|
|
319
|
+
Effect.map(
|
|
320
|
+
CurrentPasskeyTransaction,
|
|
321
|
+
(owner) =>
|
|
322
|
+
sql`exists(select 1 from ${table} where ${both(owner.exact(table, values), condition)})`,
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
unavailable,
|
|
327
|
+
invariant,
|
|
328
|
+
col,
|
|
329
|
+
equal,
|
|
330
|
+
copiedRow,
|
|
331
|
+
matchesNativeRow,
|
|
332
|
+
nonce,
|
|
333
|
+
key,
|
|
334
|
+
credentialKey,
|
|
335
|
+
handleKey,
|
|
336
|
+
subjectScope,
|
|
337
|
+
targetScope,
|
|
338
|
+
policyStorage,
|
|
339
|
+
ceremonyStorage,
|
|
340
|
+
credentialStorage,
|
|
341
|
+
profileStorage,
|
|
342
|
+
revisionStorage,
|
|
343
|
+
sameCredential,
|
|
344
|
+
sameRevision,
|
|
345
|
+
semanticCredentialColumns,
|
|
346
|
+
mappedColumns,
|
|
347
|
+
observeSemantic,
|
|
348
|
+
captureMapping,
|
|
349
|
+
validateMapping,
|
|
350
|
+
assertNow,
|
|
351
|
+
existsExact,
|
|
352
|
+
};
|
|
353
|
+
};
|