@ambushsoftworks/nestjs-auth-graphql 0.17.1 → 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 (105) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/README.md +363 -1
  3. package/dist/auth.module.d.ts +16 -0
  4. package/dist/auth.module.d.ts.map +1 -1
  5. package/dist/auth.module.js +71 -3
  6. package/dist/auth.module.js.map +1 -1
  7. package/dist/constants.d.ts +2 -0
  8. package/dist/constants.d.ts.map +1 -1
  9. package/dist/constants.js +3 -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/email-challenge.exceptions.d.ts +8 -0
  20. package/dist/exceptions/email-challenge.exceptions.d.ts.map +1 -0
  21. package/dist/exceptions/email-challenge.exceptions.js +21 -0
  22. package/dist/exceptions/email-challenge.exceptions.js.map +1 -0
  23. package/dist/exceptions/guest-session.exceptions.d.ts +9 -0
  24. package/dist/exceptions/guest-session.exceptions.d.ts.map +1 -0
  25. package/dist/exceptions/guest-session.exceptions.js +20 -0
  26. package/dist/exceptions/guest-session.exceptions.js.map +1 -0
  27. package/dist/guards/create-auth-guard.d.ts.map +1 -1
  28. package/dist/guards/create-auth-guard.js +29 -1
  29. package/dist/guards/create-auth-guard.js.map +1 -1
  30. package/dist/guards/guest-session.guard.d.ts +14 -0
  31. package/dist/guards/guest-session.guard.d.ts.map +1 -0
  32. package/dist/guards/guest-session.guard.js +63 -0
  33. package/dist/guards/guest-session.guard.js.map +1 -0
  34. package/dist/index.d.ts +11 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +11 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/interfaces/auth-logger.interface.d.ts +10 -1
  39. package/dist/interfaces/auth-logger.interface.d.ts.map +1 -1
  40. package/dist/interfaces/auth-logger.interface.js +9 -0
  41. package/dist/interfaces/auth-logger.interface.js.map +1 -1
  42. package/dist/interfaces/email-challenge-repository.interface.d.ts +22 -0
  43. package/dist/interfaces/email-challenge-repository.interface.d.ts.map +1 -0
  44. package/dist/interfaces/email-challenge-repository.interface.js +3 -0
  45. package/dist/interfaces/email-challenge-repository.interface.js.map +1 -0
  46. package/dist/interfaces/email-service.interface.d.ts +5 -0
  47. package/dist/interfaces/email-service.interface.d.ts.map +1 -1
  48. package/dist/interfaces/email-template-renderer.interface.d.ts +7 -0
  49. package/dist/interfaces/email-template-renderer.interface.d.ts.map +1 -1
  50. package/dist/interfaces/guest-session-repository.interface.d.ts +37 -0
  51. package/dist/interfaces/guest-session-repository.interface.d.ts.map +1 -0
  52. package/dist/interfaces/guest-session-repository.interface.js +3 -0
  53. package/dist/interfaces/guest-session-repository.interface.js.map +1 -0
  54. package/dist/interfaces/index.d.ts +2 -0
  55. package/dist/interfaces/index.d.ts.map +1 -1
  56. package/dist/interfaces/index.js +2 -0
  57. package/dist/interfaces/index.js.map +1 -1
  58. package/dist/repositories/in-memory-email-challenge.repository.d.ts +14 -0
  59. package/dist/repositories/in-memory-email-challenge.repository.d.ts.map +1 -0
  60. package/dist/repositories/in-memory-email-challenge.repository.js +55 -0
  61. package/dist/repositories/in-memory-email-challenge.repository.js.map +1 -0
  62. package/dist/repositories/in-memory-guest-session.repository.d.ts +18 -0
  63. package/dist/repositories/in-memory-guest-session.repository.d.ts.map +1 -0
  64. package/dist/repositories/in-memory-guest-session.repository.js +83 -0
  65. package/dist/repositories/in-memory-guest-session.repository.js.map +1 -0
  66. package/dist/services/configurable-email.service.d.ts +2 -1
  67. package/dist/services/configurable-email.service.d.ts.map +1 -1
  68. package/dist/services/configurable-email.service.js +24 -0
  69. package/dist/services/configurable-email.service.js.map +1 -1
  70. package/dist/services/default-email-template-renderer.d.ts +7 -0
  71. package/dist/services/default-email-template-renderer.d.ts.map +1 -1
  72. package/dist/services/default-email-template-renderer.js +31 -0
  73. package/dist/services/default-email-template-renderer.js.map +1 -1
  74. package/dist/services/email-challenge.service.d.ts +74 -0
  75. package/dist/services/email-challenge.service.d.ts.map +1 -0
  76. package/dist/services/email-challenge.service.js +306 -0
  77. package/dist/services/email-challenge.service.js.map +1 -0
  78. package/dist/services/guest-session.service.d.ts +80 -0
  79. package/dist/services/guest-session.service.d.ts.map +1 -0
  80. package/dist/services/guest-session.service.js +293 -0
  81. package/dist/services/guest-session.service.js.map +1 -0
  82. package/dist/services/passkey.service.d.ts.map +1 -1
  83. package/dist/services/passkey.service.js +8 -20
  84. package/dist/services/passkey.service.js.map +1 -1
  85. package/dist/testing/email-challenge-repository.contract.d.ts +15 -0
  86. package/dist/testing/email-challenge-repository.contract.d.ts.map +1 -0
  87. package/dist/testing/email-challenge-repository.contract.js +287 -0
  88. package/dist/testing/email-challenge-repository.contract.js.map +1 -0
  89. package/dist/testing/guest-session-repository.contract.d.ts +18 -0
  90. package/dist/testing/guest-session-repository.contract.d.ts.map +1 -0
  91. package/dist/testing/guest-session-repository.contract.js +368 -0
  92. package/dist/testing/guest-session-repository.contract.js.map +1 -0
  93. package/dist/testing/index.d.ts +2 -0
  94. package/dist/testing/index.d.ts.map +1 -1
  95. package/dist/testing/index.js +7 -1
  96. package/dist/testing/index.js.map +1 -1
  97. package/dist/utils/rate-limit-helpers.d.ts +4 -0
  98. package/dist/utils/rate-limit-helpers.d.ts.map +1 -0
  99. package/dist/utils/rate-limit-helpers.js +20 -0
  100. package/dist/utils/rate-limit-helpers.js.map +1 -0
  101. package/examples/email-challenge/prisma-email-challenge.repository.ts +85 -0
  102. package/examples/email-challenge/schema.prisma +24 -0
  103. package/examples/guest-sessions/prisma-guest-session.repository.ts +86 -0
  104. package/examples/guest-sessions/schema.prisma +33 -0
  105. package/package.json +1 -1
@@ -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.17.1",
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",