@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
package/CHANGELOG.md CHANGED
@@ -27,6 +27,105 @@ 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.19.0] - 2026-10-08
31
+
32
+ **Guest sessions**: a session for someone who is not a user, which works only on
33
+ routes that opt in, for the things you granted, until the dates you set.
34
+ Additive; nothing changes for an app that does not use it. Requested by Ariadne,
35
+ for outside translators working on one job.
36
+
37
+ ### ⚠ External configuration required
38
+
39
+ Only to use guest sessions:
40
+ - **Two tables for `IGuestSessionRepository`**, with no foreign key to users.
41
+ Schema and Prisma adapter in `examples/guest-sessions/`. Run
42
+ `assertGuestSessionRepositoryContract` against it on your real database.
43
+ - **Your frontend handles `GUEST_SESSION_INVALID`** by sending the person to
44
+ prove their address again. It must not be treated like `UNAUTHENTICATED`:
45
+ there is no refresh for a guest.
46
+ - Without `features.cookieAuth`, the client keeps the token `issue` returns and
47
+ sends it as `Authorization: Guest <token>`. **The token changes whenever a
48
+ grant is added**; store the new one.
49
+
50
+ ### Added
51
+
52
+ - **`GuestSessionService`**: `issue({ principal, grant })` starts a session or
53
+ adds a grant to the one the browser holds; `revokeGrant({ kind, id })`,
54
+ `revoke(sessionId)` and `end(request, res)` stop them. See the README, "Guest
55
+ Sessions".
56
+ - **`@AllowGuestSession(kind)`**, on a method, lets a guest with a standing
57
+ grant of that kind reach the handler. Guards made by `createAuthGuard` read
58
+ it, trying the user first; so does the new **`GuestSessionGuard`**, which
59
+ admits guests only. **`@CurrentGuest()`** gives the handler the guest and
60
+ their grants of that kind.
61
+ - **`IGuestSessionRepository`**, `InMemoryGuestSessionRepository`, and
62
+ `guestSessionRepositoryInstance`, which enables the feature.
63
+ - **`assertGuestSessionRepositoryContract`** in
64
+ `@ambushsoftworks/nestjs-auth-graphql/testing`.
65
+ - **`guestSession`** options: `maxGrantsPerSession` (default 10; `1` gives one
66
+ grant per session) and `maxLifetimeDays` (default 30).
67
+ - Security events `GUEST_SESSION_ISSUED`, `GUEST_SESSION_REJECTED`,
68
+ `GUEST_SESSION_REVOKED`, `GUEST_SESSION_ENDED` and `GUEST_GRANT_REVOKED`.
69
+ - Error codes `GUEST_SESSION_INVALID` (401) and `GUEST_SESSION_UNAVAILABLE`
70
+ (503).
71
+
72
+ ### Changed
73
+
74
+ - A guard made by `createAuthGuard` now reads `@AllowGuestSession` on the
75
+ handler. On a handler without it, which is every handler in an existing app,
76
+ nothing changes: no guest credential is read. `JwtAuthGuard` is unchanged and
77
+ ignores the decorator.
78
+
79
+ ## [0.18.0] - 2026-10-07
80
+
81
+ **Email challenges**: one-time codes that prove someone holds an email address,
82
+ whether or not it belongs to a user. Additive; nothing changes for an app that
83
+ does not use it. Requested by Ariadne, for outside translators who open one job
84
+ and are not users.
85
+
86
+ ### ⚠ External configuration required
87
+
88
+ Only to use email challenges:
89
+ - **A table for `IEmailChallengeRepository`**, with no foreign key to users.
90
+ Schema and Prisma adapter in `examples/email-challenge/`. Run
91
+ `assertEmailChallengeRepositoryContract` against it on your real database.
92
+ - **An email service that can send the code.** `ConfigurableEmailService` does,
93
+ with the default renderer. A custom `IEmailTemplateRenderer` adds
94
+ `renderEmailChallengeCodeEmail`; a hand-written `IEmailService` adds
95
+ `sendEmailChallengeCode`.
96
+ - **A shared `rateLimiterInstance`** if you run more than one instance: the send
97
+ limits are counted there.
98
+ - **A button in your frontend** that asks for the code. Do not send it when the
99
+ page loads: mail scanners open links.
100
+
101
+ ### Added
102
+
103
+ - **`EmailChallengeService`**: `issue({ email, purpose, emailData? })` emails a
104
+ 6-digit code and returns an opaque `challengeId`; `verify({ challengeId, code,
105
+ email, purpose })` answers `{ ok: true, email, purpose }` or `{ ok: false,
106
+ reason: 'WRONG' | 'EXPIRED' | 'LOCKED' }`; `revoke({ purpose, email? })` stops
107
+ outstanding codes. No `perform*` resolver methods: your resolver calls it with
108
+ an address and purpose from your own data. See the README, "Email Challenges",
109
+ including six rules for that resolver.
110
+ - **`IEmailChallengeRepository`**, `InMemoryEmailChallengeRepository`, and
111
+ `emailChallengeRepositoryInstance`, which enables the feature. Rows hold keyed
112
+ hashes only: no address, purpose or code.
113
+ - **`assertEmailChallengeRepositoryContract`** in
114
+ `@ambushsoftworks/nestjs-auth-graphql/testing`.
115
+ - **`emailChallenge`** options: `ttlMinutes` (default 15, up to 60) and
116
+ `issueLimits` (`cooldownSeconds` 60, `perHour` 5, `perIpPerHour` 30).
117
+ - `IEmailService.sendEmailChallengeCode` and
118
+ `IEmailTemplateRenderer.renderEmailChallengeCodeEmail`, both optional.
119
+ - Security events `EMAIL_CHALLENGE_ISSUED`, `EMAIL_CHALLENGE_VERIFIED`,
120
+ `EMAIL_CHALLENGE_FAILED` and `EMAIL_CHALLENGE_LOCKED`.
121
+ - Error codes `EMAIL_CHALLENGE_UNAVAILABLE` and `EMAIL_CHALLENGE_SEND_FAILED`
122
+ (both 503).
123
+
124
+ ### Changed
125
+
126
+ - The boot warning for a missing `rateLimiterInstance` now names everything
127
+ counted there, not only password-reset throttling.
128
+
30
129
  ## [0.17.1] - 2026-10-07
31
130
 
32
131
  A security fix for passwordless sign-up, which shipped in 0.17.0. **If you offer
package/README.md CHANGED
@@ -21,6 +21,8 @@ Production-grade authentication module for NestJS GraphQL + REST APIs. JWT, cook
21
21
  - [Biometric Authentication](#biometric-authentication)
22
22
  - [Passkeys](#passkeys)
23
23
  - [Step-Up Re-authentication](#step-up-re-authentication)
24
+ - [Email Challenges](#email-challenges)
25
+ - [Guest Sessions](#guest-sessions)
24
26
  - [Brute Force Protection](#brute-force-protection)
25
27
  - [Password Reset](#password-reset)
26
28
  - [Password Policy](#password-policy)
@@ -42,6 +44,8 @@ Production-grade authentication module for NestJS GraphQL + REST APIs. JWT, cook
42
44
  - [Biometric Options (`biometric`)](#biometric-options-biometric)
43
45
  - [Passkey Options (`passkey`)](#passkey-options-passkey)
44
46
  - [Step-Up Options (`stepUp`)](#step-up-options-stepup)
47
+ - [Email Challenge Options (`emailChallenge`)](#email-challenge-options-emailchallenge)
48
+ - [Guest Session Options (`guestSession`)](#guest-session-options-guestsession)
45
49
  - [SendGrid Options (`sendgrid`)](#sendgrid-options-sendgrid)
46
50
  - [Twilio Options (`twilio`)](#twilio-options-twilio)
47
51
  - [Optional Instance Options](#optional-instance-options)
@@ -1465,6 +1469,337 @@ over a short expiry. Shorten `stepUp.ttlSeconds` if you want a tighter window.
1465
1469
  **Email codes use a new verification type, `step-up`.** If your verification
1466
1470
  repository stores the type in a database enum, add the value.
1467
1471
 
1472
+ ### Email Challenges
1473
+
1474
+ Prove that someone holds an email address, with a 6-digit code, **whether or not
1475
+ the address belongs to a user**: an outside translator opening a job, a referee
1476
+ uploading a letter, a client's employer confirming details. Since 0.18.0.
1477
+
1478
+ There are no resolver methods for this. Your own resolver calls
1479
+ `EmailChallengeService`:
1480
+
1481
+ ```typescript
1482
+ @Resolver()
1483
+ export class TranslationJobResolver {
1484
+ constructor(
1485
+ private readonly emailChallenges: EmailChallengeService,
1486
+ private readonly jobs: JobsService,
1487
+ ) {}
1488
+
1489
+ // The link in the job email carries a job reference and no credential.
1490
+ // "Send me a code" is a button on the page it opens.
1491
+ @Mutation(() => JobCodeSent)
1492
+ async sendJobCode(@Args('jobRef') jobRef: string, @CurrentRealm() realm: string, @Context() ctx) {
1493
+ const job = await this.jobs.findByReference(jobRef); // throws if unknown
1494
+ return this.emailChallenges.issue(
1495
+ { email: job.translatorEmail, purpose: `translation-job:${job.id}`, emailData: { firm: job.firmName } },
1496
+ { realm, ipAddress: extractIpAddress(ctx.req) },
1497
+ ); // { challengeId, expiresAt, expiresInMinutes }
1498
+ }
1499
+
1500
+ @Mutation(() => JobOpened)
1501
+ async openJob(@Args('input') input: OpenJobInput, @CurrentRealm() realm: string, @Context() ctx) {
1502
+ const job = await this.jobs.findByReference(input.jobRef);
1503
+ const result = await this.emailChallenges.verify(
1504
+ // The address and purpose this job says the code must be for.
1505
+ { challengeId: input.challengeId, code: input.code, email: job.translatorEmail, purpose: `translation-job:${job.id}` },
1506
+ { realm, ipAddress: extractIpAddress(ctx.req) },
1507
+ );
1508
+ if (!result.ok) return { ok: false, reason: result.reason }; // 'WRONG' | 'EXPIRED' | 'LOCKED'
1509
+ // result.email holds mail for this job. What they get now is yours to decide.
1510
+ }
1511
+ }
1512
+ ```
1513
+
1514
+ **Enable it** with a repository and an email service that can send the code:
1515
+
1516
+ ```typescript
1517
+ AuthModule.forRootAsync({
1518
+ useFactory: (emailChallenges: PrismaEmailChallengeRepository) => ({
1519
+ // ...
1520
+ emailChallengeRepositoryInstance: emailChallenges, // enables the feature; there is no flag
1521
+ rateLimiterInstance: sharedRateLimiter, // send limits; shared if you run several instances
1522
+ }),
1523
+ })
1524
+ ```
1525
+
1526
+ `ConfigurableEmailService` sends the code through `renderEmailChallengeCodeEmail`
1527
+ on its renderer; the default renderer has a generic one. A hand-written
1528
+ `IEmailService` adds `sendEmailChallengeCode(email, code, expiresInMinutes, { purpose, data })`.
1529
+ Without the repository or the method, `issue` answers `EMAIL_CHALLENGE_UNAVAILABLE`.
1530
+
1531
+ **What a verified challenge proves:** that whoever entered the code could read
1532
+ mail sent to that address in the last 15 minutes. Not who they are, and nothing
1533
+ about any account.
1534
+
1535
+ | Behaviour | Detail |
1536
+ |---|---|
1537
+ | Code | 6 digits, 15 minutes (`emailChallenge.ttlMinutes`, or `ttlMinutes` per call; 1 to 60) |
1538
+ | Attempts | Three per code, right or wrong, then `LOCKED`. A new code can be sent |
1539
+ | Single use | A code is accepted once. Two requests with the right code: one wins |
1540
+ | Newest wins | Sending a new code stops the earlier one for that address and purpose |
1541
+ | Binding | `verify` refuses a challenge issued for another address, purpose or realm, as `EXPIRED`. Purposes never affect each other, nor a user's own email verification |
1542
+ | Send limits | Per address and purpose: one a minute, five an hour. Per IP: thirty an hour. `RATE_LIMIT_EXCEEDED` with `retryAfterSeconds`; see [`emailChallenge`](#email-challenge-options-emailchallenge) |
1543
+ | A failed send | `EMAIL_CHALLENGE_SEND_FAILED` and nothing else: provider errors quote the recipient. The detail goes to `IAuthLogger.error`. Costs the address a minute's wait, not its hourly five |
1544
+ | Revoking | `revoke({ purpose, email? })` stops outstanding codes, for when the job is cancelled |
1545
+
1546
+ | Result or code | Meaning | Tell the person |
1547
+ |---|---|---|
1548
+ | `{ ok: true, email, purpose }` | The code was right | |
1549
+ | `reason: 'WRONG'` | Not the code; attempts remain | Try again |
1550
+ | `reason: 'EXPIRED'` | Ran out, already used, replaced, or not a challenge for this address and purpose | Send a new code |
1551
+ | `reason: 'LOCKED'` | Three attempts spent | Send a new code |
1552
+ | `RATE_LIMIT_EXCEEDED` (429) | Too many codes requested | Wait `retryAfterSeconds` |
1553
+ | `EMAIL_CHALLENGE_SEND_FAILED` (503) | The email could not be sent | Try again shortly |
1554
+ | `EMAIL_CHALLENGE_UNAVAILABLE` (503) | Not configured | |
1555
+
1556
+ A refused code is a result, not an exception: what to show is your decision.
1557
+ The exact cause (`code_wrong`, `purpose_mismatch`, `challenge_expired`, ...) is
1558
+ in the `EMAIL_CHALLENGE_FAILED` security event only.
1559
+
1560
+ #### Rules for your resolver
1561
+
1562
+ `issue` sends mail to whatever address it is given, and the person asking may
1563
+ not be the address's owner. Six rules keep that safe:
1564
+
1565
+ 1. **Take the address and the purpose from your own data, never from the
1566
+ request.** `verify` then enforces that the code was issued for exactly that.
1567
+ 2. **Send the code on a button press, not when the page loads.** Mail scanners
1568
+ and link previews open links, and each would send a code and spend the
1569
+ cooldown.
1570
+ 3. **Make the reference in the link unguessable.** Whoever holds the link can
1571
+ spend the address's five codes an hour, which locks the real person out for
1572
+ that hour. They still cannot read the code.
1573
+ 4. **Pass `ipAddress`.** Without it the per-IP limit is not applied (the service
1574
+ warns once).
1575
+ 5. **Keep personal data out of `purpose`.** It is written to security events as
1576
+ given. Use ids. Put a counter in it (`translation-job:42:2`) if a re-sent job
1577
+ should start with a clean slate.
1578
+ 6. **Show the address masked** (`t•••@agency.example`), not in full: the person
1579
+ looking at the page may be holding a forwarded link.
1580
+
1581
+ #### Implementing `IEmailChallengeRepository`
1582
+
1583
+ One table, no relation to your users. Reference schema and Prisma adapter:
1584
+ [`examples/email-challenge/`](examples/email-challenge/).
1585
+
1586
+ ```prisma
1587
+ model EmailChallenge {
1588
+ id String @id // generated by the package
1589
+ emailHash String
1590
+ purposeHash String
1591
+ codeHash String
1592
+ attempts Int @default(0)
1593
+ consumedAt DateTime?
1594
+ expiresAt DateTime
1595
+ createdAt DateTime
1596
+
1597
+ @@index([purposeHash, emailHash])
1598
+ @@index([expiresAt])
1599
+ }
1600
+ ```
1601
+
1602
+ Nothing in a row is readable. The address, the purpose and the code are stored
1603
+ as keyed hashes the package computes (the realm is inside the first two), so
1604
+ the table holds no addresses of people who are not your users, and the adapter
1605
+ has no realm to filter on.
1606
+
1607
+ **Two methods must each be one conditional write**, never a read followed by a
1608
+ write: `registerAttempt` (`UPDATE … SET attempts = attempts + 1 WHERE attempts <
1609
+ max AND consumedAt IS NULL`) and `consumeChallenge` (`UPDATE … WHERE consumedAt
1610
+ IS NULL`). A read-then-write version lets guesses sent together all count as
1611
+ one, or one code be accepted twice. Check your adapter against your real
1612
+ database:
1613
+
1614
+ ```typescript
1615
+ import { assertEmailChallengeRepositoryContract } from '@ambushsoftworks/nestjs-auth-graphql/testing';
1616
+
1617
+ it('meets the email challenge repository contract', async () => {
1618
+ await assertEmailChallengeRepositoryContract(() => new PrismaEmailChallengeRepository(prisma), {
1619
+ cleanup: async ({ challengeIds }) => {
1620
+ await prisma.emailChallenge.deleteMany({ where: { id: { in: challengeIds } } });
1621
+ },
1622
+ });
1623
+ });
1624
+ ```
1625
+
1626
+ It calls `deleteExpiredChallenges`, which removes every expired challenge in
1627
+ that database. `InMemoryEmailChallengeRepository` is for tests and a single
1628
+ instance.
1629
+
1630
+ ### Guest Sessions
1631
+
1632
+ A session for someone who is **not a user**: it works only on the routes you
1633
+ mark, for the things you granted, until the dates you set. For the outside
1634
+ translator who has proved their address with an [email challenge](#email-challenges)
1635
+ and now needs to work on one job. Since 0.19.0.
1636
+
1637
+ ```typescript
1638
+ // 1. After your own check that they hold the address (a verified email challenge):
1639
+ const issued = await this.guestSessions.issue(
1640
+ {
1641
+ principal: { id: translator.id, email: job.translatorEmail }, // your id for the person
1642
+ grant: { kind: 'translation-job', id: job.id, expiresAt: job.dueAt },
1643
+ },
1644
+ { request: ctx.req, res: ctx.res, realm, ipAddress: extractIpAddress(ctx.req) },
1645
+ );
1646
+
1647
+ // 2. On the routes a guest may reach, and no others:
1648
+ @Mutation(() => UploadResult)
1649
+ @UseGuards(CsrfGuard, JwtGuard) // a guard from createAuthGuard
1650
+ @AllowGuestSession('translation-job') // on the method
1651
+ upload(@Args('input') input: UploadInput,
1652
+ @CurrentUser() user: User | undefined,
1653
+ @CurrentGuest() guest: GuestPrincipal | undefined) {
1654
+ if (guest && !guest.grants.some((g) => g.id === input.jobId)) throw new ForbiddenException();
1655
+ // ...
1656
+ }
1657
+
1658
+ // 3. When the job is delivered or taken back:
1659
+ await this.guestSessions.revokeGrant({ kind: 'translation-job', id: job.id }, { realm });
1660
+ ```
1661
+
1662
+ **Enable it** with `guestSessionRepositoryInstance`. There is no flag, and no
1663
+ fallback store.
1664
+
1665
+ #### What a guest can and cannot reach
1666
+
1667
+ | | |
1668
+ |---|---|
1669
+ | A handler with `@AllowGuestSession(kind)` | Admitted, if the session holds a standing grant of that kind |
1670
+ | Every other handler | **Refused. The guest credential is not even read** |
1671
+ | `@CurrentUser()`, `request.user` | Empty for a guest. The guest is on `request.guest` / `@CurrentGuest()` |
1672
+ | `PermissionGuard`, `TenantGuard`, `ScopeGuard` | Refuse a guest: there is no user for them to look up |
1673
+ | Token refresh | None. When a grant ends, the person proves their address again |
1674
+ | GraphQL subscriptions | Not accepted |
1675
+
1676
+ `@AllowGuestSession` is read **from the method only**. On a class it does
1677
+ nothing, so decorating a resolver cannot open every method on it.
1678
+
1679
+ | Guard on the route | With `@AllowGuestSession(kind)` |
1680
+ |---|---|
1681
+ | One made by `createAuthGuard([...])` | Users and guests. The user strategies run first, so a signed-in user is handled exactly as on any other route; only if none authenticates is the guest session looked at |
1682
+ | `GuestSessionGuard` | Guests only |
1683
+ | `JwtAuthGuard` | Users only: it ignores the decorator |
1684
+
1685
+ #### What the package checks, and what is yours
1686
+
1687
+ The package guarantees that the guest holds a standing grant of the **kind** the
1688
+ route names. **Which one is your check**, in every opted-in handler: compare
1689
+ what the request asks for with `guest.grants`. A route marked for
1690
+ `translation-job` is reachable by every guest with any translation job.
1691
+
1692
+ Two more things are yours:
1693
+
1694
+ - **What the response contains.** Field resolvers do not run guards by default.
1695
+ A guest-allowed query returning `Job { client { ... } }` exposes whatever
1696
+ those fields resolve. Return guest-shaped types from guest routes.
1697
+ - **Do not make a guest route `@Public()`.** `CsrfGuard` skips public routes,
1698
+ and a guest cookie needs its CSRF check like any other cookie session. That
1699
+ is why the route keeps its normal guard and adds the decorator.
1700
+
1701
+ #### Several grants in one session
1702
+
1703
+ A translator with two open jobs uses one browser. When `issue` is given the
1704
+ request, and that browser already holds a guest session **for the same
1705
+ `principal.id`, address and realm**, the new grant joins it. Otherwise a new
1706
+ session starts.
1707
+
1708
+ | Behaviour | Detail |
1709
+ |---|---|
1710
+ | The token changes when a grant is added | The old one stops working, so an older copy of it never gains the new job. With cookie auth the cookie is replaced for you |
1711
+ | `guestSession.maxGrantsPerSession` (default 10) | When full, the grant that ends soonest is revoked to make room |
1712
+ | `maxGrantsPerSession: 1` | One grant per session: each new one replaces the last |
1713
+ | The same `kind` and `id` again | Replaces the earlier grant, taking the new end date |
1714
+ | Each grant has its own `expiresAt` | Never moved. At most `guestSession.maxLifetimeDays` (default 30) ahead |
1715
+
1716
+ #### Credential and codes
1717
+
1718
+ The credential is an opaque random token, stored only as a keyed hash. It is not
1719
+ a JWT, so nothing that verifies access tokens can accept it.
1720
+
1721
+ - **With `features.cookieAuth`** and `res` passed to `issue`, it is set as the
1722
+ HttpOnly cookie `guest_session` (`__Host-guest_session` with
1723
+ `cookie.useHostPrefix`), with your cookie options, lasting until the session's
1724
+ last grant ends. It is a different cookie from the access token, so a staff
1725
+ member who opens a translator's link stays signed in.
1726
+ - **Otherwise** the client sends `Authorization: Guest <token>`.
1727
+
1728
+ | Answer | When | Tell the person |
1729
+ |---|---|---|
1730
+ | The route's usual `UNAUTHENTICATED` | No credential at all | Sign in, or open your link |
1731
+ | `GUEST_SESSION_INVALID` (401) | A guest credential that cannot be used here: revoked, ended, another realm, another kind, or unknown | Prove your address again |
1732
+ | `GUEST_SESSION_UNAVAILABLE` (503) | `issue` without a repository | |
1733
+
1734
+ `GUEST_SESSION_INVALID` is deliberately not `UNAUTHENTICATED`: clients treat
1735
+ that as "refresh and retry", which can never work for a guest. The same code is
1736
+ in the body on REST routes. The exact cause (`revoked`, `expired`,
1737
+ `realm_mismatch`, `kind_not_allowed`) is in the `GUEST_SESSION_REJECTED`
1738
+ security event.
1739
+
1740
+ `end(request, res)` is the guest's own "I'm done": it revokes the session and
1741
+ clears the cookie. Put `CsrfGuard` on that route too.
1742
+
1743
+ #### Implementing `IGuestSessionRepository`
1744
+
1745
+ Two tables, no relation to your users. Reference schema and Prisma adapter:
1746
+ [`examples/guest-sessions/`](examples/guest-sessions/).
1747
+
1748
+ ```prisma
1749
+ model GuestSession {
1750
+ id String @id
1751
+ tokenHash String @unique // read on every guest request
1752
+ principalId String
1753
+ email String
1754
+ realm String?
1755
+ revokedAt DateTime?
1756
+ createdAt DateTime
1757
+ grants GuestGrant[]
1758
+ }
1759
+
1760
+ model GuestGrant {
1761
+ id String @id
1762
+ sessionId String
1763
+ kind String
1764
+ grantId String
1765
+ realm String?
1766
+ expiresAt DateTime
1767
+ revokedAt DateTime?
1768
+ createdAt DateTime
1769
+ session GuestSession @relation(fields: [sessionId], references: [id], onDelete: Cascade)
1770
+
1771
+ @@index([sessionId])
1772
+ @@index([kind, grantId])
1773
+ @@index([expiresAt])
1774
+ }
1775
+ ```
1776
+
1777
+ Two rules are the ones adapters get wrong, and the contract checks both:
1778
+
1779
+ - **`rotateToken` is one conditional write** (`UPDATE … SET tokenHash = new WHERE
1780
+ tokenHash = old AND revokedAt IS NULL`), never a read followed by a write.
1781
+ - **`realm: null` means "no realm", never "any realm".** Pass it through as
1782
+ given; `where: { realm: undefined }` drops the condition.
1783
+
1784
+ ```typescript
1785
+ import { assertGuestSessionRepositoryContract } from '@ambushsoftworks/nestjs-auth-graphql/testing';
1786
+
1787
+ it('meets the guest session repository contract', async () => {
1788
+ await assertGuestSessionRepositoryContract(() => new PrismaGuestSessionRepository(prisma), {
1789
+ realms: false, // if you do not use realms
1790
+ cleanup: async ({ sessionIds, grantIds }) => {
1791
+ await prisma.guestGrant.deleteMany({ where: { id: { in: grantIds } } });
1792
+ await prisma.guestSession.deleteMany({ where: { id: { in: sessionIds } } });
1793
+ },
1794
+ });
1795
+ });
1796
+ ```
1797
+
1798
+ It calls `deleteExpired`, which removes every expired grant and every session
1799
+ without grants in that database. `InMemoryGuestSessionRepository` is for tests
1800
+ and a single instance. The store is read once per request that presents a guest
1801
+ credential on an opted-in route, and never written per request.
1802
+
1468
1803
  ### Brute Force Protection
1469
1804
 
1470
1805
  Provide a `bruteForceRepositoryInstance` and the module tracks failed login attempts and temporarily locks accounts. Without one, `NoOpBruteForceRepository` is used and no account is ever locked; there is no flag to set. The lockout policy (attempt thresholds, lockout duration) is determined by your `IBruteForceRepository` implementation.
@@ -1971,6 +2306,26 @@ reported at boot as ignored. With it, `rp` is required.
1971
2306
  | `ttlSeconds` | `number` | `300` | Step-up token lifetime, 1 to 3600 |
1972
2307
  | `issueOnLogin` | `boolean` | `false` | Return a step-up token from fresh sign-ins (see [Step-Up](#step-up-re-authentication)) |
1973
2308
 
2309
+ ### Email Challenge Options (`emailChallenge`)
2310
+
2311
+ Read only with `emailChallengeRepositoryInstance`. See [Email Challenges](#email-challenges).
2312
+
2313
+ | Option | Type | Default | Description |
2314
+ |--------|------|---------|-------------|
2315
+ | `ttlMinutes` | `number` | `15` | How long a code lasts, 1 to 60. `issue` can override it per call |
2316
+ | `issueLimits.cooldownSeconds` | `number` | `60` | Seconds between two codes for one address and purpose. `0` turns it off |
2317
+ | `issueLimits.perHour` | `number` | `5` | Codes an hour for one address and purpose. At least 1 |
2318
+ | `issueLimits.perIpPerHour` | `number` | `30` | Codes an hour from one IP, across addresses. `0` turns it off |
2319
+
2320
+ ### Guest Session Options (`guestSession`)
2321
+
2322
+ Read only with `guestSessionRepositoryInstance`. See [Guest Sessions](#guest-sessions).
2323
+
2324
+ | Option | Type | Default | Description |
2325
+ |--------|------|---------|-------------|
2326
+ | `maxGrantsPerSession` | `number` | `10` | Standing grants one session may hold, 1 to 100. `1` gives one grant per session |
2327
+ | `maxLifetimeDays` | `number` | `30` | The furthest ahead a grant's `expiresAt` may be, 1 to 365 |
2328
+
1974
2329
  ### Optional Instance Options
1975
2330
 
1976
2331
  | Option | Interface | Fallback |
@@ -1982,6 +2337,8 @@ reported at boot as ignored. With it, `rp` is required.
1982
2337
  | `bruteForceRepositoryInstance` | `IBruteForceRepository` | `NoOpBruteForceRepository` |
1983
2338
  | `biometricRepositoryInstance` | `IBiometricRepository` | none: biometrics disabled |
1984
2339
  | `passkeyRepositoryInstance` | `IPasskeyRepository` | none: passkeys disabled |
2340
+ | `emailChallengeRepositoryInstance` | `IEmailChallengeRepository` | none: email challenges unavailable |
2341
+ | `guestSessionRepositoryInstance` | `IGuestSessionRepository` | none: guest sessions disabled |
1985
2342
  | `authLoggerInstance` | `IAuthLogger` | `ConsoleAuthLogger` |
1986
2343
  | `tenantRepositoryInstance` | `ITenantRepository` | `null` |
1987
2344
  | `tenantExtractorInstance` | `ITenantExtractor` | `null` |
@@ -2004,6 +2361,8 @@ reported at boot as ignored. With it, `rp` is required.
2004
2361
  | `@RequirePermissions('p1', 'p2')` | Method/Class | Require specific permissions |
2005
2362
  | `@RequireScopes('s1', 's2')` | Method/Class | Require API key scopes, enforced by `ScopeGuard`. People are unaffected. See [API Key Authentication](#api-key-authentication) |
2006
2363
  | `@CurrentUser()` | Parameter | Inject authenticated user |
2364
+ | `@AllowGuestSession('kind')` | **Method only** | Let a guest session with a standing grant of that kind reach the handler. See [Guest Sessions](#guest-sessions) |
2365
+ | `@CurrentGuest()` | Parameter | Inject the admitted guest, or `undefined` for a user |
2007
2366
  | `@CurrentTenant()` | Parameter | Inject resolved tenant context |
2008
2367
  | `@CurrentRealm()` | Parameter | Inject resolved realm from request context |
2009
2368
  | `@ResourceScope('resourceType', 'argName')` | Method | Resource-level permission scoping |
@@ -2012,7 +2371,8 @@ reported at boot as ignored. With it, `rp` is required.
2012
2371
 
2013
2372
  | Guard | Description |
2014
2373
  |-------|-------------|
2015
- | `createAuthGuard(strategies, options)` | Factory for multi-strategy auth guards |
2374
+ | `createAuthGuard(strategies, options)` | Factory for multi-strategy auth guards. Also admits guests on handlers marked `@AllowGuestSession()`, after trying the user strategies |
2375
+ | `GuestSessionGuard` | Guests only, on handlers marked `@AllowGuestSession()` |
2016
2376
  | `CsrfGuard` | CSRF header validation for cookie auth |
2017
2377
  | `TenantGuard` | Multi-tenant context resolution |
2018
2378
  | `PermissionGuard` | Permission checking against tenant context |
@@ -2110,6 +2470,8 @@ export class CustomGuard implements CanActivate {
2110
2470
  - **`__Host-` cookie prefix** support for enhanced cookie security
2111
2471
  - **Separate HMAC secrets** for refresh tokens, verification codes, and OAuth state
2112
2472
  - **Realm-based identity isolation** with JWT realm claim validation, realm-scoped rate limiting and lockout, and auth flows that refuse a request without a realm
2473
+ - **Guest sessions for non-users** -- an opaque token stored as a keyed hash, never a JWT, kept off `request.user`, refused by every route that has not opted in by name, with a hard end per grant, revocation effective on the next request, and the token replaced whenever a grant is added (see [Guest Sessions](#guest-sessions))
2474
+ - **Email challenges for non-users** -- one-time codes whose address, purpose and code are stored only as keyed hashes, bound to the address, purpose and realm they were issued for, with attempts and single use enforced by atomic writes in your store (see [Email Challenges](#email-challenges))
2113
2475
  - **Pinned JWT algorithms** -- the package's own `jwt` strategy verifies `HS256` only, and `createExternalJwtStrategy` requires an explicit `algorithms` list from one family (see [External JWTs](#external-jwts))
2114
2476
  - **API keys with scopes and expiry**, refused hard when they carry your prefix and match no key, so no other strategy accepts them
2115
2477
  - **No raw personal data in package logs** — log lines use the user ID or a salted hash (`emailHash=`, `ipHash=`, under `AUTH_IP_HASH_SALT` or `jwtSecret`), phone numbers keep only their last two digits, and the default `ConsoleAuthLogger` redacts security-event metadata. A custom `authLoggerInstance` receives the raw fields; pass them through `redactSecurityEventMetadata` to store them redacted.
@@ -10,6 +10,8 @@ import { IVerificationRepository } from './interfaces/verification-repository.in
10
10
  import { IBruteForceRepository } from './interfaces/brute-force-repository.interface';
11
11
  import { IBiometricRepository } from './interfaces/biometric-repository.interface';
12
12
  import { IPasskeyRepository } from './interfaces/passkey-repository.interface';
13
+ import { IEmailChallengeRepository } from './interfaces/email-challenge-repository.interface';
14
+ import { IGuestSessionRepository } from './interfaces/guest-session-repository.interface';
13
15
  import { PasskeyRelyingPartyConfig } from './utils/passkey-rp';
14
16
  import { IAuthLogger } from './interfaces/auth-logger.interface';
15
17
  import { ITenantRepository } from './interfaces/tenant-repository.interface';
@@ -35,6 +37,8 @@ export interface AuthModuleOptions {
35
37
  bruteForceRepositoryInstance?: IBruteForceRepository;
36
38
  biometricRepositoryInstance?: IBiometricRepository;
37
39
  passkeyRepositoryInstance?: IPasskeyRepository;
40
+ emailChallengeRepositoryInstance?: IEmailChallengeRepository;
41
+ guestSessionRepositoryInstance?: IGuestSessionRepository;
38
42
  authLoggerInstance?: IAuthLogger;
39
43
  refreshTokenSecret?: string;
40
44
  verificationCodeSecret?: string;
@@ -148,6 +152,18 @@ export interface AuthModuleOptions {
148
152
  removed?: boolean;
149
153
  };
150
154
  };
155
+ emailChallenge?: {
156
+ ttlMinutes?: number;
157
+ issueLimits?: {
158
+ cooldownSeconds?: number;
159
+ perHour?: number;
160
+ perIpPerHour?: number;
161
+ };
162
+ };
163
+ guestSession?: {
164
+ maxGrantsPerSession?: number;
165
+ maxLifetimeDays?: number;
166
+ };
151
167
  stepUp?: {
152
168
  ttlSeconds?: number;
153
169
  issueOnLogin?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../src/auth.module.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../src/auth.module.ts"],"names":[],"mappings":"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"}