@yielded/auth-persistence 0.1.0-beta.22 → 0.1.0-beta.24
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 -0
- package/dist/Adapter.d.mts +13 -5
- package/dist/Adapter.mjs +9 -2
- package/dist/OAuthPersistence.d.mts +87 -0
- package/dist/OAuthPersistence.mjs +125 -0
- package/dist/OAuthPersistence.mjs.map +1 -0
- package/dist/_virtual/_rolldown/runtime.mjs +13 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +3 -1
- package/dist/internal/authority-snapshot.mjs +53 -0
- package/dist/internal/authority-snapshot.mjs.map +1 -0
- package/dist/internal/configuration.d.mts +1 -1
- package/dist/internal/configuration.mjs.map +1 -1
- package/dist/internal/models/oauth-connected-model.d.mts +294 -0
- package/dist/internal/models/oauth-connected-model.mjs +22 -0
- package/dist/internal/models/oauth-connected-model.mjs.map +1 -0
- package/dist/internal/models/oauth-model.d.mts +467 -0
- package/dist/internal/models/oauth-model.mjs +49 -0
- package/dist/internal/models/oauth-model.mjs.map +1 -0
- package/dist/internal/models/password-model.d.mts +4 -97
- package/dist/internal/models/password-model.mjs +0 -3
- package/dist/internal/models/password-model.mjs.map +1 -1
- package/dist/internal/native-sql-table.d.mts +34 -0
- package/dist/internal/native-sql-table.mjs +111 -0
- package/dist/internal/native-sql-table.mjs.map +1 -0
- package/dist/internal/oauth/accounts.d.mts +8 -0
- package/dist/internal/oauth/accounts.mjs +379 -0
- package/dist/internal/oauth/accounts.mjs.map +1 -0
- package/dist/internal/oauth/connected-access.d.mts +9 -0
- package/dist/internal/oauth/connected-access.mjs +229 -0
- package/dist/internal/oauth/connected-access.mjs.map +1 -0
- package/dist/internal/oauth/connected-collection.d.mts +7 -0
- package/dist/internal/oauth/connected-collection.mjs +214 -0
- package/dist/internal/oauth/connected-collection.mjs.map +1 -0
- package/dist/internal/oauth/connected-custody.d.mts +6 -0
- package/dist/internal/oauth/connected-custody.mjs +165 -0
- package/dist/internal/oauth/connected-custody.mjs.map +1 -0
- package/dist/internal/oauth/connected-flow.d.mts +7 -0
- package/dist/internal/oauth/connected-flow.mjs +324 -0
- package/dist/internal/oauth/connected-flow.mjs.map +1 -0
- package/dist/internal/oauth/connected-input.mjs +122 -0
- package/dist/internal/oauth/connected-input.mjs.map +1 -0
- package/dist/internal/oauth/connected-keys.d.mts +2 -0
- package/dist/internal/oauth/connected-keys.mjs +22 -0
- package/dist/internal/oauth/connected-keys.mjs.map +1 -0
- package/dist/internal/oauth/connected-maintenance.d.mts +9 -0
- package/dist/internal/oauth/connected-maintenance.mjs +226 -0
- package/dist/internal/oauth/connected-maintenance.mjs.map +1 -0
- package/dist/internal/oauth/connected-management.d.mts +9 -0
- package/dist/internal/oauth/connected-management.mjs +243 -0
- package/dist/internal/oauth/connected-management.mjs.map +1 -0
- package/dist/internal/oauth/connected-reference.mjs +63 -0
- package/dist/internal/oauth/connected-reference.mjs.map +1 -0
- package/dist/internal/oauth/connected-settlement.d.mts +8 -0
- package/dist/internal/oauth/connected-settlement.mjs +141 -0
- package/dist/internal/oauth/connected-settlement.mjs.map +1 -0
- package/dist/internal/oauth/connected-sign-in.d.mts +10 -0
- package/dist/internal/oauth/connected-sign-in.mjs +281 -0
- package/dist/internal/oauth/connected-sign-in.mjs.map +1 -0
- package/dist/internal/oauth/connected-state.d.mts +14 -0
- package/dist/internal/oauth/connected-state.mjs +469 -0
- package/dist/internal/oauth/connected-state.mjs.map +1 -0
- package/dist/internal/oauth/connected-target.d.mts +21 -0
- package/dist/internal/oauth/connected-target.mjs +117 -0
- package/dist/internal/oauth/connected-target.mjs.map +1 -0
- package/dist/internal/oauth/flow.d.mts +5 -0
- package/dist/internal/oauth/flow.mjs +279 -0
- package/dist/internal/oauth/flow.mjs.map +1 -0
- package/dist/internal/oauth/input.mjs +127 -0
- package/dist/internal/oauth/input.mjs.map +1 -0
- package/dist/internal/oauth/owner.d.mts +10 -0
- package/dist/internal/oauth/owner.mjs +10 -0
- package/dist/internal/oauth/owner.mjs.map +1 -0
- package/dist/internal/oauth/query-compiler.d.mts +7 -0
- package/dist/internal/oauth/query-compiler.mjs +9 -0
- package/dist/internal/oauth/query-compiler.mjs.map +1 -0
- package/dist/internal/oauth/registration.d.mts +7 -0
- package/dist/internal/oauth/registration.mjs +393 -0
- package/dist/internal/oauth/registration.mjs.map +1 -0
- package/dist/internal/oauth/state.mjs +107 -0
- package/dist/internal/oauth/state.mjs.map +1 -0
- package/dist/internal/oauth/target.d.mts +31 -0
- package/dist/internal/oauth/target.mjs +273 -0
- package/dist/internal/oauth/target.mjs.map +1 -0
- package/dist/internal/oauth-kernel.d.mts +1423 -0
- package/dist/internal/oauth-kernel.mjs +187 -0
- package/dist/internal/oauth-kernel.mjs.map +1 -0
- package/dist/internal/oauth-proxy.d.mts +31 -0
- package/dist/internal/oauth-proxy.mjs +129 -0
- package/dist/internal/oauth-proxy.mjs.map +1 -0
- package/dist/internal/passkey/target.d.mts +1 -1
- package/dist/internal/passkey-kernel.d.mts +5 -5
- package/dist/internal/passkeys.mjs +1 -1
- package/dist/internal/password-credentials.d.mts +11 -0
- package/dist/internal/password-credentials.mjs +121 -0
- package/dist/internal/password-credentials.mjs.map +1 -0
- package/dist/internal/password-kernel.d.mts +15 -28
- package/dist/internal/password-kernel.mjs +35 -236
- package/dist/internal/password-kernel.mjs.map +1 -1
- package/dist/internal/persistence.d.mts +2 -0
- package/dist/internal/persistence.mjs +5 -5
- package/dist/internal/persistence.mjs.map +1 -1
- package/dist/internal/query-operations.d.mts +2 -0
- package/dist/internal/session-kernel.d.mts +5 -3
- package/dist/internal/session-kernel.mjs +95 -29
- package/dist/internal/session-kernel.mjs.map +1 -1
- package/dist/internal/sql-oauth-connected-model.d.mts +29 -0
- package/dist/internal/sql-oauth-connected-model.mjs +7 -0
- package/dist/internal/sql-oauth-connected-model.mjs.map +1 -0
- package/dist/internal/sql-oauth-model.d.mts +60 -0
- package/dist/internal/sql-oauth-model.mjs +10 -0
- package/dist/internal/sql-oauth-model.mjs.map +1 -0
- package/dist/internal/sql-persistence.mjs +3 -1
- package/dist/internal/sql-persistence.mjs.map +1 -1
- package/dist/internal/sql-query.d.mts +12 -0
- package/dist/internal/sql-query.mjs +15 -18
- package/dist/internal/sql-query.mjs.map +1 -1
- package/dist/internal/sql-storage-validation.mjs +54 -0
- package/dist/internal/sql-storage-validation.mjs.map +1 -0
- package/dist/internal/storage-mapping.mjs +2 -67
- package/dist/internal/storage-mapping.mjs.map +1 -1
- package/dist/internal/storage-plans.d.mts +7 -0
- package/dist/internal/storage-plans.mjs +189 -0
- package/dist/internal/storage-plans.mjs.map +1 -0
- package/dist/internal/storage-tables.d.mts +0 -99
- package/dist/internal/storage-tables.mjs +0 -43
- package/dist/internal/storage-tables.mjs.map +1 -1
- package/dist/internal/storage-validation.d.mts +6 -1
- package/dist/internal/storage-validation.mjs +42 -1
- package/dist/internal/storage-validation.mjs.map +1 -1
- package/dist/internal/transaction-execution-kernel.d.mts +1 -1
- package/dist/internal/transaction-kernel.d.mts +1 -1
- package/dist/internal/transaction-kernel.mjs +3 -2
- package/dist/internal/transaction-kernel.mjs.map +1 -1
- package/package.json +1 -1
- package/src/Adapter.ts +97 -8
- package/src/OAuthPersistence.ts +427 -0
- package/src/index.ts +3 -0
- package/src/internal/authority-snapshot.ts +91 -0
- package/src/internal/configuration.ts +1 -8
- package/src/internal/models/oauth-connected-model.ts +396 -0
- package/src/internal/models/oauth-model.ts +708 -0
- package/src/internal/models/password-model.ts +0 -120
- package/src/internal/native-sql-table.ts +178 -0
- package/src/internal/oauth/accounts.ts +838 -0
- package/src/internal/oauth/connected-access.ts +388 -0
- package/src/internal/oauth/connected-collection.ts +345 -0
- package/src/internal/oauth/connected-custody.ts +308 -0
- package/src/internal/oauth/connected-flow.ts +562 -0
- package/src/internal/oauth/connected-input.ts +147 -0
- package/src/internal/oauth/connected-keys.ts +31 -0
- package/src/internal/oauth/connected-maintenance.ts +379 -0
- package/src/internal/oauth/connected-management.ts +391 -0
- package/src/internal/oauth/connected-reference.ts +135 -0
- package/src/internal/oauth/connected-settlement.ts +278 -0
- package/src/internal/oauth/connected-sign-in.ts +472 -0
- package/src/internal/oauth/connected-state.ts +924 -0
- package/src/internal/oauth/connected-target.ts +307 -0
- package/src/internal/oauth/flow.ts +505 -0
- package/src/internal/oauth/input.ts +201 -0
- package/src/internal/oauth/owner.ts +22 -0
- package/src/internal/oauth/query-compiler.ts +15 -0
- package/src/internal/oauth/registration.ts +676 -0
- package/src/internal/oauth/state.ts +218 -0
- package/src/internal/oauth/target.ts +803 -0
- package/src/internal/oauth-kernel.ts +294 -0
- package/src/internal/oauth-proxy.ts +176 -0
- package/src/internal/password-credentials.ts +245 -0
- package/src/internal/password-kernel.ts +98 -712
- package/src/internal/persistence.ts +5 -10
- package/src/internal/query-operations.ts +2 -0
- package/src/internal/session-kernel.ts +231 -81
- package/src/internal/sql-oauth-connected-model.ts +145 -0
- package/src/internal/sql-oauth-model.ts +263 -0
- package/src/internal/sql-persistence.ts +2 -0
- package/src/internal/sql-query.ts +16 -13
- package/src/internal/sql-storage-validation.ts +64 -0
- package/src/internal/storage-mapping.ts +0 -66
- package/src/internal/storage-plans.ts +218 -0
- package/src/internal/storage-tables.ts +0 -35
- package/src/internal/storage-validation.ts +58 -0
- package/src/internal/transaction-kernel.ts +6 -2
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
import { CurrentCommitJournal, hasCommitScope } from "@yielded/auth/Hooks";
|
|
2
|
+
import type { LoginIdentifier } from "@yielded/auth/Identity";
|
|
3
|
+
import {
|
|
4
|
+
EncodedPasswordHash,
|
|
5
|
+
type PasswordPersistence,
|
|
6
|
+
PasswordUnavailable,
|
|
7
|
+
snapshotPasswordCredential,
|
|
8
|
+
type PasswordCredentialSnapshot,
|
|
9
|
+
} from "@yielded/auth/Password";
|
|
10
|
+
import { reportPersistenceFailure } from "@yielded/auth/Persistence";
|
|
11
|
+
import { Effect, Option, Redacted, Schema } from "effect";
|
|
12
|
+
|
|
13
|
+
import type { PersistenceMappingError } from "./mapping-error";
|
|
14
|
+
import type { AnyPasswordPersistenceMapping } from "./models/password-model";
|
|
15
|
+
import { sqlMapping, type SqlTable } from "./native-sql-table";
|
|
16
|
+
import type { PasswordSqlConfiguration } from "./password-kernel";
|
|
17
|
+
import { NativeDatabase } from "./transaction-kernel";
|
|
18
|
+
|
|
19
|
+
type Row = Readonly<Record<string, unknown>>;
|
|
20
|
+
const unavailable = () => PasswordUnavailable.make({});
|
|
21
|
+
|
|
22
|
+
const failure = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
23
|
+
reportPersistenceFailure(effect, Schema.is(PasswordUnavailable)).pipe(
|
|
24
|
+
Effect.mapError(unavailable),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
export const samePasswordCredentialSnapshot = (
|
|
28
|
+
a: PasswordCredentialSnapshot,
|
|
29
|
+
b: PasswordCredentialSnapshot,
|
|
30
|
+
) =>
|
|
31
|
+
a.moduleId === b.moduleId &&
|
|
32
|
+
a.revision.subjectId === b.revision.subjectId &&
|
|
33
|
+
a.revision.securityRevision === b.revision.securityRevision &&
|
|
34
|
+
a.revision.credentials.length === b.revision.credentials.length &&
|
|
35
|
+
new Set(a.revision.credentials.map((entry) => entry.credentialId)).size ===
|
|
36
|
+
a.revision.credentials.length &&
|
|
37
|
+
new Set(b.revision.credentials.map((entry) => entry.credentialId)).size ===
|
|
38
|
+
b.revision.credentials.length &&
|
|
39
|
+
a.revision.credentials.every((entry) =>
|
|
40
|
+
b.revision.credentials.some(
|
|
41
|
+
(other) => entry.credentialId === other.credentialId && entry.revision === other.revision,
|
|
42
|
+
),
|
|
43
|
+
) &&
|
|
44
|
+
a.credentialId === b.credentialId &&
|
|
45
|
+
a.credentialRevision === b.credentialRevision &&
|
|
46
|
+
a.verifierVersion === b.verifierVersion &&
|
|
47
|
+
Redacted.value(a.verifier) === Redacted.value(b.verifier) &&
|
|
48
|
+
a.normalization === b.normalization &&
|
|
49
|
+
a.identifier.namespace === b.identifier.namespace &&
|
|
50
|
+
a.identifier.value === b.identifier.value &&
|
|
51
|
+
a.identifierBindingRevision === b.identifierBindingRevision &&
|
|
52
|
+
a.identifierVerifiedAtMillis === b.identifierVerifiedAtMillis;
|
|
53
|
+
|
|
54
|
+
/** Lookup is read-only; rehash is one conditional maintenance write, never authentication. */
|
|
55
|
+
export const makePasswordCredentials = Effect.fnUntraced(function* (
|
|
56
|
+
table: (table: object) => SqlTable,
|
|
57
|
+
mapping: AnyPasswordPersistenceMapping,
|
|
58
|
+
options: PasswordSqlConfiguration,
|
|
59
|
+
): Effect.fn.Return<
|
|
60
|
+
Pick<PasswordPersistence["Service"], "findCredential" | "rehashIfCurrent">,
|
|
61
|
+
PasswordUnavailable,
|
|
62
|
+
NativeDatabase
|
|
63
|
+
> {
|
|
64
|
+
const sql = (yield* NativeDatabase).$client.withoutTransforms();
|
|
65
|
+
const parent = yield* Effect.serviceOption(CurrentCommitJournal);
|
|
66
|
+
|
|
67
|
+
const [s, i, c] = yield* sqlMapping(
|
|
68
|
+
() =>
|
|
69
|
+
[
|
|
70
|
+
table(mapping.subject.table),
|
|
71
|
+
table(mapping.identifier.table),
|
|
72
|
+
table(mapping.credential.table),
|
|
73
|
+
] as const,
|
|
74
|
+
).pipe(failure);
|
|
75
|
+
|
|
76
|
+
const standalone = Effect.gen(function* () {
|
|
77
|
+
if (options.coordinated) {
|
|
78
|
+
const current = yield* Effect.serviceOption(CurrentCommitJournal);
|
|
79
|
+
|
|
80
|
+
if (Option.isNone(parent) || Option.isNone(current) || parent.value !== current.value)
|
|
81
|
+
return yield* unavailable();
|
|
82
|
+
} else {
|
|
83
|
+
if (yield* hasCommitScope) return yield* unavailable();
|
|
84
|
+
yield* options.standaloneGuard;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const allocate = <A>(
|
|
89
|
+
asynchronous: Effect.Effect<A, PersistenceMappingError> | undefined,
|
|
90
|
+
sync: (() => A) | undefined,
|
|
91
|
+
) => {
|
|
92
|
+
if (options.mode !== "synchronous" && asynchronous !== undefined) return asynchronous;
|
|
93
|
+
|
|
94
|
+
return sync === undefined
|
|
95
|
+
? Effect.fail(unavailable())
|
|
96
|
+
: Effect.try({ try: sync, catch: unavailable });
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Bind each native ID through its own column codec. Physical encodings of the
|
|
100
|
+
// same application ID need not match across subject, identifier and password.
|
|
101
|
+
const snapshot = Effect.fnUntraced(function* (
|
|
102
|
+
nativeId: unknown,
|
|
103
|
+
moduleId: string,
|
|
104
|
+
identifier: LoginIdentifier,
|
|
105
|
+
) {
|
|
106
|
+
const subjectTable = s.as("password_subject");
|
|
107
|
+
const identifierTable = i.as("password_identifier");
|
|
108
|
+
const credentialTable = c.as("password_credential");
|
|
109
|
+
|
|
110
|
+
const rows = yield* sqlMapping(
|
|
111
|
+
() =>
|
|
112
|
+
sql<Row>`select ${subjectTable.fields("subject_")}, ${identifierTable.fields("identifier_")}, ${credentialTable.fields("credential_")}
|
|
113
|
+
from ${subjectTable.name}
|
|
114
|
+
inner join ${identifierTable.name} on ${identifierTable.column(mapping.identifier.namespace)} = ${identifierTable.value(mapping.identifier.namespace, identifier.namespace)}
|
|
115
|
+
and ${identifierTable.column(mapping.identifier.value)} = ${identifierTable.value(mapping.identifier.value, identifier.value)}
|
|
116
|
+
and ${identifierTable.column(mapping.identifier.subjectId)} = ${identifierTable.value(mapping.identifier.subjectId, nativeId)}
|
|
117
|
+
inner join ${credentialTable.name} on ${credentialTable.column(mapping.credential.moduleId)} = ${credentialTable.value(mapping.credential.moduleId, moduleId)}
|
|
118
|
+
and ${credentialTable.column(mapping.credential.subjectId)} = ${credentialTable.value(mapping.credential.subjectId, nativeId)}
|
|
119
|
+
where ${subjectTable.column(mapping.subject.id)} = ${subjectTable.value(mapping.subject.id, nativeId)}
|
|
120
|
+
`,
|
|
121
|
+
).pipe(Effect.flatten);
|
|
122
|
+
|
|
123
|
+
if (rows.length !== 1) return undefined;
|
|
124
|
+
|
|
125
|
+
const [subject, identifierRow, credential] = yield* sqlMapping(
|
|
126
|
+
() =>
|
|
127
|
+
[
|
|
128
|
+
s.decode(rows[0]!, "subject_"),
|
|
129
|
+
i.decode(rows[0]!, "identifier_"),
|
|
130
|
+
c.decode(rows[0]!, "credential_"),
|
|
131
|
+
] as const,
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
if (
|
|
135
|
+
yield* sqlMapping(
|
|
136
|
+
() =>
|
|
137
|
+
!mapping.subject.isActiveStatus(subject[mapping.subject.status]) ||
|
|
138
|
+
!mapping.identifier.isCurrent(identifierRow) ||
|
|
139
|
+
!mapping.subjectId.equals(nativeId, identifierRow[mapping.identifier.subjectId]) ||
|
|
140
|
+
!mapping.subjectId.equals(nativeId, subject[mapping.subject.id]) ||
|
|
141
|
+
!mapping.subjectId.equals(nativeId, credential[mapping.credential.subjectId]),
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
return undefined;
|
|
145
|
+
|
|
146
|
+
const captured = yield* sqlMapping(() =>
|
|
147
|
+
mapping.credential.decode({ moduleId, subject, identifier: identifierRow, credential }),
|
|
148
|
+
).pipe(Effect.flatten, Effect.flatMap(snapshotPasswordCredential));
|
|
149
|
+
|
|
150
|
+
return captured;
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
findCredential: (uncaptured) =>
|
|
155
|
+
Effect.gen(function* () {
|
|
156
|
+
const input = { ...uncaptured, identifier: { ...uncaptured.identifier } };
|
|
157
|
+
|
|
158
|
+
yield* standalone;
|
|
159
|
+
|
|
160
|
+
const identifiers = yield* sqlMapping(
|
|
161
|
+
() => sql<Row>`select ${i.fields("i_")} from ${i.name}
|
|
162
|
+
where ${i.column(mapping.identifier.namespace)} = ${i.value(mapping.identifier.namespace, input.identifier.namespace)}
|
|
163
|
+
and ${i.column(mapping.identifier.value)} = ${i.value(mapping.identifier.value, input.identifier.value)}`,
|
|
164
|
+
).pipe(Effect.flatten);
|
|
165
|
+
|
|
166
|
+
const identifier =
|
|
167
|
+
identifiers.length === 1
|
|
168
|
+
? yield* sqlMapping(() => i.decode(identifiers[0]!, "i_"))
|
|
169
|
+
: undefined;
|
|
170
|
+
|
|
171
|
+
if (
|
|
172
|
+
identifier === undefined ||
|
|
173
|
+
!(yield* sqlMapping(() => mapping.identifier.isCurrent(identifier)))
|
|
174
|
+
)
|
|
175
|
+
return Option.none();
|
|
176
|
+
|
|
177
|
+
const credential = yield* snapshot(
|
|
178
|
+
identifier[mapping.identifier.subjectId],
|
|
179
|
+
input.moduleId,
|
|
180
|
+
input.identifier,
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return credential === undefined ||
|
|
184
|
+
(input.subjectId !== undefined && credential.revision.subjectId !== input.subjectId)
|
|
185
|
+
? Option.none()
|
|
186
|
+
: Option.some(credential);
|
|
187
|
+
}).pipe(failure),
|
|
188
|
+
rehashIfCurrent: (input) =>
|
|
189
|
+
Effect.gen(function* () {
|
|
190
|
+
const nextVerifier = Redacted.make(
|
|
191
|
+
yield* Schema.decodeEffect(EncodedPasswordHash)(Redacted.value(input.nextVerifier)),
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const credential = yield* snapshotPasswordCredential(input.credential);
|
|
195
|
+
|
|
196
|
+
yield* standalone;
|
|
197
|
+
const nativeId = yield* mapping.subjectId.toNative(credential.revision.subjectId);
|
|
198
|
+
const version = yield* allocate(mapping.allocateRevision, mapping.allocateRevisionSync);
|
|
199
|
+
|
|
200
|
+
if (version === credential.verifierVersion) return yield* unavailable();
|
|
201
|
+
yield* sqlMapping(() => {
|
|
202
|
+
const encoded = mapping.credential.encodeVerifier(nextVerifier, version);
|
|
203
|
+
|
|
204
|
+
// A custom encoder cannot turn maintenance into a semantic credential change.
|
|
205
|
+
const values = {
|
|
206
|
+
[mapping.credential.verifier]: encoded[mapping.credential.verifier],
|
|
207
|
+
[mapping.credential.verifierVersion]: encoded[mapping.credential.verifierVersion],
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
if (Object.values(values).some((value) => value === undefined)) throw unavailable();
|
|
211
|
+
|
|
212
|
+
// Ordinary identity predicates retain index use; the full CAS ignores text collation.
|
|
213
|
+
const exact = sql.and(
|
|
214
|
+
Object.entries({
|
|
215
|
+
[mapping.credential.moduleId]: credential.moduleId,
|
|
216
|
+
[mapping.credential.subjectId]: nativeId,
|
|
217
|
+
[mapping.credential.credentialId]: credential.credentialId,
|
|
218
|
+
[mapping.credential.credentialRevision]: credential.credentialRevision,
|
|
219
|
+
[mapping.credential.verifierVersion]: credential.verifierVersion,
|
|
220
|
+
[mapping.credential.verifier]: Redacted.value(credential.verifier),
|
|
221
|
+
[mapping.credential.normalization]: credential.normalization,
|
|
222
|
+
}).map(([key, value]) => {
|
|
223
|
+
const column = c.column(key),
|
|
224
|
+
bound = c.value(key, value);
|
|
225
|
+
|
|
226
|
+
if (typeof value !== "string") return sql`${column} = ${bound}`;
|
|
227
|
+
|
|
228
|
+
return sql.onDialectOrElse({
|
|
229
|
+
mysql: () => sql`binary ${column} = binary ${bound}`,
|
|
230
|
+
pg: () =>
|
|
231
|
+
sql`convert_to(cast(${column} as text), 'UTF8') = convert_to(cast(${bound} as text), 'UTF8')`,
|
|
232
|
+
orElse: () => sql`cast(${column} as blob) = cast(${bound} as blob)`,
|
|
233
|
+
});
|
|
234
|
+
}),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
return sql`${c.update(values)}
|
|
238
|
+
where ${c.column(mapping.credential.moduleId)} = ${c.value(mapping.credential.moduleId, credential.moduleId)}
|
|
239
|
+
and ${c.column(mapping.credential.subjectId)} = ${c.value(mapping.credential.subjectId, nativeId)}
|
|
240
|
+
and ${c.column(mapping.credential.credentialId)} = ${c.value(mapping.credential.credentialId, credential.credentialId)}
|
|
241
|
+
and ${exact}`;
|
|
242
|
+
}).pipe(Effect.flatten);
|
|
243
|
+
}).pipe(failure),
|
|
244
|
+
} satisfies Pick<PasswordPersistence["Service"], "findCredential" | "rehashIfCurrent">;
|
|
245
|
+
});
|