@ambushsoftworks/nestjs-auth-graphql 0.16.0-rc.2 → 0.16.0-rc.3

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/CHANGELOG.md CHANGED
@@ -27,6 +27,37 @@ for buried obligations. **The marker was introduced in 0.9.0 and has not been
27
27
  retro-applied**, so it is reliable from 0.9.0 onward only. Convention adopted
28
28
  from `@ambushsoftworks/nestjs-payments-graphql`.
29
29
 
30
+ ## [0.16.0-rc.3] - 2026-10-01
31
+
32
+ The repository contract from rc.2 could not run against a real schema,
33
+ including the reference schema in this README. Found by LiftIQ on Postgres.
34
+
35
+ ### Fixed
36
+
37
+ - **`assertPasskeyRepositoryContract` takes options** (defaults unchanged):
38
+ - `userIds: [a, b, ...]`: existing users to attach credentials to. Without
39
+ them the contract invented user ids, so any schema with a foreign key to
40
+ users, the README's included, failed with `P2003` on the first insert.
41
+ - `realms: false`: for deployments without realms. Every fixture carried a
42
+ realm and every check demanded it back, though the interface says realm
43
+ is only set when realms are enabled.
44
+ - `cleanup({ credentialIds, challengeIds })`: called once, pass or fail,
45
+ with every id created. Before, every run left rows in the database the
46
+ README told adopters to use.
47
+ - **Listing is checked by containment, not an exact list**, so users who
48
+ already have passkeys do not fail it.
49
+ - **`userIds` without `cleanup` is refused**: the contract creates active
50
+ passkeys on those users, which would show in their passkey lists. Pass
51
+ `cleanup: () => {}` to keep them on purpose.
52
+
53
+ ### Added
54
+
55
+ - **`examples/passkeys/`**: the reference Prisma schema and a
56
+ `PrismaPasskeyRepository` built from the README's snippets, verified with the
57
+ contract on PostgreSQL 16, with and without realms, with a foreign key to
58
+ users, and with cleanup removing every row on both a passing and a failing
59
+ run.
60
+
30
61
  ## [0.16.0-rc.2] - 2026-10-01
31
62
 
32
63
  Fixes from LiftIQ's pilot of rc.1 on a real Android Credential Manager. Still a
package/README.md CHANGED
@@ -1276,7 +1276,14 @@ tests use, against a real database:
1276
1276
  import { assertPasskeyRepositoryContract } from '@ambushsoftworks/nestjs-auth-graphql/testing';
1277
1277
 
1278
1278
  it('meets the passkey repository contract', async () => {
1279
- await assertPasskeyRepositoryContract(() => new PrismaPasskeyRepository(prisma));
1279
+ await assertPasskeyRepositoryContract(() => new PrismaPasskeyRepository(prisma), {
1280
+ userIds: [alice.id, bob.id], // existing users: the reference schema has a foreign key to User
1281
+ realms: false, // only if you do not use realms
1282
+ cleanup: async ({ credentialIds, challengeIds }) => {
1283
+ await prisma.passkeyCredential.deleteMany({ where: { credentialId: { in: credentialIds } } });
1284
+ await prisma.passkeyChallenge.deleteMany({ where: { id: { in: challengeIds } } });
1285
+ },
1286
+ });
1280
1287
  });
1281
1288
  ```
1282
1289
 
@@ -1284,6 +1291,15 @@ It resolves when the adapter is correct and otherwise rejects naming the rule
1284
1291
  broken: concurrent claims of one challenge, a counter column too small for a
1285
1292
  uint32, an unmapped duplicate, a dropped realm, hidden deactivated
1286
1293
  credentials, expiry left to a TTL. Framework-agnostic.
1294
+
1295
+ | Option | Default | Use it when |
1296
+ |---|---|---|
1297
+ | `userIds` | invented ids | Your credentials table has a foreign key to users, as the reference schema does. At least two existing users; they may already have passkeys. **Requires `cleanup`** |
1298
+ | `realms` | `true` | `false` when you do not set `realmExtractorInstance` and store no realm |
1299
+ | `cleanup` | none | Always, against a shared database. Called once, pass or fail, with every id created; the list may include ids never stored, so delete with `deleteMany` |
1300
+
1301
+ `examples/passkeys/` has the reference schema and a Prisma adapter that pass
1302
+ this contract on PostgreSQL 16, with and without realms.
1287
1303
  `InMemoryPasskeyRepository` is exported for tests and single-instance
1288
1304
  development.
1289
1305
 
@@ -1,3 +1,3 @@
1
- export { assertPasskeyRepositoryContract, PasskeyRepositoryContractError, } from './passkey-repository.contract';
1
+ export { assertPasskeyRepositoryContract, PasskeyRepositoryContractError, type PasskeyRepositoryContractOptions, type PasskeyContractCreatedIds, } from './passkey-repository.contract';
2
2
  export { SoftwareAuthenticator, type CreateOptions as SoftwareAuthenticatorCreateOptions, type GetOptions as SoftwareAuthenticatorGetOptions, type SoftwareAlgorithm, } from '../test-utils/software-authenticator';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,GAC/B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,qBAAqB,EACrB,KAAK,aAAa,IAAI,kCAAkC,EACxD,KAAK,UAAU,IAAI,+BAA+B,EAClD,KAAK,iBAAiB,GACvB,MAAM,sCAAsC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAMA,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,EAC9B,KAAK,gCAAgC,EACrC,KAAK,yBAAyB,GAC/B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,qBAAqB,EACrB,KAAK,aAAa,IAAI,kCAAkC,EACxD,KAAK,UAAU,IAAI,+BAA+B,EAClD,KAAK,iBAAiB,GACvB,MAAM,sCAAsC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":";;;AAMA,6EAGuC;AAFrC,8IAAA,+BAA+B,OAAA;AAC/B,6IAAA,8BAA8B,OAAA;AAEhC,+EAK8C;AAJ5C,+HAAA,qBAAqB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":";;;AAMA,6EAKuC;AAJrC,8IAAA,+BAA+B,OAAA;AAC/B,6IAAA,8BAA8B,OAAA;AAIhC,+EAK8C;AAJ5C,+HAAA,qBAAqB,OAAA"}
@@ -1,8 +1,19 @@
1
1
  import { IPasskeyRepository } from '../interfaces/passkey-repository.interface';
2
2
  export declare class PasskeyRepositoryContractError extends Error {
3
- constructor(check: string, detail: string);
3
+ constructor(check: string, detail: string, options?: {
4
+ cause?: unknown;
5
+ });
6
+ }
7
+ export interface PasskeyContractCreatedIds {
8
+ credentialIds: string[];
9
+ challengeIds: string[];
10
+ }
11
+ export interface PasskeyRepositoryContractOptions {
12
+ realms?: boolean;
13
+ userIds?: readonly string[];
14
+ cleanup?: (created: PasskeyContractCreatedIds) => void | Promise<void>;
4
15
  }
5
16
  type Factory = () => IPasskeyRepository | Promise<IPasskeyRepository>;
6
- export declare function assertPasskeyRepositoryContract(factory: Factory): Promise<void>;
17
+ export declare function assertPasskeyRepositoryContract(factory: Factory, options?: PasskeyRepositoryContractOptions): Promise<void>;
7
18
  export {};
8
19
  //# sourceMappingURL=passkey-repository.contract.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"passkey-repository.contract.d.ts","sourceRoot":"","sources":["../../src/testing/passkey-repository.contract.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAGnB,MAAM,4CAA4C,CAAC;AAGpD,qBAAa,8BAA+B,SAAQ,KAAK;gBAC3C,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAI1C;AAED,KAAK,OAAO,GAAG,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAuCtE,wBAAsB,+BAA+B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAarF"}
1
+ {"version":3,"file":"passkey-repository.contract.d.ts","sourceRoot":"","sources":["../../src/testing/passkey-repository.contract.ts"],"names":[],"mappings":"AACA,OAAO,EACL,kBAAkB,EAInB,MAAM,4CAA4C,CAAC;AAGpD,qBAAa,8BAA+B,SAAQ,KAAK;gBAC3C,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAOzE;AAGD,MAAM,WAAW,yBAAyB;IAKxC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,gCAAgC;IAQ/C,MAAM,CAAC,EAAE,OAAO,CAAC;IAYjB,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAW5B,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE;AAED,KAAK,OAAO,GAAG,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;AA8CtE,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,OAAO,EAChB,OAAO,GAAE,gCAAqC,GAC7C,OAAO,CAAC,IAAI,CAAC,CAwCf"}
@@ -5,60 +5,145 @@ exports.assertPasskeyRepositoryContract = assertPasskeyRepositoryContract;
5
5
  const crypto_1 = require("crypto");
6
6
  const passkey_repository_interface_1 = require("../interfaces/passkey-repository.interface");
7
7
  class PasskeyRepositoryContractError extends Error {
8
- constructor(check, detail) {
8
+ constructor(check, detail, options) {
9
9
  super(`IPasskeyRepository contract — ${check}: ${detail}`);
10
10
  this.name = 'PasskeyRepositoryContractError';
11
+ if (options?.cause !== undefined) {
12
+ this.cause = options.cause;
13
+ }
11
14
  }
12
15
  }
13
16
  exports.PasskeyRepositoryContractError = PasskeyRepositoryContractError;
14
17
  const CONCURRENCY = 8;
15
18
  const MAX_COUNTER = 2 ** 32 - 1;
16
- async function assertPasskeyRepositoryContract(factory) {
17
- for (const [name, check] of CHECKS) {
18
- const repository = await factory();
19
+ async function assertPasskeyRepositoryContract(factory, options = {}) {
20
+ validateOptions(options);
21
+ const run = new ContractRun(options);
22
+ let failure;
23
+ try {
24
+ for (const [name, check] of CHECKS) {
25
+ try {
26
+ const repository = run.track(await factory());
27
+ await check(repository, run);
28
+ }
29
+ catch (error) {
30
+ if (error instanceof PasskeyRepositoryContractError)
31
+ throw error;
32
+ throw new PasskeyRepositoryContractError(name, `threw ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`);
33
+ }
34
+ }
35
+ }
36
+ catch (error) {
37
+ failure = error;
38
+ }
39
+ if (options.cleanup) {
19
40
  try {
20
- await check(repository);
41
+ await options.cleanup(run.created());
21
42
  }
22
- catch (error) {
23
- if (error instanceof PasskeyRepositoryContractError)
24
- throw error;
25
- throw new PasskeyRepositoryContractError(name, `threw ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`);
43
+ catch (cleanupError) {
44
+ if (failure instanceof PasskeyRepositoryContractError) {
45
+ throw new PasskeyRepositoryContractError('cleanup', `${failure.message} — and cleanup then threw: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`, { cause: cleanupError });
46
+ }
47
+ if (failure === undefined)
48
+ throw cleanupError;
26
49
  }
27
50
  }
51
+ if (failure !== undefined)
52
+ throw failure;
53
+ }
54
+ function validateOptions(options) {
55
+ const { realms, userIds, cleanup } = options;
56
+ if (realms !== undefined && typeof realms !== 'boolean') {
57
+ throw new TypeError(`assertPasskeyRepositoryContract: realms must be a boolean; received ${String(realms)}`);
58
+ }
59
+ if (cleanup !== undefined && typeof cleanup !== 'function') {
60
+ throw new TypeError('assertPasskeyRepositoryContract: cleanup must be a function');
61
+ }
62
+ if (userIds !== undefined) {
63
+ const ids = Array.isArray(userIds) ? userIds : [];
64
+ if (ids.length < 2 || ids.some((id) => typeof id !== 'string' || id === '') || new Set(ids).size !== ids.length) {
65
+ throw new TypeError('assertPasskeyRepositoryContract: userIds must hold at least two distinct, non-empty ids of ' +
66
+ `existing users; received ${JSON.stringify(userIds)}`);
67
+ }
68
+ if (!cleanup) {
69
+ throw new TypeError('assertPasskeyRepositoryContract: userIds needs cleanup. The contract creates active passkeys ' +
70
+ 'on those users, which would appear in their passkey lists. Delete them in cleanup, or pass ' +
71
+ '`cleanup: () => {}` to keep them on purpose.');
72
+ }
73
+ }
74
+ }
75
+ class ContractRun {
76
+ constructor(options) {
77
+ this.credentialIds = new Set();
78
+ this.challengeIds = new Set();
79
+ this.realms = options.realms ?? true;
80
+ this.users = options.userIds;
81
+ }
82
+ user(which) {
83
+ return this.users?.[which] ?? `contract-user-${(0, crypto_1.randomUUID)()}`;
84
+ }
85
+ realm(realm) {
86
+ return this.realms ? { realm } : {};
87
+ }
88
+ created() {
89
+ return { credentialIds: [...this.credentialIds], challengeIds: [...this.challengeIds] };
90
+ }
91
+ track(repo) {
92
+ return new Proxy(repo, {
93
+ get: (target, property, receiver) => {
94
+ const value = Reflect.get(target, property, receiver);
95
+ if (property === 'storeChallenge') {
96
+ return async (input) => {
97
+ const id = await value.call(target, input);
98
+ if (typeof id === 'string')
99
+ this.challengeIds.add(id);
100
+ return id;
101
+ };
102
+ }
103
+ if (property === 'createCredential') {
104
+ return async (record) => {
105
+ this.credentialIds.add(record.credentialId);
106
+ return value.call(target, record);
107
+ };
108
+ }
109
+ return typeof value === 'function' ? value.bind(target) : value;
110
+ },
111
+ });
112
+ }
113
+ credential(overrides = {}) {
114
+ return {
115
+ credentialId: `contract-${(0, crypto_1.randomUUID)()}`,
116
+ userId: this.user(0),
117
+ ...this.realm('contract-realm'),
118
+ publicKey: 'pQECAyYgASFYIA',
119
+ counter: 0,
120
+ userHandle: `handle-${(0, crypto_1.randomUUID)()}`,
121
+ transports: ['internal', 'hybrid'],
122
+ deviceType: 'multiDevice',
123
+ backedUp: true,
124
+ aaguid: '00000000-0000-0000-0000-000000000000',
125
+ name: 'Contract passkey',
126
+ createdAt: new Date(Math.floor(Date.now() / 1000) * 1000),
127
+ lastUsedAt: null,
128
+ isActive: true,
129
+ ...overrides,
130
+ };
131
+ }
28
132
  }
29
133
  function fail(check, detail) {
30
134
  throw new PasskeyRepositoryContractError(check, detail);
31
135
  }
32
- function credential(overrides = {}) {
33
- return {
34
- credentialId: `contract-${(0, crypto_1.randomUUID)()}`,
35
- userId: `contract-user-${(0, crypto_1.randomUUID)()}`,
36
- realm: 'contract-realm',
37
- publicKey: 'pQECAyYgASFYIA',
38
- counter: 0,
39
- userHandle: `handle-${(0, crypto_1.randomUUID)()}`,
40
- transports: ['internal', 'hybrid'],
41
- deviceType: 'multiDevice',
42
- backedUp: true,
43
- aaguid: '00000000-0000-0000-0000-000000000000',
44
- name: 'Contract passkey',
45
- createdAt: new Date(Math.floor(Date.now() / 1000) * 1000),
46
- lastUsedAt: null,
47
- isActive: true,
48
- ...overrides,
49
- };
50
- }
51
136
  const sameTime = (a, b) => a === b || (a instanceof Date && b instanceof Date && Math.abs(a.getTime() - b.getTime()) < 1000);
52
137
  const CHECKS = [
53
138
  [
54
139
  'challenges round-trip every field',
55
- async (repo) => {
140
+ async (repo, run) => {
56
141
  const check = 'challenges round-trip every field';
57
142
  const input = {
58
143
  challenge: `challenge-${(0, crypto_1.randomUUID)()}`,
59
144
  purpose: 'registration',
60
- userId: `user-${(0, crypto_1.randomUUID)()}`,
61
- realm: 'contract-realm',
145
+ userId: run.user(0),
146
+ ...run.realm('contract-realm'),
62
147
  userHandle: `handle-${(0, crypto_1.randomUUID)()}`,
63
148
  expiresAt: new Date(Date.now() + 60_000),
64
149
  };
@@ -68,7 +153,8 @@ const CHECKS = [
68
153
  const got = await repo.consumeChallenge(id);
69
154
  if (!got)
70
155
  fail(check, 'consumeChallenge returned null for a fresh challenge');
71
- for (const key of ['challenge', 'purpose', 'userId', 'realm', 'userHandle']) {
156
+ const keys = ['challenge', 'purpose', 'userId', 'userHandle', ...(run.realms ? ['realm'] : [])];
157
+ for (const key of keys) {
72
158
  if (got[key] !== input[key]) {
73
159
  fail(check, `${key} came back as ${JSON.stringify(got[key])}, stored ${JSON.stringify(input[key])}`);
74
160
  }
@@ -81,11 +167,12 @@ const CHECKS = [
81
167
  ],
82
168
  [
83
169
  'a challenge is claimed exactly once, under concurrency',
84
- async (repo) => {
170
+ async (repo, run) => {
85
171
  const check = 'a challenge is claimed exactly once, under concurrency';
86
172
  const id = await repo.storeChallenge({
87
173
  challenge: 'c',
88
174
  purpose: 'authentication',
175
+ ...run.realm('contract-realm'),
89
176
  expiresAt: new Date(Date.now() + 60_000),
90
177
  });
91
178
  const claims = await Promise.all(Array.from({ length: CONCURRENCY }, () => repo.consumeChallenge(id)));
@@ -100,11 +187,12 @@ const CHECKS = [
100
187
  ],
101
188
  [
102
189
  'expired and unknown challenges answer null',
103
- async (repo) => {
190
+ async (repo, run) => {
104
191
  const check = 'expired and unknown challenges answer null';
105
192
  const id = await repo.storeChallenge({
106
193
  challenge: 'c',
107
194
  purpose: 'authentication',
195
+ ...run.realm('contract-realm'),
108
196
  expiresAt: new Date(Date.now() - 1_000),
109
197
  });
110
198
  if (await repo.consumeChallenge(id)) {
@@ -116,17 +204,19 @@ const CHECKS = [
116
204
  ],
117
205
  [
118
206
  'credentials round-trip every field, counter up to 2^32-1',
119
- async (repo) => {
207
+ async (repo, run) => {
120
208
  const check = 'credentials round-trip every field';
121
- const record = credential({ counter: MAX_COUNTER, metadata: { device: 'contract' } });
209
+ const record = run.credential({ counter: MAX_COUNTER, metadata: { device: 'contract' } });
122
210
  await repo.createCredential(record);
123
- const got = await repo.getCredential(record.credentialId);
211
+ const got = await repo.getCredential(record.credentialId, record.realm);
124
212
  if (!got)
125
213
  fail(check, 'getCredential returned null for a credential just created');
126
- for (const key of [
127
- 'credentialId', 'userId', 'realm', 'publicKey', 'counter', 'userHandle',
214
+ const keys = [
215
+ 'credentialId', 'userId', 'publicKey', 'counter', 'userHandle',
128
216
  'deviceType', 'backedUp', 'aaguid', 'name', 'isActive',
129
- ]) {
217
+ ...(run.realms ? ['realm'] : []),
218
+ ];
219
+ for (const key of keys) {
130
220
  if (got[key] !== record[key]) {
131
221
  fail(check, `${key} came back as ${JSON.stringify(got[key])}, stored ${JSON.stringify(record[key])}` +
132
222
  (key === 'counter' ? ' (a uint32: store it in a bigint column)' : ''));
@@ -143,19 +233,19 @@ const CHECKS = [
143
233
  ],
144
234
  [
145
235
  'a duplicate credential id throws PasskeyCredentialExistsError and leaves the original',
146
- async (repo) => {
236
+ async (repo, run) => {
147
237
  const check = 'a duplicate credential id is refused';
148
- const original = credential();
238
+ const original = run.credential();
149
239
  await repo.createCredential(original);
150
240
  try {
151
- await repo.createCredential(credential({ credentialId: original.credentialId }));
241
+ await repo.createCredential(run.credential({ credentialId: original.credentialId, userId: run.user(1) }));
152
242
  }
153
243
  catch (error) {
154
244
  if (!(error instanceof passkey_repository_interface_1.PasskeyCredentialExistsError)) {
155
245
  fail(check, `threw ${error instanceof Error ? error.name : String(error)}; map your unique-constraint ` +
156
246
  'violation (Prisma P2002) to PasskeyCredentialExistsError');
157
247
  }
158
- const kept = await repo.getCredential(original.credentialId);
248
+ const kept = await repo.getCredential(original.credentialId, original.realm);
159
249
  if (kept?.userId !== original.userId)
160
250
  fail(check, "the original credential's owner changed");
161
251
  return;
@@ -165,12 +255,12 @@ const CHECKS = [
165
255
  ],
166
256
  [
167
257
  'deactivated credentials are still returned',
168
- async (repo) => {
258
+ async (repo, run) => {
169
259
  const check = 'deactivated credentials are still returned';
170
- const record = credential();
260
+ const record = run.credential();
171
261
  await repo.createCredential(record);
172
262
  await repo.deactivateCredential(record.credentialId);
173
- const got = await repo.getCredential(record.credentialId);
263
+ const got = await repo.getCredential(record.credentialId, record.realm);
174
264
  if (!got)
175
265
  fail(check, 'getCredential hid a deactivated credential; the package decides, not the store');
176
266
  if (got.isActive !== false)
@@ -182,31 +272,49 @@ const CHECKS = [
182
272
  },
183
273
  ],
184
274
  [
185
- 'listing is by user, narrowed by realm',
186
- async (repo) => {
187
- const check = 'listing is by user, narrowed by realm';
188
- const userId = `contract-user-${(0, crypto_1.randomUUID)()}`;
189
- const mine = credential({ userId, realm: 'realm-a' });
190
- const otherRealm = credential({ userId, realm: 'realm-b' });
191
- const otherUser = credential({ realm: 'realm-a' });
275
+ 'listing is by user, narrowed by realm when realms are on',
276
+ async (repo, run) => {
277
+ const check = 'listing is by user';
278
+ const ids = (list) => new Set(list.map((c) => c.credentialId));
279
+ const user = run.user(0);
280
+ if (!run.realms) {
281
+ const mine = run.credential({ userId: user });
282
+ const otherUser = run.credential({ userId: run.user(1) });
283
+ for (const c of [mine, otherUser])
284
+ await repo.createCredential(c);
285
+ const listed = ids(await repo.listCredentials(user));
286
+ if (!listed.has(mine.credentialId))
287
+ fail(check, "listCredentials(user) left out the user's passkey");
288
+ if (listed.has(otherUser.credentialId))
289
+ fail(check, "listCredentials(user) returned another user's passkey");
290
+ return;
291
+ }
292
+ const mine = run.credential({ userId: user, realm: 'realm-a' });
293
+ const otherRealm = run.credential({ userId: user, realm: 'realm-b' });
294
+ const otherUser = run.credential({ userId: run.user(1), realm: 'realm-a' });
192
295
  for (const c of [mine, otherRealm, otherUser])
193
296
  await repo.createCredential(c);
194
- const inRealm = (await repo.listCredentials(userId, 'realm-a')).map((c) => c.credentialId);
195
- if (inRealm.length !== 1 || inRealm[0] !== mine.credentialId) {
196
- fail(check, `listCredentials(user, 'realm-a') returned ${JSON.stringify(inRealm)}`);
297
+ const inRealm = ids(await repo.listCredentials(user, 'realm-a'));
298
+ if (!inRealm.has(mine.credentialId))
299
+ fail(check, "listCredentials(user, 'realm-a') left out the user's passkey");
300
+ if (inRealm.has(otherRealm.credentialId)) {
301
+ fail(check, "listCredentials(user, 'realm-a') returned the user's passkey from realm-b");
197
302
  }
198
- const all = (await repo.listCredentials(userId)).map((c) => c.credentialId).sort();
199
- const expected = [mine.credentialId, otherRealm.credentialId].sort();
200
- if (JSON.stringify(all) !== JSON.stringify(expected)) {
201
- fail(check, `listCredentials(user) returned ${JSON.stringify(all)}`);
303
+ if (inRealm.has(otherUser.credentialId))
304
+ fail(check, "listCredentials(user, 'realm-a') returned another user's passkey");
305
+ const all = ids(await repo.listCredentials(user));
306
+ if (!all.has(mine.credentialId) || !all.has(otherRealm.credentialId)) {
307
+ fail(check, 'listCredentials(user) without a realm left out one of the user’s passkeys');
202
308
  }
309
+ if (all.has(otherUser.credentialId))
310
+ fail(check, "listCredentials(user) returned another user's passkey");
203
311
  },
204
312
  ],
205
313
  [
206
314
  'updateCounter is a compare-and-set, under concurrency',
207
- async (repo) => {
315
+ async (repo, run) => {
208
316
  const check = 'updateCounter is a compare-and-set';
209
- const record = credential({ counter: 3 });
317
+ const record = run.credential({ counter: 3 });
210
318
  await repo.createCredential(record);
211
319
  const update = (expectedCounter, newCounter) => repo.updateCounter({
212
320
  credentialId: record.credentialId,
@@ -223,7 +331,7 @@ const CHECKS = [
223
331
  fail(check, `${applied} of ${CONCURRENCY} concurrent updates from the same counter applied. It must be one ` +
224
332
  'conditional update (UPDATE … WHERE counter = expected), not a read then a write.');
225
333
  }
226
- const got = await repo.getCredential(record.credentialId);
334
+ const got = await repo.getCredential(record.credentialId, record.realm);
227
335
  if (!got || got.counter < 4)
228
336
  fail(check, `the counter is ${String(got?.counter)} after an applied update`);
229
337
  if (got.backedUp !== false)
@@ -234,11 +342,11 @@ const CHECKS = [
234
342
  ],
235
343
  [
236
344
  'renameCredential changes the name',
237
- async (repo) => {
238
- const record = credential();
345
+ async (repo, run) => {
346
+ const record = run.credential();
239
347
  await repo.createCredential(record);
240
348
  await repo.renameCredential(record.credentialId, 'Renamed');
241
- const got = await repo.getCredential(record.credentialId);
349
+ const got = await repo.getCredential(record.credentialId, record.realm);
242
350
  if (got?.name !== 'Renamed') {
243
351
  fail('renameCredential changes the name', `name is ${JSON.stringify(got?.name)}`);
244
352
  }
@@ -1 +1 @@
1
- {"version":3,"file":"passkey-repository.contract.js","sourceRoot":"","sources":["../../src/testing/passkey-repository.contract.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"passkey-repository.contract.js","sourceRoot":"","sources":["../../src/testing/passkey-repository.contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { Prisma, PrismaClient } from '@prisma/client';
3
+ import {
4
+ IPasskeyRepository,
5
+ PasskeyChallengeRecord,
6
+ PasskeyCredentialExistsError,
7
+ PasskeyCredentialRecord,
8
+ } from '@ambushsoftworks/nestjs-auth-graphql';
9
+
10
+ /**
11
+ * IPasskeyRepository on Prisma, for the models in ./schema.prisma.
12
+ *
13
+ * Verified against PostgreSQL 16 with `assertPasskeyRepositoryContract` from
14
+ * `@ambushsoftworks/nestjs-auth-graphql/testing`, with and without realms and
15
+ * with a foreign key to users. Run that contract against your copy, too:
16
+ *
17
+ * await assertPasskeyRepositoryContract(() => new PrismaPasskeyRepository(prisma), {
18
+ * userIds: [userA.id, userB.id],
19
+ * cleanup: async ({ credentialIds, challengeIds }) => {
20
+ * await prisma.passkeyCredential.deleteMany({ where: { credentialId: { in: credentialIds } } });
21
+ * await prisma.passkeyChallenge.deleteMany({ where: { id: { in: challengeIds } } });
22
+ * },
23
+ * });
24
+ *
25
+ * Inject your own PrismaService in place of PrismaClient if you have one.
26
+ */
27
+ @Injectable()
28
+ export class PrismaPasskeyRepository implements IPasskeyRepository {
29
+ constructor(private readonly prisma: PrismaClient) {}
30
+
31
+ async storeChallenge(input: Omit<PasskeyChallengeRecord, 'challengeId'>): Promise<string> {
32
+ const row = await this.prisma.passkeyChallenge.create({
33
+ data: {
34
+ challenge: input.challenge,
35
+ purpose: input.purpose,
36
+ userId: input.userId ?? null,
37
+ realm: input.realm ?? null,
38
+ userHandle: input.userHandle ?? null,
39
+ expiresAt: input.expiresAt,
40
+ },
41
+ select: { id: true },
42
+ });
43
+ return row.id;
44
+ }
45
+
46
+ /** One conditional update, then a read: never a read followed by a write. */
47
+ async consumeChallenge(challengeId: string): Promise<PasskeyChallengeRecord | null> {
48
+ const { count } = await this.prisma.passkeyChallenge.updateMany({
49
+ where: { id: challengeId, used: false, expiresAt: { gt: new Date() } },
50
+ data: { used: true, usedAt: new Date() },
51
+ });
52
+ if (count !== 1) return null;
53
+
54
+ const row = await this.prisma.passkeyChallenge.findUnique({ where: { id: challengeId } });
55
+ if (!row) return null;
56
+ return {
57
+ challengeId: row.id,
58
+ challenge: row.challenge,
59
+ purpose: row.purpose as PasskeyChallengeRecord['purpose'],
60
+ ...(row.userId !== null ? { userId: row.userId } : {}),
61
+ ...(row.realm !== null ? { realm: row.realm } : {}),
62
+ ...(row.userHandle !== null ? { userHandle: row.userHandle } : {}),
63
+ expiresAt: row.expiresAt,
64
+ };
65
+ }
66
+
67
+ async deleteExpiredChallenges(): Promise<number> {
68
+ const { count } = await this.prisma.passkeyChallenge.deleteMany({
69
+ where: { expiresAt: { lt: new Date() } },
70
+ });
71
+ return count;
72
+ }
73
+
74
+ /** The primary key is the uniqueness check; its violation becomes the package's error. */
75
+ async createCredential(record: PasskeyCredentialRecord): Promise<void> {
76
+ try {
77
+ await this.prisma.passkeyCredential.create({
78
+ data: {
79
+ credentialId: record.credentialId,
80
+ userId: record.userId,
81
+ realm: record.realm ?? null,
82
+ publicKey: record.publicKey,
83
+ counter: BigInt(record.counter),
84
+ userHandle: record.userHandle,
85
+ transports: record.transports ?? [],
86
+ deviceType: record.deviceType,
87
+ backedUp: record.backedUp,
88
+ aaguid: record.aaguid ?? null,
89
+ name: record.name,
90
+ createdAt: record.createdAt,
91
+ lastUsedAt: record.lastUsedAt,
92
+ isActive: record.isActive,
93
+ ...(record.metadata !== undefined ? { metadata: record.metadata as Prisma.InputJsonValue } : {}),
94
+ },
95
+ });
96
+ } catch (error: unknown) {
97
+ if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2002') {
98
+ throw new PasskeyCredentialExistsError(record.credentialId);
99
+ }
100
+ throw error;
101
+ }
102
+ }
103
+
104
+ /** Returns deactivated credentials too, and any realm: the package decides. */
105
+ async getCredential(credentialId: string): Promise<PasskeyCredentialRecord | null> {
106
+ const row = await this.prisma.passkeyCredential.findUnique({ where: { credentialId } });
107
+ return row ? toRecord(row) : null;
108
+ }
109
+
110
+ async listCredentials(userId: string, realm?: string): Promise<PasskeyCredentialRecord[]> {
111
+ const rows = await this.prisma.passkeyCredential.findMany({
112
+ where: { userId, ...(realm !== undefined ? { realm } : {}) },
113
+ orderBy: { createdAt: 'asc' },
114
+ });
115
+ return rows.map(toRecord);
116
+ }
117
+
118
+ /** Compare-and-set: applies only while the stored counter is still `expectedCounter`. */
119
+ async updateCounter(input: {
120
+ credentialId: string;
121
+ expectedCounter: number;
122
+ newCounter: number;
123
+ backedUp: boolean;
124
+ lastUsedAt: Date;
125
+ }): Promise<boolean> {
126
+ const { count } = await this.prisma.passkeyCredential.updateMany({
127
+ where: { credentialId: input.credentialId, counter: BigInt(input.expectedCounter) },
128
+ data: {
129
+ counter: BigInt(input.newCounter),
130
+ backedUp: input.backedUp,
131
+ lastUsedAt: input.lastUsedAt,
132
+ },
133
+ });
134
+ return count === 1;
135
+ }
136
+
137
+ async renameCredential(credentialId: string, name: string): Promise<void> {
138
+ await this.prisma.passkeyCredential.update({ where: { credentialId }, data: { name } });
139
+ }
140
+
141
+ async deactivateCredential(credentialId: string): Promise<void> {
142
+ await this.prisma.passkeyCredential.update({ where: { credentialId }, data: { isActive: false } });
143
+ }
144
+ }
145
+
146
+ type CredentialRow = Prisma.PasskeyCredentialGetPayload<Record<string, never>>;
147
+
148
+ function toRecord(row: CredentialRow): PasskeyCredentialRecord {
149
+ return {
150
+ credentialId: row.credentialId,
151
+ userId: row.userId,
152
+ ...(row.realm !== null ? { realm: row.realm } : {}),
153
+ publicKey: row.publicKey,
154
+ counter: Number(row.counter),
155
+ userHandle: row.userHandle,
156
+ transports: row.transports,
157
+ deviceType: row.deviceType as PasskeyCredentialRecord['deviceType'],
158
+ backedUp: row.backedUp,
159
+ ...(row.aaguid !== null ? { aaguid: row.aaguid } : {}),
160
+ name: row.name,
161
+ createdAt: row.createdAt,
162
+ lastUsedAt: row.lastUsedAt,
163
+ isActive: row.isActive,
164
+ ...(row.metadata !== null ? { metadata: row.metadata as Record<string, unknown> } : {}),
165
+ };
166
+ }
@@ -0,0 +1,46 @@
1
+ // Reference schema for IPasskeyRepository, as in the README ("Implementing
2
+ // IPasskeyRepository"). Verified against PostgreSQL 16 with
3
+ // `assertPasskeyRepositoryContract`, with and without realms.
4
+ //
5
+ // Merge these two models into your schema, and add the back-relation to your
6
+ // User model:
7
+ //
8
+ // model User {
9
+ // ...
10
+ // passkeyCredentials PasskeyCredential[]
11
+ // }
12
+
13
+ model PasskeyCredential {
14
+ credentialId String @id // base64url, unique across all users
15
+ userId String
16
+ realm String? // set when realms are enabled
17
+ publicKey String // base64url COSE key
18
+ counter BigInt @default(0) // uint32 on the wire; Int is too small
19
+ userHandle String
20
+ transports String[]
21
+ deviceType String
22
+ backedUp Boolean
23
+ aaguid String?
24
+ name String
25
+ createdAt DateTime @default(now())
26
+ lastUsedAt DateTime?
27
+ isActive Boolean @default(true)
28
+ metadata Json?
29
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
30
+
31
+ @@index([userId, realm])
32
+ }
33
+
34
+ model PasskeyChallenge {
35
+ id String @id @default(uuid())
36
+ challenge String
37
+ purpose String // registration | authentication | step-up
38
+ userId String? // not a foreign key: sign-in challenges have no user
39
+ realm String?
40
+ userHandle String? // registration only
41
+ expiresAt DateTime
42
+ used Boolean @default(false)
43
+ usedAt DateTime?
44
+
45
+ @@index([expiresAt])
46
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ambushsoftworks/nestjs-auth-graphql",
3
- "version": "0.16.0-rc.2",
3
+ "version": "0.16.0-rc.3",
4
4
  "description": "Production-grade authentication package for NestJS with GraphQL, supporting JWT, OAuth, email/SMS verification, and biometric auth",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",