@ambushsoftworks/nestjs-auth-graphql 0.18.0 → 0.19.0

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +190 -1
  3. package/dist/auth.module.d.ts +6 -0
  4. package/dist/auth.module.d.ts.map +1 -1
  5. package/dist/auth.module.js +30 -0
  6. package/dist/auth.module.js.map +1 -1
  7. package/dist/constants.d.ts +1 -0
  8. package/dist/constants.d.ts.map +1 -1
  9. package/dist/constants.js +2 -1
  10. package/dist/constants.js.map +1 -1
  11. package/dist/decorators/allow-guest-session.decorator.d.ts +3 -0
  12. package/dist/decorators/allow-guest-session.decorator.d.ts.map +1 -0
  13. package/dist/decorators/allow-guest-session.decorator.js +8 -0
  14. package/dist/decorators/allow-guest-session.decorator.js.map +1 -0
  15. package/dist/decorators/current-guest.decorator.d.ts +2 -0
  16. package/dist/decorators/current-guest.decorator.d.ts.map +1 -0
  17. package/dist/decorators/current-guest.decorator.js +7 -0
  18. package/dist/decorators/current-guest.decorator.js.map +1 -0
  19. package/dist/exceptions/guest-session.exceptions.d.ts +9 -0
  20. package/dist/exceptions/guest-session.exceptions.d.ts.map +1 -0
  21. package/dist/exceptions/guest-session.exceptions.js +20 -0
  22. package/dist/exceptions/guest-session.exceptions.js.map +1 -0
  23. package/dist/guards/create-auth-guard.d.ts.map +1 -1
  24. package/dist/guards/create-auth-guard.js +29 -1
  25. package/dist/guards/create-auth-guard.js.map +1 -1
  26. package/dist/guards/guest-session.guard.d.ts +14 -0
  27. package/dist/guards/guest-session.guard.d.ts.map +1 -0
  28. package/dist/guards/guest-session.guard.js +63 -0
  29. package/dist/guards/guest-session.guard.js.map +1 -0
  30. package/dist/index.d.ts +7 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +7 -0
  33. package/dist/index.js.map +1 -1
  34. package/dist/interfaces/auth-logger.interface.d.ts +6 -1
  35. package/dist/interfaces/auth-logger.interface.d.ts.map +1 -1
  36. package/dist/interfaces/auth-logger.interface.js +5 -0
  37. package/dist/interfaces/auth-logger.interface.js.map +1 -1
  38. package/dist/interfaces/guest-session-repository.interface.d.ts +37 -0
  39. package/dist/interfaces/guest-session-repository.interface.d.ts.map +1 -0
  40. package/dist/interfaces/guest-session-repository.interface.js +3 -0
  41. package/dist/interfaces/guest-session-repository.interface.js.map +1 -0
  42. package/dist/interfaces/index.d.ts +1 -0
  43. package/dist/interfaces/index.d.ts.map +1 -1
  44. package/dist/interfaces/index.js +1 -0
  45. package/dist/interfaces/index.js.map +1 -1
  46. package/dist/repositories/in-memory-guest-session.repository.d.ts +18 -0
  47. package/dist/repositories/in-memory-guest-session.repository.d.ts.map +1 -0
  48. package/dist/repositories/in-memory-guest-session.repository.js +83 -0
  49. package/dist/repositories/in-memory-guest-session.repository.js.map +1 -0
  50. package/dist/services/guest-session.service.d.ts +80 -0
  51. package/dist/services/guest-session.service.d.ts.map +1 -0
  52. package/dist/services/guest-session.service.js +293 -0
  53. package/dist/services/guest-session.service.js.map +1 -0
  54. package/dist/testing/guest-session-repository.contract.d.ts +18 -0
  55. package/dist/testing/guest-session-repository.contract.d.ts.map +1 -0
  56. package/dist/testing/guest-session-repository.contract.js +368 -0
  57. package/dist/testing/guest-session-repository.contract.js.map +1 -0
  58. package/dist/testing/index.d.ts +1 -0
  59. package/dist/testing/index.d.ts.map +1 -1
  60. package/dist/testing/index.js +4 -1
  61. package/dist/testing/index.js.map +1 -1
  62. package/examples/guest-sessions/prisma-guest-session.repository.ts +86 -0
  63. package/examples/guest-sessions/schema.prisma +33 -0
  64. package/package.json +1 -1
@@ -0,0 +1,368 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GuestSessionRepositoryContractError = void 0;
4
+ exports.assertGuestSessionRepositoryContract = assertGuestSessionRepositoryContract;
5
+ const crypto_1 = require("crypto");
6
+ class GuestSessionRepositoryContractError extends Error {
7
+ constructor(check, detail, options) {
8
+ super(`IGuestSessionRepository contract — ${check}: ${detail}`);
9
+ this.name = 'GuestSessionRepositoryContractError';
10
+ if (options?.cause !== undefined) {
11
+ this.cause = options.cause;
12
+ }
13
+ }
14
+ }
15
+ exports.GuestSessionRepositoryContractError = GuestSessionRepositoryContractError;
16
+ const CONCURRENCY = 8;
17
+ async function assertGuestSessionRepositoryContract(factory, options = {}) {
18
+ if (options.realms !== undefined && typeof options.realms !== 'boolean') {
19
+ throw new TypeError(`assertGuestSessionRepositoryContract: realms must be a boolean; received ${String(options.realms)}`);
20
+ }
21
+ if (options.cleanup !== undefined && typeof options.cleanup !== 'function') {
22
+ throw new TypeError('assertGuestSessionRepositoryContract: cleanup must be a function');
23
+ }
24
+ const run = new ContractRun(options.realms ?? true);
25
+ let failure;
26
+ try {
27
+ for (const [name, check] of CHECKS) {
28
+ try {
29
+ const repository = run.track(await factory());
30
+ await check(repository, run, name);
31
+ }
32
+ catch (error) {
33
+ if (error instanceof GuestSessionRepositoryContractError)
34
+ throw error;
35
+ throw new GuestSessionRepositoryContractError(name, `threw ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`);
36
+ }
37
+ }
38
+ }
39
+ catch (error) {
40
+ failure = error;
41
+ }
42
+ if (options.cleanup) {
43
+ try {
44
+ await options.cleanup(run.created());
45
+ }
46
+ catch (cleanupError) {
47
+ if (failure instanceof GuestSessionRepositoryContractError) {
48
+ throw new GuestSessionRepositoryContractError('cleanup', `${failure.message} — and cleanup then threw: ${cleanupError instanceof Error ? cleanupError.message : String(cleanupError)}`, { cause: cleanupError });
49
+ }
50
+ if (failure === undefined)
51
+ throw cleanupError;
52
+ }
53
+ }
54
+ if (failure !== undefined)
55
+ throw failure;
56
+ }
57
+ class ContractRun {
58
+ constructor(realms) {
59
+ this.realms = realms;
60
+ this.sessionIds = new Set();
61
+ this.grantIds = new Set();
62
+ }
63
+ created() {
64
+ return { sessionIds: [...this.sessionIds], grantIds: [...this.grantIds] };
65
+ }
66
+ track(repo) {
67
+ return new Proxy(repo, {
68
+ get: (target, property, receiver) => {
69
+ const value = Reflect.get(target, property, receiver);
70
+ if (property === 'createSession') {
71
+ return async (session, grant) => {
72
+ this.sessionIds.add(session.id);
73
+ this.grantIds.add(grant.id);
74
+ return value.call(target, session, grant);
75
+ };
76
+ }
77
+ if (property === 'addGrant') {
78
+ return async (grant) => {
79
+ this.grantIds.add(grant.id);
80
+ return value.call(target, grant);
81
+ };
82
+ }
83
+ return typeof value === 'function' ? value.bind(target) : value;
84
+ },
85
+ });
86
+ }
87
+ realm(realm) {
88
+ return this.realms ? realm : null;
89
+ }
90
+ session(overrides = {}) {
91
+ return {
92
+ id: (0, crypto_1.randomUUID)(),
93
+ tokenHash: hash(),
94
+ principalId: `contract-principal-${(0, crypto_1.randomUUID)()}`,
95
+ email: `contract-${(0, crypto_1.randomUUID)()}@example.com`,
96
+ realm: this.realm('contract-realm'),
97
+ revokedAt: null,
98
+ createdAt: wholeSecond(),
99
+ ...overrides,
100
+ };
101
+ }
102
+ grant(session, overrides = {}) {
103
+ return {
104
+ id: (0, crypto_1.randomUUID)(),
105
+ sessionId: session.id,
106
+ kind: 'contract-kind',
107
+ grantId: `contract-grant-${(0, crypto_1.randomUUID)()}`,
108
+ realm: session.realm,
109
+ expiresAt: new Date(wholeSecond().getTime() + 600_000),
110
+ revokedAt: null,
111
+ createdAt: wholeSecond(),
112
+ ...overrides,
113
+ };
114
+ }
115
+ async stored(repo, sessionOverrides = {}, grantOverrides = {}) {
116
+ const session = this.session(sessionOverrides);
117
+ const grant = this.grant(session, grantOverrides);
118
+ await repo.createSession(session, grant);
119
+ return { session, grant };
120
+ }
121
+ }
122
+ const hash = () => (0, crypto_1.randomBytes)(32).toString('hex');
123
+ const wholeSecond = () => new Date(Math.floor(Date.now() / 1000) * 1000);
124
+ function fail(check, detail) {
125
+ throw new GuestSessionRepositoryContractError(check, detail);
126
+ }
127
+ const sameTime = (a, b) => a === b || (a instanceof Date && b instanceof Date && Math.abs(a.getTime() - b.getTime()) < 1000);
128
+ const CHECKS = [
129
+ [
130
+ 'a session and its grants round-trip every field',
131
+ async (repo, run, check) => {
132
+ const { session, grant } = await run.stored(repo);
133
+ const second = run.grant(session, { kind: 'contract-other-kind' });
134
+ await repo.addGrant(second);
135
+ const got = await repo.findSessionByTokenHash(session.tokenHash);
136
+ if (!got)
137
+ fail(check, 'findSessionByTokenHash returned null for a session just created');
138
+ for (const key of ['id', 'tokenHash', 'principalId', 'email', 'realm', 'revokedAt']) {
139
+ if (got[key] !== session[key]) {
140
+ fail(check, `session ${key} came back as ${JSON.stringify(got[key])}, stored ${JSON.stringify(session[key])}`);
141
+ }
142
+ }
143
+ if (!(got.createdAt instanceof Date) || !sameTime(got.createdAt, session.createdAt)) {
144
+ fail(check, 'session createdAt changed, or is not a Date');
145
+ }
146
+ if (!Array.isArray(got.grants) || got.grants.length !== 2) {
147
+ fail(check, `the session came back with ${JSON.stringify(got.grants?.length)} grants; it has 2`);
148
+ }
149
+ for (const stored of [grant, second]) {
150
+ const back = got.grants.find((g) => g.id === stored.id);
151
+ if (!back)
152
+ fail(check, `grant ${stored.kind} is missing from the session`);
153
+ for (const key of ['sessionId', 'kind', 'grantId', 'realm', 'revokedAt']) {
154
+ if (back[key] !== stored[key]) {
155
+ fail(check, `grant ${key} came back as ${JSON.stringify(back[key])}, stored ${JSON.stringify(stored[key])}`);
156
+ }
157
+ }
158
+ if (!(back.expiresAt instanceof Date) || !sameTime(back.expiresAt, stored.expiresAt)) {
159
+ fail(check, 'grant expiresAt changed, or is not a Date');
160
+ }
161
+ if (!(back.createdAt instanceof Date) || !sameTime(back.createdAt, stored.createdAt)) {
162
+ fail(check, 'grant createdAt changed, or is not a Date');
163
+ }
164
+ }
165
+ },
166
+ ],
167
+ [
168
+ 'a session without a realm stores null',
169
+ async (repo, run, check) => {
170
+ const { session } = await run.stored(repo, { realm: null });
171
+ const got = await repo.findSessionByTokenHash(session.tokenHash);
172
+ if (!got)
173
+ fail(check, 'findSessionByTokenHash returned null');
174
+ if (got.realm !== null || got.grants[0]?.realm !== null) {
175
+ fail(check, `realm came back as ${JSON.stringify(got.realm)} on the session and ` +
176
+ `${JSON.stringify(got.grants[0]?.realm)} on its grant; both must be null, not undefined or ''`);
177
+ }
178
+ },
179
+ ],
180
+ [
181
+ 'an unknown token or id answers null or false',
182
+ async (repo, _run, check) => {
183
+ const unknown = `unknown-${(0, crypto_1.randomUUID)()}`;
184
+ if ((await repo.findSessionByTokenHash(hash())) !== null)
185
+ fail(check, 'findSessionByTokenHash did not return null');
186
+ if ((await repo.rotateToken(unknown, hash(), hash())) !== false)
187
+ fail(check, 'rotateToken did not return false');
188
+ if ((await repo.revokeSession(unknown)) !== false)
189
+ fail(check, 'revokeSession did not return false');
190
+ if ((await repo.revokeGrant(unknown)) !== false)
191
+ fail(check, 'revokeGrant did not return false');
192
+ },
193
+ ],
194
+ [
195
+ 'revoked sessions, and revoked and expired grants, are still returned',
196
+ async (repo, run, check) => {
197
+ const { session, grant } = await run.stored(repo);
198
+ const expired = run.grant(session, { expiresAt: new Date(Date.now() - 60_000) });
199
+ await repo.addGrant(expired);
200
+ await repo.revokeGrant(grant.id);
201
+ await repo.revokeSession(session.id);
202
+ const got = await repo.findSessionByTokenHash(session.tokenHash);
203
+ if (!got)
204
+ fail(check, 'findSessionByTokenHash hid a revoked session. Return it: the package decides.');
205
+ if (!(got.revokedAt instanceof Date))
206
+ fail(check, 'revokedAt is not a Date after revokeSession');
207
+ const revoked = got.grants.find((g) => g.id === grant.id);
208
+ if (!revoked)
209
+ fail(check, 'a revoked grant is missing from its session');
210
+ if (!(revoked.revokedAt instanceof Date))
211
+ fail(check, "a revoked grant's revokedAt is not a Date");
212
+ if (!got.grants.some((g) => g.id === expired.id))
213
+ fail(check, 'an expired grant is missing from its session');
214
+ },
215
+ ],
216
+ [
217
+ 'rotateToken replaces the hash only from the current one',
218
+ async (repo, run, check) => {
219
+ const { session } = await run.stored(repo);
220
+ const next = hash();
221
+ if ((await repo.rotateToken(session.id, hash(), next)) !== false) {
222
+ fail(check, 'it rotated from a hash that is not the current one');
223
+ }
224
+ if ((await repo.rotateToken(session.id, session.tokenHash, next)) !== true) {
225
+ fail(check, 'it refused to rotate from the current hash');
226
+ }
227
+ if ((await repo.findSessionByTokenHash(session.tokenHash)) !== null) {
228
+ fail(check, 'the old token hash still finds the session after a rotation');
229
+ }
230
+ if ((await repo.findSessionByTokenHash(next))?.id !== session.id) {
231
+ fail(check, 'the new token hash does not find the session');
232
+ }
233
+ if ((await repo.rotateToken(session.id, session.tokenHash, hash())) !== false) {
234
+ fail(check, 'it rotated again from the hash it had just replaced');
235
+ }
236
+ },
237
+ ],
238
+ [
239
+ 'rotateToken refuses a revoked session',
240
+ async (repo, run, check) => {
241
+ const { session } = await run.stored(repo);
242
+ await repo.revokeSession(session.id);
243
+ if ((await repo.rotateToken(session.id, session.tokenHash, hash())) !== false) {
244
+ fail(check, 'it rotated the token of a revoked session');
245
+ }
246
+ },
247
+ ],
248
+ [
249
+ 'rotateToken applies exactly once, under concurrency',
250
+ async (repo, run, check) => {
251
+ const { session } = await run.stored(repo);
252
+ const candidates = Array.from({ length: CONCURRENCY }, hash);
253
+ const results = await Promise.all(candidates.map((next) => repo.rotateToken(session.id, session.tokenHash, next)));
254
+ const applied = results.filter((r) => r === true).length;
255
+ if (applied !== 1) {
256
+ fail(check, `${applied} of ${CONCURRENCY} concurrent rotations from the same hash applied; exactly one must. ` +
257
+ 'It must be one conditional update (UPDATE … SET tokenHash = new WHERE tokenHash = old), ' +
258
+ 'not a read then a write.');
259
+ }
260
+ const winner = candidates[results.indexOf(true)];
261
+ if ((await repo.findSessionByTokenHash(winner))?.id !== session.id) {
262
+ fail(check, 'the rotation that reported success is not the hash the session ended up with');
263
+ }
264
+ },
265
+ ],
266
+ [
267
+ 'revoking a session or a grant happens once',
268
+ async (repo, run, check) => {
269
+ const { session, grant } = await run.stored(repo);
270
+ if ((await repo.revokeGrant(grant.id)) !== true)
271
+ fail(check, 'revokeGrant refused a standing grant');
272
+ const grantRevokedAt = (await repo.findSessionByTokenHash(session.tokenHash))?.grants[0]?.revokedAt;
273
+ if ((await repo.revokeGrant(grant.id)) !== false)
274
+ fail(check, 'revokeGrant revoked a grant twice');
275
+ if ((await repo.revokeSession(session.id)) !== true)
276
+ fail(check, 'revokeSession refused a standing session');
277
+ const sessionRevokedAt = (await repo.findSessionByTokenHash(session.tokenHash))?.revokedAt;
278
+ if ((await repo.revokeSession(session.id)) !== false)
279
+ fail(check, 'revokeSession revoked a session twice');
280
+ const got = await repo.findSessionByTokenHash(session.tokenHash);
281
+ if (!sameTime(got?.revokedAt, sessionRevokedAt) || !(got?.revokedAt instanceof Date)) {
282
+ fail(check, "the session's revokedAt changed on the second revoke");
283
+ }
284
+ if (!sameTime(got.grants[0]?.revokedAt, grantRevokedAt) || !(got.grants[0]?.revokedAt instanceof Date)) {
285
+ fail(check, "the grant's revokedAt changed on the second revoke");
286
+ }
287
+ },
288
+ ],
289
+ [
290
+ 'revokeGrants is by kind, id and realm, across sessions',
291
+ async (repo, run, check) => {
292
+ const realm = run.realm('contract-realm');
293
+ const grantId = `contract-job-${(0, crypto_1.randomUUID)()}`;
294
+ const one = await run.stored(repo, {}, { grantId });
295
+ const two = await run.stored(repo, {}, { grantId });
296
+ const otherId = await run.stored(repo);
297
+ const otherKind = await run.stored(repo, {}, { grantId, kind: 'contract-other-kind' });
298
+ const standing = async (stored) => (await repo.findSessionByTokenHash(stored.session.tokenHash))?.grants.find((g) => g.id === stored.grant.id)?.revokedAt === null;
299
+ const count = await repo.revokeGrants({ kind: 'contract-kind', grantId, realm });
300
+ if (await standing(one))
301
+ fail(check, 'a matching grant is still standing');
302
+ if (await standing(two))
303
+ fail(check, "a matching grant in another session is still standing");
304
+ if (!(await standing(otherId)))
305
+ fail(check, 'it revoked a grant with another id');
306
+ if (!(await standing(otherKind)))
307
+ fail(check, 'it revoked a grant of another kind with the same id');
308
+ if (count !== 2)
309
+ fail(check, `it reported ${JSON.stringify(count)} revoked; 2 matched`);
310
+ const again = await repo.revokeGrants({ kind: 'contract-kind', grantId, realm });
311
+ if (again !== 0)
312
+ fail(check, `a second call reported ${JSON.stringify(again)} revoked; none was standing`);
313
+ },
314
+ ],
315
+ [
316
+ 'revokeGrants keeps realms apart, and null means no realm',
317
+ async (repo, run, check) => {
318
+ if (!run.realms)
319
+ return;
320
+ const grantId = `contract-job-${(0, crypto_1.randomUUID)()}`;
321
+ const inA = await run.stored(repo, { realm: 'contract-realm-a' }, { grantId });
322
+ const inB = await run.stored(repo, { realm: 'contract-realm-b' }, { grantId });
323
+ const inNone = await run.stored(repo, { realm: null }, { grantId });
324
+ const standing = async (stored) => (await repo.findSessionByTokenHash(stored.session.tokenHash))?.grants[0]?.revokedAt === null;
325
+ const none = await repo.revokeGrants({ kind: 'contract-kind', grantId, realm: null });
326
+ if (!(await standing(inA)) || !(await standing(inB))) {
327
+ fail(check, 'realm: null revoked a grant that has a realm. null means "no realm", not "any realm": ' +
328
+ 'do not let it become `where: { realm: undefined }`.');
329
+ }
330
+ if (await standing(inNone))
331
+ fail(check, 'realm: null left the grant without a realm standing');
332
+ if (none !== 1)
333
+ fail(check, `realm: null reported ${JSON.stringify(none)} revoked; 1 matched`);
334
+ const a = await repo.revokeGrants({ kind: 'contract-kind', grantId, realm: 'contract-realm-a' });
335
+ if (await standing(inA))
336
+ fail(check, "a realm's own grant is still standing");
337
+ if (!(await standing(inB)))
338
+ fail(check, "revoking in one realm revoked another realm's grant");
339
+ if (a !== 1)
340
+ fail(check, `one realm reported ${JSON.stringify(a)} revoked; 1 matched`);
341
+ },
342
+ ],
343
+ [
344
+ 'deleteExpired removes expired grants and emptied sessions, and keeps the rest',
345
+ async (repo, run, check) => {
346
+ const past = new Date(Date.now() - 60_000);
347
+ const dead = await run.stored(repo, {}, { expiresAt: past });
348
+ const mixed = await run.stored(repo);
349
+ const mixedExpired = run.grant(mixed.session, { expiresAt: past });
350
+ await repo.addGrant(mixedExpired);
351
+ const deleted = await repo.deleteExpired();
352
+ if (typeof deleted !== 'number' || deleted < 2) {
353
+ fail(check, `deleteExpired returned ${JSON.stringify(deleted)}; at least two grants had expired`);
354
+ }
355
+ if ((await repo.findSessionByTokenHash(dead.session.tokenHash)) !== null) {
356
+ fail(check, 'a session whose only grant had expired survived');
357
+ }
358
+ const kept = await repo.findSessionByTokenHash(mixed.session.tokenHash);
359
+ if (!kept)
360
+ fail(check, 'a session with a live grant was deleted');
361
+ if (kept.grants.some((g) => g.id === mixedExpired.id))
362
+ fail(check, 'an expired grant survived');
363
+ if (!kept.grants.some((g) => g.id === mixed.grant.id))
364
+ fail(check, 'a live grant was deleted');
365
+ },
366
+ ],
367
+ ];
368
+ //# sourceMappingURL=guest-session-repository.contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guest-session-repository.contract.js","sourceRoot":"","sources":["../../src/testing/guest-session-repository.contract.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  export { assertPasskeyRepositoryContract, PasskeyRepositoryContractError, type PasskeyRepositoryContractOptions, type PasskeyContractCreatedIds, } from './passkey-repository.contract';
2
2
  export { assertEmailChallengeRepositoryContract, EmailChallengeRepositoryContractError, type EmailChallengeRepositoryContractOptions, } from './email-challenge-repository.contract';
3
+ export { assertGuestSessionRepositoryContract, GuestSessionRepositoryContractError, type GuestSessionRepositoryContractOptions, type GuestSessionContractCreatedIds, } from './guest-session-repository.contract';
3
4
  export { SoftwareAuthenticator, type CreateOptions as SoftwareAuthenticatorCreateOptions, type GetOptions as SoftwareAuthenticatorGetOptions, type SoftwareAlgorithm, } from '../test-utils/software-authenticator';
4
5
  //# 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,EAC9B,KAAK,gCAAgC,EACrC,KAAK,yBAAyB,GAC/B,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,sCAAsC,EACtC,qCAAqC,EACrC,KAAK,uCAAuC,GAC7C,MAAM,uCAAuC,CAAC;AAC/C,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,sCAAsC,EACtC,qCAAqC,EACrC,KAAK,uCAAuC,GAC7C,MAAM,uCAAuC,CAAC;AAC/C,OAAO,EACL,oCAAoC,EACpC,mCAAmC,EACnC,KAAK,qCAAqC,EAC1C,KAAK,8BAA8B,GACpC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,qBAAqB,EACrB,KAAK,aAAa,IAAI,kCAAkC,EACxD,KAAK,UAAU,IAAI,+BAA+B,EAClD,KAAK,iBAAiB,GACvB,MAAM,sCAAsC,CAAC"}
@@ -1,12 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SoftwareAuthenticator = exports.EmailChallengeRepositoryContractError = exports.assertEmailChallengeRepositoryContract = exports.PasskeyRepositoryContractError = exports.assertPasskeyRepositoryContract = void 0;
3
+ exports.SoftwareAuthenticator = exports.GuestSessionRepositoryContractError = exports.assertGuestSessionRepositoryContract = exports.EmailChallengeRepositoryContractError = exports.assertEmailChallengeRepositoryContract = exports.PasskeyRepositoryContractError = exports.assertPasskeyRepositoryContract = void 0;
4
4
  var passkey_repository_contract_1 = require("./passkey-repository.contract");
5
5
  Object.defineProperty(exports, "assertPasskeyRepositoryContract", { enumerable: true, get: function () { return passkey_repository_contract_1.assertPasskeyRepositoryContract; } });
6
6
  Object.defineProperty(exports, "PasskeyRepositoryContractError", { enumerable: true, get: function () { return passkey_repository_contract_1.PasskeyRepositoryContractError; } });
7
7
  var email_challenge_repository_contract_1 = require("./email-challenge-repository.contract");
8
8
  Object.defineProperty(exports, "assertEmailChallengeRepositoryContract", { enumerable: true, get: function () { return email_challenge_repository_contract_1.assertEmailChallengeRepositoryContract; } });
9
9
  Object.defineProperty(exports, "EmailChallengeRepositoryContractError", { enumerable: true, get: function () { return email_challenge_repository_contract_1.EmailChallengeRepositoryContractError; } });
10
+ var guest_session_repository_contract_1 = require("./guest-session-repository.contract");
11
+ Object.defineProperty(exports, "assertGuestSessionRepositoryContract", { enumerable: true, get: function () { return guest_session_repository_contract_1.assertGuestSessionRepositoryContract; } });
12
+ Object.defineProperty(exports, "GuestSessionRepositoryContractError", { enumerable: true, get: function () { return guest_session_repository_contract_1.GuestSessionRepositoryContractError; } });
10
13
  var software_authenticator_1 = require("../test-utils/software-authenticator");
11
14
  Object.defineProperty(exports, "SoftwareAuthenticator", { enumerable: true, get: function () { return software_authenticator_1.SoftwareAuthenticator; } });
12
15
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
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,6FAI+C;AAH7C,6JAAA,sCAAsC,OAAA;AACtC,4JAAA,qCAAqC,OAAA;AAGvC,+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,6FAI+C;AAH7C,6JAAA,sCAAsC,OAAA;AACtC,4JAAA,qCAAqC,OAAA;AAGvC,yFAK6C;AAJ3C,yJAAA,oCAAoC,OAAA;AACpC,wJAAA,mCAAmC,OAAA;AAIrC,+EAK8C;AAJ5C,+HAAA,qBAAqB,OAAA"}
@@ -0,0 +1,86 @@
1
+ import { Injectable } from '@nestjs/common';
2
+ import { PrismaClient } from '@prisma/client';
3
+ import {
4
+ GuestGrantRecord,
5
+ GuestSessionRecord,
6
+ GuestSessionWithGrants,
7
+ IGuestSessionRepository,
8
+ } from '@ambushsoftworks/nestjs-auth-graphql';
9
+
10
+ /**
11
+ * IGuestSessionRepository on Prisma, for the models in ./schema.prisma.
12
+ *
13
+ * Verified against PostgreSQL 16 with `assertGuestSessionRepositoryContract`
14
+ * from `@ambushsoftworks/nestjs-auth-graphql/testing`, with and without
15
+ * realms. Run that contract against your copy, too, on your real database:
16
+ *
17
+ * await assertGuestSessionRepositoryContract(() => new PrismaGuestSessionRepository(prisma), {
18
+ * cleanup: async ({ sessionIds, grantIds }) => {
19
+ * await prisma.guestGrant.deleteMany({ where: { id: { in: grantIds } } });
20
+ * await prisma.guestSession.deleteMany({ where: { id: { in: sessionIds } } });
21
+ * },
22
+ * });
23
+ *
24
+ * Inject your own PrismaService in place of PrismaClient if you have one.
25
+ */
26
+ @Injectable()
27
+ export class PrismaGuestSessionRepository implements IGuestSessionRepository {
28
+ constructor(private readonly prisma: PrismaClient) {}
29
+
30
+ async createSession(session: GuestSessionRecord, grant: GuestGrantRecord): Promise<void> {
31
+ const { sessionId: _sessionId, ...firstGrant } = grant;
32
+ await this.prisma.guestSession.create({
33
+ data: { ...session, grants: { create: firstGrant } },
34
+ });
35
+ }
36
+
37
+ /** Every grant, whatever its state: the package decides what revoked and expired mean. */
38
+ async findSessionByTokenHash(tokenHash: string): Promise<GuestSessionWithGrants | null> {
39
+ return this.prisma.guestSession.findUnique({ where: { tokenHash }, include: { grants: true } });
40
+ }
41
+
42
+ async addGrant(grant: GuestGrantRecord): Promise<void> {
43
+ await this.prisma.guestGrant.create({ data: grant });
44
+ }
45
+
46
+ /** One conditional update: never a read followed by a write. */
47
+ async rotateToken(sessionId: string, oldHash: string, newHash: string): Promise<boolean> {
48
+ const { count } = await this.prisma.guestSession.updateMany({
49
+ where: { id: sessionId, tokenHash: oldHash, revokedAt: null },
50
+ data: { tokenHash: newHash },
51
+ });
52
+ return count === 1;
53
+ }
54
+
55
+ async revokeSession(sessionId: string): Promise<boolean> {
56
+ const { count } = await this.prisma.guestSession.updateMany({
57
+ where: { id: sessionId, revokedAt: null },
58
+ data: { revokedAt: new Date() },
59
+ });
60
+ return count === 1;
61
+ }
62
+
63
+ async revokeGrant(grantRecordId: string): Promise<boolean> {
64
+ const { count } = await this.prisma.guestGrant.updateMany({
65
+ where: { id: grantRecordId, revokedAt: null },
66
+ data: { revokedAt: new Date() },
67
+ });
68
+ return count === 1;
69
+ }
70
+
71
+ async revokeGrants(filter: { kind: string; grantId: string; realm: string | null }): Promise<number> {
72
+ const { count } = await this.prisma.guestGrant.updateMany({
73
+ // `realm` goes through as given. null means "no realm" here; turning it
74
+ // into undefined would drop the condition and match every realm.
75
+ where: { kind: filter.kind, grantId: filter.grantId, realm: filter.realm, revokedAt: null },
76
+ data: { revokedAt: new Date() },
77
+ });
78
+ return count;
79
+ }
80
+
81
+ async deleteExpired(): Promise<number> {
82
+ const grants = await this.prisma.guestGrant.deleteMany({ where: { expiresAt: { lt: new Date() } } });
83
+ const sessions = await this.prisma.guestSession.deleteMany({ where: { grants: { none: {} } } });
84
+ return grants.count + sessions.count;
85
+ }
86
+ }
@@ -0,0 +1,33 @@
1
+ // Reference schema for IGuestSessionRepository, as in the README ("Guest
2
+ // Sessions"). Verified against PostgreSQL 16 with
3
+ // `assertGuestSessionRepositoryContract`, with and without realms.
4
+ //
5
+ // Merge these two models into your schema. Neither relates to your User model,
6
+ // on purpose: a guest is not a user.
7
+
8
+ model GuestSession {
9
+ id String @id // generated by the package
10
+ tokenHash String @unique // looked up on every guest request
11
+ principalId String // your own id for the person
12
+ email String
13
+ realm String? // set when realms are enabled
14
+ revokedAt DateTime?
15
+ createdAt DateTime
16
+ grants GuestGrant[]
17
+ }
18
+
19
+ model GuestGrant {
20
+ id String @id // generated by the package
21
+ sessionId String
22
+ kind String // what routes opt in by: 'translation-job'
23
+ grantId String // your id for the thing granted
24
+ realm String? // copied from the session
25
+ expiresAt DateTime
26
+ revokedAt DateTime?
27
+ createdAt DateTime
28
+ session GuestSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
29
+
30
+ @@index([sessionId])
31
+ @@index([kind, grantId])
32
+ @@index([expiresAt])
33
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ambushsoftworks/nestjs-auth-graphql",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
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",