@ambushsoftworks/nestjs-auth-graphql 0.12.0 → 0.15.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 (62) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/README.md +285 -1
  3. package/dist/auth.module.d.ts +9 -0
  4. package/dist/auth.module.d.ts.map +1 -1
  5. package/dist/auth.module.js +69 -4
  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/index.d.ts +3 -6
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +3 -6
  14. package/dist/index.js.map +1 -1
  15. package/dist/interfaces/biometric-repository.interface.d.ts +30 -17
  16. package/dist/interfaces/biometric-repository.interface.d.ts.map +1 -1
  17. package/dist/interfaces/biometric-verifier.interface.d.ts +10 -0
  18. package/dist/interfaces/biometric-verifier.interface.d.ts.map +1 -0
  19. package/dist/interfaces/{magic-link-repository.interface.js → biometric-verifier.interface.js} +1 -1
  20. package/dist/interfaces/biometric-verifier.interface.js.map +1 -0
  21. package/dist/services/biometric-auth.service.d.ts +51 -17
  22. package/dist/services/biometric-auth.service.d.ts.map +1 -1
  23. package/dist/services/biometric-auth.service.js +121 -67
  24. package/dist/services/biometric-auth.service.js.map +1 -1
  25. package/dist/services/biometric-challenge.service.d.ts +27 -0
  26. package/dist/services/biometric-challenge.service.d.ts.map +1 -0
  27. package/dist/services/biometric-challenge.service.js +116 -0
  28. package/dist/services/biometric-challenge.service.js.map +1 -0
  29. package/dist/services/biometric-verification.service.d.ts.map +1 -1
  30. package/dist/services/biometric-verification.service.js +4 -0
  31. package/dist/services/biometric-verification.service.js.map +1 -1
  32. package/dist/test-utils/mock-repositories.d.ts.map +1 -1
  33. package/dist/test-utils/mock-repositories.js +8 -7
  34. package/dist/test-utils/mock-repositories.js.map +1 -1
  35. package/dist/verifiers/es256-device-key.verifier.d.ts +14 -0
  36. package/dist/verifiers/es256-device-key.verifier.d.ts.map +1 -0
  37. package/dist/verifiers/es256-device-key.verifier.js +42 -0
  38. package/dist/verifiers/es256-device-key.verifier.js.map +1 -0
  39. package/package.json +8 -2
  40. package/dist/interfaces/magic-link-repository.interface.d.ts +0 -6
  41. package/dist/interfaces/magic-link-repository.interface.d.ts.map +0 -1
  42. package/dist/interfaces/magic-link-repository.interface.js.map +0 -1
  43. package/dist/interfaces/password-reset-strategy.interface.d.ts +0 -7
  44. package/dist/interfaces/password-reset-strategy.interface.d.ts.map +0 -1
  45. package/dist/interfaces/password-reset-strategy.interface.js +0 -3
  46. package/dist/interfaces/password-reset-strategy.interface.js.map +0 -1
  47. package/dist/repositories/noop-biometric.repository.d.ts +0 -26
  48. package/dist/repositories/noop-biometric.repository.d.ts.map +0 -1
  49. package/dist/repositories/noop-biometric.repository.js +0 -56
  50. package/dist/repositories/noop-biometric.repository.js.map +0 -1
  51. package/dist/repositories/noop-magic-link.repository.d.ts +0 -9
  52. package/dist/repositories/noop-magic-link.repository.d.ts.map +0 -1
  53. package/dist/repositories/noop-magic-link.repository.js +0 -37
  54. package/dist/repositories/noop-magic-link.repository.js.map +0 -1
  55. package/dist/strategies/magic-link.strategy.d.ts +0 -16
  56. package/dist/strategies/magic-link.strategy.d.ts.map +0 -1
  57. package/dist/strategies/magic-link.strategy.js +0 -80
  58. package/dist/strategies/magic-link.strategy.js.map +0 -1
  59. package/dist/strategies/verification-code.strategy.d.ts +0 -11
  60. package/dist/strategies/verification-code.strategy.d.ts.map +0 -1
  61. package/dist/strategies/verification-code.strategy.js +0 -44
  62. package/dist/strategies/verification-code.strategy.js.map +0 -1
package/CHANGELOG.md CHANGED
@@ -27,6 +27,216 @@ 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.15.0] - 2026-10-01
31
+
32
+ A biometric credential now signs in only to the realm it was enrolled in.
33
+
34
+ ### ⚠ Security
35
+
36
+ - **A credential enrolled in one realm could sign in to another.** The credential
37
+ record carried no realm, and `authenticateWithBiometric` issued the session with
38
+ the realm of the *request*. A credential enrolled in realm `staff`, used on a
39
+ request in realm `customers`, therefore received a token with `realm:
40
+ "customers"` for the staff user — and `JwtStrategy`'s realm check accepted it,
41
+ because the claim and the request agreed. Realms are documented as partitioning
42
+ identity; this crossed them. Only consumers that pass `realm` to
43
+ `authenticateWithBiometric` were exposed. A consumer that omitted it got a token
44
+ carrying the user's own realm instead, which is why the e2e suite did not see
45
+ it. Credentials only exist since 0.14.0 (released the day before).
46
+
47
+ ### Changed — breaking
48
+
49
+ - **`BiometricCredentialRecord` has a `realm` field, and adapters must persist
50
+ it.** `getCredential` must return it. A credential whose stored realm differs
51
+ from the request's is refused with the usual `401 Biometric authentication
52
+ failed` (logged reason `credential_realm_mismatch`). A credential stored
53
+ **without** a realm, while realms are enabled, is refused too: it fails closed.
54
+ - **With realms enabled, `enrolCredential` and `authenticateWithBiometric` require
55
+ a realm** and throw `400 Realm required` without one, the same answer as
56
+ `realmForRequest`. Pass the request's realm, e.g.
57
+ `this.getRealmFromContext(context)` in a resolver extending `BaseAuthResolver`:
58
+
59
+ ```typescript
60
+ enrolCredential({ userId, publicKey, deviceName, realm })
61
+ authenticateWithBiometric(input, { realm, res, ipAddress })
62
+ ```
63
+
64
+ ### Migration
65
+
66
+ 1. Add a nullable `realm` column to your biometric credential table, write it in
67
+ `storeCredential` and return it from `getCredential`.
68
+ 2. Backfill existing rows from their user's realm, e.g.
69
+ `UPDATE "BiometricCredential" c SET realm = u.realm FROM "User" u WHERE u.id = c."userId";`
70
+ Rows left without a realm stop authenticating while realms are enabled; their
71
+ users re-enrol.
72
+ 3. Pass `realm` to `enrolCredential` and `authenticateWithBiometric`.
73
+
74
+ Without realms (`realmExtractorInstance` not set) nothing changes: no column
75
+ needed, no argument needed.
76
+
77
+ ## [0.14.0] - 2026-09-29
78
+
79
+ Biometric authentication works. It has been exported since v0.1.3 and never did.
80
+
81
+ ### ⚠ Security
82
+
83
+ - **The previous `authenticateWithBiometric` verified nothing.** It took
84
+ `(userId, deviceId)`, checked only that an enrolled credential's id equalled the
85
+ supplied `deviceId`, and then issued an access and refresh token. No signature,
86
+ no challenge, no nonce; the public key `enableBiometric` stored was never read
87
+ by anything. Neither input is a secret, so anyone who knew a user id and an
88
+ enrolled device id could obtain a session. It also minted its own tokens, so it
89
+ bypassed the account-status check every other login goes through — a suspended
90
+ user could sign in.
91
+
92
+ **If you wired biometric authentication on 0.13.0 or earlier, treat any session
93
+ it issued as unauthenticated**, and revoke refresh tokens for affected users.
94
+ We know of no consumer that did: `IBiometricRepository` had no implementations,
95
+ and LiftIQ, the only app with biometrics, built its own rather than use this.
96
+
97
+ ### Added
98
+
99
+ - **A working challenge-response flow.**
100
+ ```
101
+ enrolCredential({ userId, publicKey, deviceName }) -> credentialId
102
+ requestChallenge(credentialId) -> { challengeId, challenge, expiresAt }
103
+ authenticateWithBiometric({ challengeId, credentialId, signature }) -> session
104
+ ```
105
+ 32-byte single-use challenges, 60-second default expiry, ES256 signature
106
+ verification, and a session issued through `AuthService.issueAuthSession` — the
107
+ same sink as every other login, so `assertUserActive`, cookie auth, realm claims
108
+ and `SESSION_ISSUED` all apply without being reimplemented.
109
+ - **`IBiometricVerifier`**, with `Es256DeviceKeyVerifier` as the default. The
110
+ verifier is selected by each credential's stored `algorithm`, so a credential
111
+ enrolled as ES256 is only ever verified as ES256 — the same pinning `JwtStrategy`
112
+ gained in 0.12.0. WebAuthn can be added later against this interface without
113
+ reshaping the port.
114
+ - **`biometric` module options**: `verifiers`, `challengeExpirySeconds`,
115
+ `challengeRateLimit`. Boot refuses an empty verifier list, two verifiers claiming
116
+ one algorithm, a non-positive expiry, and a rate limit without
117
+ `rateLimiterInstance`.
118
+ - **A per-IP rate limit** on challenge issuance alongside the per-credential one.
119
+ The per-credential limit cannot see a caller varying the credential id.
120
+
121
+ ### Changed — breaking
122
+
123
+ - **`IBiometricRepository` is reshaped**, composed from a new
124
+ `IBiometricChallengeStore`. It had no implementations, so this breaks nobody in
125
+ practice. Credentials now carry `algorithm`, `isActive`, `lastUsedAt` and
126
+ optional `metadata`; challenges are keyed on `credentialId`.
127
+
128
+ Its one hard contract: **`consumeChallenge` must be atomic.** A read, a check and
129
+ then a write lets two concurrent requests with one challenge both succeed. Use a
130
+ single conditional update and check the affected count — the README has the
131
+ Prisma form.
132
+
133
+ Two schema notes: `credentialId` on a challenge is **not** a foreign key (a
134
+ challenge is stored for ids that do not exist, so the signed-out request cannot
135
+ reveal which credentials are enrolled), and there is **no uniqueness** on any
136
+ device string.
137
+ - **`authenticateWithBiometric`'s signature changed** to
138
+ `({ challengeId, credentialId, signature }, opts)`.
139
+ - **`enableBiometric`, `disableBiometric` and `updateLastBiometricLogin` are gone
140
+ from `IUserRepositoryBiometric`.** Having credentials in two ports is why
141
+ enrolment and authentication used different stores and never met.
142
+ - **`NoOpBiometricRepository` is removed.** A no-op that accepts enrolments and
143
+ returns no credentials turns a missing implementation into a login that silently
144
+ never works. Without `biometricRepositoryInstance` the services are not
145
+ registered.
146
+ - **`credentialId` is generated by the package**, not by the client or the adapter.
147
+ It travels on an unauthenticated request, so it must be unguessable.
148
+
149
+ ### Security properties worth knowing
150
+
151
+ - **What it proves.** Possession of the enrolled private key — **not** that a
152
+ biometric check happened. Nothing in an ECDSA signature attests to that, and the
153
+ server cannot tell a hardware-backed key from a software one. Most mobile
154
+ biometric APIs accept a device PIN by default. Weigh that before making it a
155
+ sole factor.
156
+ - **Every biometric failure returns one 401.** Unknown credential, deactivated
157
+ credential, replayed or mismatched challenge, bad signature, missing verifier.
158
+ The route is reachable signed-out, so anything distinguishable reveals which
159
+ credentials exist. An inactive *account* is deliberately not flattened into it.
160
+ - **A failed attempt still burns the challenge**, so one challenge cannot be used
161
+ to try many signatures.
162
+
163
+ ### Documentation
164
+
165
+ - README: **Biometric Authentication**, leading with what a successful
166
+ authentication does and does not prove, the `IBiometricRepository` contract with
167
+ the atomic-consume example, and a `biometric` options table.
168
+ - CLAUDE.md: replaced a flow description that named three methods which never
169
+ existed and described verification the code never performed.
170
+
171
+ ### Testing
172
+
173
+ - 24 unit tests on the flow with real P-256 keys, real signatures and a real
174
+ `AuthService` — so the suspended-user case exercises `assertUserActive` rather
175
+ than a double. The spec they replace mocked both sides of every seam and
176
+ asserted the method returned tokens, which was true and hid that it returned
177
+ them without verifying anything.
178
+ - 9 end-to-end tests on both NestJS majors, including that the issued token works
179
+ on a guarded route, which is what proves the session came from the shared sink.
180
+ - 13 on the challenge service, including twenty concurrent claims on one challenge
181
+ yielding exactly one winner.
182
+ - Mutation checks: dropping the challenge/credential binding, forcing the verifier
183
+ true, ignoring `isActive`, minting tokens locally instead of via
184
+ `issueAuthSession`, and making the test store non-atomic. Each compiles; each
185
+ fails the suite.
186
+ ## [0.13.0] - 2026-09-29
187
+
188
+ **Never published separately — these changes ship inside `0.14.0`.** Both landed
189
+ on master before either was released, and `npm version` writes one version per
190
+ release, so `0.13.0` does not exist on npm. The entry stays because the changes
191
+ are real and worth reading on their own; if you are upgrading, everything here is
192
+ in `0.14.0`.
193
+
194
+ A documentation release, with one removal. Nothing in the runtime behaviour of
195
+ any supported path changes.
196
+
197
+ ### Removed
198
+
199
+ - **`MagicLinkStrategy`, `VerificationCodeStrategy`, `IPasswordResetStrategy`,
200
+ `IMagicLinkRepository` and `NoOpMagicLinkRepository`** are no longer exported,
201
+ and their files are gone. Nothing in the package consumed
202
+ `IPasswordResetStrategy` — `AuthService` calls `VerificationService` directly —
203
+ and `MagicLinkStrategy` could not be constructed through Nest DI at all,
204
+ because it injects a `'MAGIC_LINK_REPOSITORY'` token that `AuthModule` never
205
+ registered. The `NoOpMagicLinkRepository` docblock even showed a
206
+ `magicLinkRepositoryInstance` option that does not exist in
207
+ `AuthModuleOptions`.
208
+
209
+ This is a public API removal, hence the minor bump, but it cannot break a
210
+ working consumer: there was no supported way to reach any of it. If you
211
+ constructed `MagicLinkStrategy` yourself by registering that token, the file is
212
+ small and self-contained — copy it into your application. Password reset
213
+ through the package remains the 6-digit code flow, unchanged.
214
+
215
+ ### Documentation
216
+
217
+ - **README: new `Password Reset` section.** The feature had one passing mention,
218
+ despite the properties a consumer cannot infer and must not discover in
219
+ production: 3 attempts before the code is destroyed, the 15-minute expiry, the
220
+ 60-second per-user cooldown, the per-IP limit of 5/hour checked *before* the
221
+ user lookup for enumeration safety, and that a successful reset **revokes every
222
+ refresh token for that user**. It also states plainly that the response is
223
+ identical whether the address was sent a code, skipped or does not exist, and
224
+ that the distinguishing signal is `PASSWORD_RESET_REQUESTED` with
225
+ `result: 'SKIPPED_SOCIAL_ONLY'` on your `IAuthLogger`.
226
+ - **README: new `Phone & SMS Verification` section.** Five `perform*` methods
227
+ that previously appeared only as rows in a table. Records that SMS is
228
+ code-only — `features.verificationMode: 'token'` does not apply to it — and
229
+ that omitting `smsServiceInstance` substitutes a no-op, so codes are stored
230
+ but never delivered.
231
+ - **README: `sendgrid` and `twilio` option tables.** The only two of 42
232
+ `AuthModuleOptions` fields documented nowhere. Both are read solely by the
233
+ bundled services; a custom `IEmailService` or `ISmsService` ignores them.
234
+ - **`verification.baseUrl`** now names its exported type,
235
+ `VerificationBaseUrlResolver`.
236
+ - **`package.json` gains `homepage` and `bugs`.** npmjs.com renders both in the
237
+ sidebar, so until now the package page offered no route to the repository or
238
+ to filing an issue.
239
+
30
240
  ## [0.12.0] - 2026-09-20
31
241
 
32
242
  Ambush Desk asked for two things: verifying JWTs that other systems sign, and API
package/README.md CHANGED
@@ -16,8 +16,11 @@ Production-grade authentication module for NestJS GraphQL + REST APIs. JWT, cook
16
16
  - [External JWTs](#external-jwts)
17
17
  - [Email System](#email-system)
18
18
  - [Verification Modes](#verification-modes)
19
+ - [Phone \& SMS Verification](#phone--sms-verification)
19
20
  - [OAuth](#oauth)
21
+ - [Biometric Authentication](#biometric-authentication)
20
22
  - [Brute Force Protection](#brute-force-protection)
23
+ - [Password Reset](#password-reset)
21
24
  - [Password Policy](#password-policy)
22
25
  - [Lifecycle Hooks](#lifecycle-hooks)
23
26
  - [Account Status](#account-status)
@@ -34,12 +37,16 @@ Production-grade authentication module for NestJS GraphQL + REST APIs. JWT, cook
34
37
  - [CSRF Options](#csrf-options-csrf)
35
38
  - [Composable Email](#composable-email-email)
36
39
  - [Verification Options](#verification-options-verification)
40
+ - [Biometric Options (`biometric`)](#biometric-options-biometric)
41
+ - [SendGrid Options (`sendgrid`)](#sendgrid-options-sendgrid)
42
+ - [Twilio Options (`twilio`)](#twilio-options-twilio)
37
43
  - [Optional Instance Options](#optional-instance-options)
38
44
  - [Decorators](#decorators)
39
45
  - [Guards](#guards)
40
46
  - [GraphQL Subscriptions](#graphql-subscriptions)
41
47
  - [Utilities](#utilities)
42
48
  - [Security Features](#security-features)
49
+ - [Upgrading from 0.9.x](#upgrading-from-09x)
43
50
  - [Migrating to v0.12.0](#migrating-to-v0120)
44
51
  - [Migrating to v0.11.0](#migrating-to-v0110)
45
52
  - [Migrating to v0.10.0](#migrating-to-v0100)
@@ -805,6 +812,45 @@ link. `expiresInMinutes` reflects the mode's real expiry, so render it rather
805
812
  than hardcoding a duration. Both trailing parameters are optional, so existing
806
813
  implementations continue to compile.
807
814
 
815
+ ### Phone & SMS Verification
816
+
817
+ An authenticated user adds a phone number, receives a 6-digit code by SMS, and
818
+ confirms it. All five methods take the current user, so they sit behind your
819
+ authentication guard — this is account management, not a sign-in path.
820
+
821
+ | Method | Purpose |
822
+ |---|---|
823
+ | `performSendPhoneVerification(input, user)` | Store the number and send a code |
824
+ | `performVerifyPhone(input, user)` | Confirm the code, mark the number verified |
825
+ | `performResendPhoneVerification(phoneNumber, user)` | Send again, subject to the 60-second cooldown |
826
+ | `performRemovePhoneNumber(user)` | Clear the number and its verified state |
827
+ | `performPhoneVerificationStatus(user)` | Whether a number is present and verified |
828
+
829
+ Provide `smsServiceInstance` and `verificationRepositoryInstance`. `ISmsService`
830
+ has a single method, so the seam is small:
831
+
832
+ ```typescript
833
+ export class TwilioSmsService implements ISmsService {
834
+ async sendVerificationSms(phoneNumber: string, code: string): Promise<void> {
835
+ await this.client.messages.create({ to: phoneNumber, body: `Your code is ${code}` });
836
+ }
837
+ }
838
+ ```
839
+
840
+ Two things worth knowing:
841
+
842
+ - **SMS is code-only.** `features.verificationMode: 'token'` governs email
843
+ verification and password reset. A phone always receives a 6-digit code,
844
+ because a link is not usable from a text message in the same way — so
845
+ `verification.baseUrl` is irrelevant here.
846
+ - **Omitting `smsServiceInstance` does not fail loudly.** The package substitutes
847
+ a no-op that logs, so codes are generated and stored but never delivered.
848
+ Verification then only succeeds for someone reading your application logs.
849
+
850
+ The number's realm is scoped like any other identity field: with realms enabled,
851
+ `findByPhoneNumber` receives the realm, so the same number can exist once per
852
+ realm.
853
+
808
854
  ### OAuth
809
855
 
810
856
  Google and Facebook OAuth with encrypted token storage (AES-256-GCM).
@@ -858,6 +904,114 @@ It sits beside `useFactory` because Nest fixes a module's controllers before asy
858
904
 
859
905
  With realms enabled, a request without a realm gets `400 Realm required` first. Before 0.10.0 the lockout, inactive-account, rate-limit and unconfigured-provider cases answered 500.
860
906
 
907
+ ### Biometric Authentication
908
+
909
+ A device holds an ECDSA P-256 keypair, gated behind the OS biometric prompt. The
910
+ server stores the public key and verifies a signature over a challenge it issued.
911
+
912
+ ```typescript
913
+ biometricRepositoryInstance: myBiometricRepo, // enables the capability
914
+ biometric: {
915
+ challengeExpirySeconds: 60, // default
916
+ challengeRateLimit: { maxAttempts: 5, windowMs: 60_000 }, // needs rateLimiterInstance
917
+ },
918
+ ```
919
+
920
+ #### ⚠ What a successful authentication actually proves
921
+
922
+ **It proves the client still holds the private key. It does not prove a biometric
923
+ check happened.** Nothing in an ECDSA signature attests to that, and the server
924
+ cannot distinguish a key held in a hardware enclave from one generated in
925
+ software. Whether a fingerprint was scanned — or a PIN accepted instead, which
926
+ most mobile biometric APIs allow by default — is the client's word.
927
+
928
+ Treat this as *possession of a device-bound key that the client promises to
929
+ gate*. It is a good second factor and a good convenience login. Before making it
930
+ the sole factor for something sensitive, be clear that you are trusting the app,
931
+ not the biometric. WebAuthn's `userVerification` flag is what actually attests to
932
+ user verification, and is not this.
933
+
934
+ #### The flow
935
+
936
+ ```typescript
937
+ // 1. Enrol, authenticated. The package generates the credential id — keep it
938
+ // on the device alongside the private key.
939
+ const { credentialId } = await biometricAuth.enrolCredential({
940
+ userId: user.id,
941
+ publicKey, // PEM, SPKI
942
+ deviceName: 'iPhone 15',
943
+ metadata: { yourDeviceString: '...' }, // optional, opaque to the package
944
+ realm, // required with realms enabled; the credential is bound to it
945
+ });
946
+
947
+ // 2. Request a challenge. No user id — the caller is usually signed out.
948
+ const challenge = await biometricAuth.requestChallenge(credentialId, {
949
+ ipAddress: req.ip,
950
+ });
951
+ if (!challenge) { /* rate limited */ }
952
+
953
+ // 3. The client signs `challenge.challenge` (base64) with the private key.
954
+
955
+ // 4. Verify and receive a session.
956
+ const session = await biometricAuth.authenticateWithBiometric(
957
+ { challengeId: challenge.challengeId, credentialId, signature },
958
+ { res, realm, ipAddress: req.ip },
959
+ );
960
+ ```
961
+
962
+ Also `listCredentials(userId)` for a device list and `removeCredential(userId,
963
+ credentialId)` to deactivate one.
964
+
965
+ #### What the package guarantees
966
+
967
+ | | |
968
+ |---|---|
969
+ | Challenge | 32 random bytes, single use, 60s default |
970
+ | Replay | A challenge is consumed **atomically** before anything is checked, so a retry loses the race — and a failed attempt still burns it |
971
+ | Cross-credential | A challenge issued for one credential cannot be answered with another's key |
972
+ | Realm | A credential signs in only to the realm it was enrolled in (since 0.15.0). With realms enabled, `enrolCredential` and `authenticateWithBiometric` require `realm` |
973
+ | Algorithm | Pinned **per credential**. A credential enrolled as ES256 is only verified as ES256; no verifier for its algorithm means refusal, not a fallback |
974
+ | Account status | The session comes from the same sink as every other login, so a suspended or soft-deleted user is refused **after** a valid signature |
975
+ | Enumeration | Every biometric failure returns the same 401, and a challenge is issued even for a credential that does not exist |
976
+ | Rate limiting | Per credential, and per IP so varying the credential id cannot evade it |
977
+
978
+ #### Implementing `IBiometricRepository`
979
+
980
+ One method has a contract you cannot satisfy with a read followed by a write:
981
+
982
+ ```typescript
983
+ async consumeChallenge(challengeId: string) {
984
+ // Atomic: mark used and return it, only if it was unused and unexpired.
985
+ const { count } = await prisma.biometricChallenge.updateMany({
986
+ where: { id: challengeId, used: false, expiresAt: { gt: new Date() } },
987
+ data: { used: true, usedAt: new Date() },
988
+ });
989
+ if (count !== 1) return null; // someone else claimed it, or it expired
990
+ return this.load(challengeId);
991
+ }
992
+ ```
993
+
994
+ Read-then-write lets two concurrent requests with one challenge both succeed,
995
+ which is the replay single use exists to prevent.
996
+
997
+ Two more things your schema needs to know:
998
+
999
+ - **Persist `realm` on the credential** and return it from `getCredential`. A
1000
+ credential without its realm, while realms are enabled, never authenticates.
1001
+ - **`credentialId` on a challenge is not a foreign key.** A challenge is stored
1002
+ for credential ids that do not exist, so that the signed-out request cannot be
1003
+ used to discover which credentials are enrolled.
1004
+ - **No uniqueness on `deviceName` or any device string.** It is a display label,
1005
+ never matched on. Key rotation is enrol-new then deactivate-old.
1006
+
1007
+ `getCredential` must return deactivated credentials rather than hiding them — the
1008
+ package decides, so that a deactivated credential and an unknown one produce the
1009
+ same answer.
1010
+
1011
+ **There is no no-op fallback.** Omit `biometricRepositoryInstance` and the
1012
+ biometric services are not registered at all, rather than silently accepting
1013
+ enrolments that can never authenticate.
1014
+
861
1015
  ### Brute Force Protection
862
1016
 
863
1017
  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.
@@ -962,6 +1116,78 @@ async changePassword(
962
1116
 
963
1117
  `issueAuthSession(userId, res?, realm?)` mints a new access + refresh pair, persists the refresh token, and (when `features.cookieAuth` is enabled and `res` is supplied) sets the auth cookies on the response. The caller is responsible for verifying the user's identity — `issueAuthSession` takes a `userId` and trusts it. Pair it only with flows that have already authenticated the user (`changePassword`, MFA enrollment completion, admin-impersonation reissue, etc.). Emits `SecurityEvent.SESSION_ISSUED`.
964
1118
 
1119
+ ### Password Reset
1120
+
1121
+ A reset is two calls: `performRequestPasswordReset` sends a 6-digit code, and
1122
+ `performResetPassword` redeems it. `performChangePassword` is the separate,
1123
+ authenticated path for a signed-in user who knows their current password.
1124
+
1125
+ ```typescript
1126
+ @Mutation(() => PasswordResetResponse)
1127
+ requestPasswordReset(@Args('input') input: RequestPasswordResetInput, @Context() ctx: any) {
1128
+ return this.performRequestPasswordReset(input, ctx);
1129
+ }
1130
+
1131
+ @Mutation(() => PasswordResetResponse)
1132
+ resetPassword(@Args('input') input: ResetPasswordInput, @Context() ctx: any) {
1133
+ return this.performResetPassword(input, ctx);
1134
+ }
1135
+ ```
1136
+
1137
+ It needs `verificationRepositoryInstance` to store the code and
1138
+ `emailServiceInstance` to deliver it. Without the first, codes are logged rather
1139
+ than stored and no reset can complete.
1140
+
1141
+ #### What the package enforces for you
1142
+
1143
+ None of this is configurable, and most of it is invisible until it bites:
1144
+
1145
+ | | |
1146
+ |---|---|
1147
+ | Code | 6 digits, HMAC-SHA256 hashed at rest, never logged |
1148
+ | Expiry | 15 minutes |
1149
+ | Attempts | **3**, then the code is destroyed and a new one must be requested |
1150
+ | Per-user cooldown | 60 seconds, tracked on `passwordResetSentAt` |
1151
+ | Per-IP limit | 5 per hour, checked **before** the user lookup |
1152
+ | On success | **every refresh token for that user is revoked** — all devices are signed out |
1153
+ | Comparison | constant-time |
1154
+
1155
+ The IP limit runs before the lookup deliberately: checking it afterwards would
1156
+ make a request for a non-existent address measurably faster than one for a real
1157
+ address, which is the enumeration leak the generic message exists to prevent.
1158
+
1159
+ #### The response never tells you what happened
1160
+
1161
+ `requestPasswordReset` returns the same success payload whether the address does
1162
+ not exist, belongs to a social-login account, or was actually sent a code. That
1163
+ is deliberate — the mutation is reachable unauthenticated, so any distinguishable
1164
+ result is an oracle for whether an address is registered.
1165
+
1166
+ If you need to know, read your `IAuthLogger`. A skip is reported as
1167
+ `PASSWORD_RESET_REQUESTED` with `result: 'SKIPPED_SOCIAL_ONLY'` and the user ID.
1168
+ An operator tool that creates accounts and invites by reset should assert on that
1169
+ event, not on the mutation's return value.
1170
+
1171
+ #### Accounts with no password
1172
+
1173
+ An account is refused only if it has an actual social identity — `googleId`,
1174
+ `facebookId` or `appleId`. A password-less account with none of those, which is
1175
+ how an operator-provisioned account starts, receives a code and can redeem it.
1176
+ This is the documented "invite by password reset" flow.
1177
+
1178
+ **This changed in 0.12.0.** Before then the test was `passwordHash == null`, so
1179
+ operator-created accounts were skipped silently and could never sign in. See
1180
+ [Migrating to v0.12.0](#migrating-to-v0120) for the consequence worth deciding
1181
+ on. The predicate is exported as `hasSocialIdentity(user)` if you want the same
1182
+ test in your own code.
1183
+
1184
+ #### Token mode
1185
+
1186
+ With `features.verificationMode: 'token'` the email carries a link rather than a
1187
+ code, and `performResetPassword` takes the token lifted from that link in place
1188
+ of the 6-digit code. Everything above still applies. See
1189
+ [Verification Modes](#verification-modes).
1190
+
965
1191
  ### Password Policy
966
1192
 
967
1193
  ```typescript
@@ -1199,7 +1425,40 @@ When `email` is provided, it takes precedence over `emailServiceInstance`.
1199
1425
  |--------|------|---------|-------------|
1200
1426
  | `tokenLength` | `number` | `64` | Token length in bytes (token mode) |
1201
1427
  | `tokenExpiresInMinutes` | `number` | `60` | Token expiration (token mode) |
1202
- | `baseUrl` | `string \| (realm?: string) => string` | — | Absolute http(s) base URL for verification/reset links, or a resolver called with the request's realm. **Required when `verificationMode` is `'token'`** (validated at boot); optional in code mode |
1428
+ | `baseUrl` | `string \| VerificationBaseUrlResolver` | — | Absolute http(s) base URL for verification/reset links, or a resolver called with the request's realm. **Required when `verificationMode` is `'token'`** (validated at boot); optional in code mode |
1429
+
1430
+ ### SendGrid Options (`sendgrid`)
1431
+
1432
+ Read only by the bundled `SendGridEmailService`. A custom `IEmailService` ignores
1433
+ this block entirely.
1434
+
1435
+ | Option | Type | Required | Description |
1436
+ |--------|------|----------|-------------|
1437
+ | `apiKey` | `string` | yes | SendGrid API key |
1438
+ | `fromEmail` | `string` | yes | Verified sender address |
1439
+ | `fromName` | `string` | — | Display name on the From header |
1440
+
1441
+ ### Twilio Options (`twilio`)
1442
+
1443
+ Read only by the bundled `TwilioSmsService`. A custom `ISmsService` ignores this
1444
+ block entirely.
1445
+
1446
+ | Option | Type | Required | Description |
1447
+ |--------|------|----------|-------------|
1448
+ | `accountSid` | `string` | yes | Twilio account SID |
1449
+ | `authToken` | `string` | yes | Twilio auth token |
1450
+ | `phoneNumber` | `string` | yes | Sending number, E.164 |
1451
+
1452
+ ### Biometric Options (`biometric`)
1453
+
1454
+ Read only when `biometricRepositoryInstance` is provided. Passing this block
1455
+ without it is reported at boot, not refused.
1456
+
1457
+ | Option | Type | Default | Description |
1458
+ |--------|------|---------|-------------|
1459
+ | `verifiers` | `IBiometricVerifier[]` | `[new Es256DeviceKeyVerifier()]` | One per credential algorithm. Two claiming the same algorithm is refused at boot — resolution would depend on array order |
1460
+ | `challengeExpirySeconds` | `number` | `60` | Short on purpose: it bounds replay |
1461
+ | `challengeRateLimit` | `{ maxAttempts, windowMs }` | `5 / 60s` | Per credential. **Requires `rateLimiterInstance`** — the same guardrail as `bruteForce.ipRateLimit`, because per-replica counters would make the limit silently looser. A per-IP limit at ten times this bound also applies when you pass `ipAddress` |
1203
1462
 
1204
1463
  ### Optional Instance Options
1205
1464
 
@@ -1343,6 +1602,31 @@ export class CustomGuard implements CanActivate {
1343
1602
  - **API keys with scopes and expiry**, refused hard when they carry your prefix and match no key, so no other strategy accepts them
1344
1603
  - **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.
1345
1604
 
1605
+ ## Upgrading from 0.9.x
1606
+
1607
+ Four releases separate 0.9 from 0.14 and each has its own note below. If you are
1608
+ jumping the whole way, this is the order things will bite, and the short answer
1609
+ for each.
1610
+
1611
+ | Release | What you have to do |
1612
+ |---|---|
1613
+ | **0.10.0** | Usually nothing. If you relied on `['api-key','jwt']` rejecting JWTs, it no longer does — an unknown key now fails softly so the next strategy runs |
1614
+ | **0.11.0** | `getRequestFromContext` can return `undefined`; TypeScript flags every call that assumed otherwise. If you use subscriptions with cookie auth, set `csrf.webSocket` |
1615
+ | **0.12.0** | The `jwt` strategy pins HS256 — check anything minting staff tokens elsewhere. Security events are logged once instead of twice, so counts built on them halve. Password reset now reaches password-less accounts with no social identity |
1616
+ | **0.13.0** | Nothing — and it was never published separately; its changes ship inside 0.14.0 |
1617
+ | **0.14.0** | Biometric authentication is rebuilt and breaking. If you never wired `biometricRepositoryInstance`, nothing changes. If you did, read the security note in the changelog first |
1618
+
1619
+ Two things that are easy to miss because nothing errors:
1620
+
1621
+ - **Security-event counts.** 0.12.0 stopped double-logging `SIGNUP_SUCCESS`,
1622
+ `LOGIN_SUCCESS` and `LOGOUT_SUCCESS`. Dashboards built before it were reading
1623
+ double and will step down on upgrade rather than when you fix the data.
1624
+ - **`verification.baseUrl`.** If you run realms and serve more than one frontend,
1625
+ it accepts a resolver from 0.13.0. Before that, every realm got the same host.
1626
+
1627
+ Take them in order rather than jumping straight to 0.14.0 — each note assumes the
1628
+ one before it.
1629
+
1346
1630
  ## Migrating to v0.12.0
1347
1631
 
1348
1632
  **Password reset now reaches accounts that have no password and no social login.** The guard on `requestPasswordReset` and `resetPassword` tested `passwordHash == null` and treated it as "this user signed in with Google". That is also how an operator-provisioned account starts, so an account created by an admin CLI was skipped silently — the request returned the same success message as a real send, and the reset would have been refused even if a code had arrived. Both guards now test for an actual social identity (`googleId`, `facebookId` or `appleId`), so a social-only account behaves exactly as before, and a password-less account with no social identity receives a reset code and can redeem it.
@@ -14,6 +14,7 @@ import { ITenantRepository } from './interfaces/tenant-repository.interface';
14
14
  import { ITenantExtractor } from './interfaces/tenant-extractor.interface';
15
15
  import { IRealmExtractor } from './interfaces/realm-extractor.interface';
16
16
  import { IResourcePermissionRepository } from './interfaces/resource-permission-repository.interface';
17
+ import { IBiometricVerifier } from './interfaces/biometric-verifier.interface';
17
18
  import { IJwtPayloadFactory } from './interfaces/jwt-payload-factory.interface';
18
19
  import { IApiKeyRepository } from './interfaces/api-key-repository.interface';
19
20
  import { PasswordPolicyConfig } from './interfaces/password-policy-config.interface';
@@ -121,6 +122,14 @@ export interface AuthModuleOptions {
121
122
  branding?: EmailBrandingConfig;
122
123
  templateRenderer?: IEmailTemplateRenderer;
123
124
  };
125
+ biometric?: {
126
+ verifiers?: IBiometricVerifier[];
127
+ challengeExpirySeconds?: number;
128
+ challengeRateLimit?: {
129
+ maxAttempts: number;
130
+ windowMs: number;
131
+ };
132
+ };
124
133
  verification?: {
125
134
  tokenLength?: number;
126
135
  tokenExpiresInMinutes?: number;
@@ -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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"}