@ambushsoftworks/nestjs-auth-graphql 0.7.2 → 0.9.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +270 -9
- package/dist/auth.module.d.ts +7 -0
- package/dist/auth.module.d.ts.map +1 -1
- package/dist/auth.module.js +53 -0
- package/dist/auth.module.js.map +1 -1
- package/dist/exceptions/invalid-auth-config.exception.d.ts +4 -0
- package/dist/exceptions/invalid-auth-config.exception.d.ts.map +1 -0
- package/dist/exceptions/invalid-auth-config.exception.js +12 -0
- package/dist/exceptions/invalid-auth-config.exception.js.map +1 -0
- package/dist/guards/tenant.guard.d.ts.map +1 -1
- package/dist/guards/tenant.guard.js +9 -1
- package/dist/guards/tenant.guard.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/auth-lifecycle-hooks.interface.d.ts +1 -0
- package/dist/interfaces/auth-lifecycle-hooks.interface.d.ts.map +1 -1
- package/dist/interfaces/auth-logger.interface.d.ts +2 -0
- package/dist/interfaces/auth-logger.interface.d.ts.map +1 -1
- package/dist/interfaces/auth-logger.interface.js +2 -0
- package/dist/interfaces/auth-logger.interface.js.map +1 -1
- package/dist/interfaces/brute-force-repository.interface.d.ts +1 -0
- package/dist/interfaces/brute-force-repository.interface.d.ts.map +1 -1
- package/dist/interfaces/email-service.interface.d.ts +2 -6
- package/dist/interfaces/email-service.interface.d.ts.map +1 -1
- package/dist/interfaces/email-template-renderer.interface.d.ts +2 -6
- package/dist/interfaces/email-template-renderer.interface.d.ts.map +1 -1
- package/dist/interfaces/realm-extractor.interface.d.ts +1 -1
- package/dist/interfaces/realm-extractor.interface.d.ts.map +1 -1
- package/dist/interfaces/tenant-extractor.interface.d.ts +1 -1
- package/dist/interfaces/tenant-extractor.interface.d.ts.map +1 -1
- package/dist/middleware/realm.middleware.d.ts +2 -1
- package/dist/middleware/realm.middleware.d.ts.map +1 -1
- package/dist/middleware/realm.middleware.js +15 -5
- package/dist/middleware/realm.middleware.js.map +1 -1
- package/dist/repositories/noop-brute-force.repository.d.ts +1 -0
- package/dist/repositories/noop-brute-force.repository.d.ts.map +1 -1
- package/dist/repositories/noop-brute-force.repository.js +4 -0
- package/dist/repositories/noop-brute-force.repository.js.map +1 -1
- package/dist/resolvers/base-auth.resolver.d.ts +1 -0
- package/dist/resolvers/base-auth.resolver.d.ts.map +1 -1
- package/dist/resolvers/base-auth.resolver.js +12 -2
- package/dist/resolvers/base-auth.resolver.js.map +1 -1
- package/dist/services/auth.service.d.ts +9 -0
- package/dist/services/auth.service.d.ts.map +1 -1
- package/dist/services/auth.service.js +34 -11
- package/dist/services/auth.service.js.map +1 -1
- package/dist/services/brute-force-protection.service.d.ts +12 -3
- package/dist/services/brute-force-protection.service.d.ts.map +1 -1
- package/dist/services/brute-force-protection.service.js +70 -6
- package/dist/services/brute-force-protection.service.js.map +1 -1
- package/dist/services/configurable-email.service.d.ts +3 -7
- package/dist/services/configurable-email.service.d.ts.map +1 -1
- package/dist/services/configurable-email.service.js +16 -60
- package/dist/services/configurable-email.service.js.map +1 -1
- package/dist/services/default-email-template-renderer.d.ts +4 -7
- package/dist/services/default-email-template-renderer.d.ts.map +1 -1
- package/dist/services/default-email-template-renderer.js +40 -55
- package/dist/services/default-email-template-renderer.js.map +1 -1
- package/dist/services/noop-email-sender.d.ts +2 -0
- package/dist/services/noop-email-sender.d.ts.map +1 -1
- package/dist/services/noop-email-sender.js +28 -0
- package/dist/services/noop-email-sender.js.map +1 -1
- package/dist/services/noop-email.service.d.ts +2 -6
- package/dist/services/noop-email.service.d.ts.map +1 -1
- package/dist/services/noop-email.service.js +9 -17
- package/dist/services/noop-email.service.js.map +1 -1
- package/dist/services/sendgrid-email.service.d.ts +0 -13
- package/dist/services/sendgrid-email.service.d.ts.map +1 -1
- package/dist/services/sendgrid-email.service.js +4 -528
- package/dist/services/sendgrid-email.service.js.map +1 -1
- package/dist/services/verification.service.d.ts +15 -0
- package/dist/services/verification.service.d.ts.map +1 -1
- package/dist/services/verification.service.js +70 -3
- package/dist/services/verification.service.js.map +1 -1
- package/dist/test-utils/index.d.ts +1 -1
- package/dist/test-utils/index.d.ts.map +1 -1
- package/dist/test-utils/index.js +2 -1
- package/dist/test-utils/index.js.map +1 -1
- package/dist/test-utils/mock-repositories.d.ts.map +1 -1
- package/dist/test-utils/mock-repositories.js +1 -0
- package/dist/test-utils/mock-repositories.js.map +1 -1
- package/dist/test-utils/mock-services.d.ts +2 -0
- package/dist/test-utils/mock-services.d.ts.map +1 -1
- package/dist/test-utils/mock-services.js +10 -4
- package/dist/test-utils/mock-services.js.map +1 -1
- package/dist/utils/ip-hash.d.ts +2 -0
- package/dist/utils/ip-hash.d.ts.map +1 -0
- package/dist/utils/ip-hash.js +11 -0
- package/dist/utils/ip-hash.js.map +1 -0
- package/examples/full-resolver-template.ts +311 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -35,6 +35,7 @@ Production-grade authentication module for NestJS GraphQL + REST APIs. JWT, cook
|
|
|
35
35
|
- [Guards](#guards)
|
|
36
36
|
- [Utilities](#utilities)
|
|
37
37
|
- [Security Features](#security-features)
|
|
38
|
+
- [Migrating to v0.9.0](#migrating-to-v090)
|
|
38
39
|
- [License](#license)
|
|
39
40
|
|
|
40
41
|
---
|
|
@@ -166,8 +167,12 @@ export class AuthResolver extends BaseAuthResolver<User> {
|
|
|
166
167
|
}
|
|
167
168
|
|
|
168
169
|
@Mutation(() => LogoutResponse)
|
|
169
|
-
async logout(
|
|
170
|
-
|
|
170
|
+
async logout(
|
|
171
|
+
@Args('input') input: LogoutInput,
|
|
172
|
+
@CurrentUser() user: User,
|
|
173
|
+
@Context() ctx: any,
|
|
174
|
+
) {
|
|
175
|
+
return this.performLogout(input, user, ctx);
|
|
171
176
|
}
|
|
172
177
|
}
|
|
173
178
|
```
|
|
@@ -207,6 +212,8 @@ TypeScript decorator metadata is not preserved when importing from compiled npm
|
|
|
207
212
|
|
|
208
213
|
When cookie auth is enabled, pass the GraphQL `context` so tokens are set as HttpOnly cookies. The response body will contain empty strings for `accessToken`/`refreshToken`.
|
|
209
214
|
|
|
215
|
+
**Reference template:** `examples/full-resolver-template.ts` ships in the published package and covers every `perform*` method with copy-paste-ready `@Mutation`/`@Query` decorators, `@Throttle` on auth-sensitive endpoints, and `@UseGuards(JwtAuthGuard)` on authenticated routes. It also demonstrates the `changePassword` + `issueAuthSession` keep-alive pattern. Each `perform*` method on `BaseAuthResolver` also has a method-specific JSDoc snippet you can lift directly.
|
|
216
|
+
|
|
210
217
|
### Available `perform*()` methods
|
|
211
218
|
|
|
212
219
|
**Authentication:**
|
|
@@ -275,7 +282,9 @@ When `useHostPrefix: true`, cookie names become `__Host-access_token` and `__Hos
|
|
|
275
282
|
|
|
276
283
|
You can also customize cookie names and max ages -- see [Cookie Options](#cookie-options-cookie).
|
|
277
284
|
|
|
278
|
-
The JWT strategy automatically reads from cookies first, then falls back to `Authorization: Bearer` headers.
|
|
285
|
+
The JWT strategy automatically reads from cookies first, then falls back to `Authorization: Bearer` headers. `performRefreshToken` and `performLogout` are symmetric: when `cookieAuth` is on, they accept an empty `input.refreshToken` and pull the token from the refresh cookie instead. Browser SPAs cannot read HttpOnly cookies, so they should send `{ refreshToken: "" }` and rely on the credentialed request carrying the cookie. **Your consumer DTO must allow the empty value** — mark `refreshToken` `@IsOptional()` (or `@IsString()` without `@IsNotEmpty()`) in your `RefreshTokenInput` and `LogoutInput` when targeting browser cookie auth. Non-browser clients (mobile/CLI) keep passing the token explicitly and that path takes precedence.
|
|
286
|
+
|
|
287
|
+
> **Note:** Reading cookies requires `cookie-parser` middleware (or the Fastify cookie plugin) to populate `req.cookies`. The JWT strategy depends on this already; the same setup serves refresh/logout.
|
|
279
288
|
|
|
280
289
|
#### CSRF Protection
|
|
281
290
|
|
|
@@ -364,6 +373,8 @@ const extractor = new HeaderTenantExtractor();
|
|
|
364
373
|
const extractor = new HeaderTenantExtractor('x-org-id');
|
|
365
374
|
```
|
|
366
375
|
|
|
376
|
+
`extractTenantId` may also return `Promise<string | null>`. `TenantGuard` awaits the result with no microtask overhead for sync returns. Async throws and rejected Promises are treated identically to a sync `null` return — the request is rejected with `400 Bad Request` and the internal cause is logged via Nest's `Logger` (never leaked to the client).
|
|
377
|
+
|
|
367
378
|
**Access tenant context** in resolvers:
|
|
368
379
|
|
|
369
380
|
```typescript
|
|
@@ -422,6 +433,22 @@ export class HostnameRealmExtractor implements IRealmExtractor {
|
|
|
422
433
|
}
|
|
423
434
|
```
|
|
424
435
|
|
|
436
|
+
**Async extractor (DB-backed lookup):** `extractRealm` may also return `Promise<string | null>`. `RealmMiddleware` awaits the result with no microtask overhead for sync returns (`instanceof Promise` gate). Async throws and rejected Promises are treated identically to a sync `null` return — the request is rejected with `400 Bad Request` and the internal cause is logged via Nest's `Logger` (never leaked to the client). The same widening applies to `ITenantExtractor.extractTenantId`.
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
@Injectable()
|
|
440
|
+
export class DbRealmExtractor implements IRealmExtractor {
|
|
441
|
+
constructor(private readonly prisma: PrismaService) {}
|
|
442
|
+
|
|
443
|
+
async extractRealm(request: Record<string, any>): Promise<string | null> {
|
|
444
|
+
const host = request.headers?.host;
|
|
445
|
+
if (!host) return null;
|
|
446
|
+
const site = await this.prisma.site.findUnique({ where: { hostname: host } });
|
|
447
|
+
return site ? `site:${site.slug}` : null;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
425
452
|
**Register the module with realm support:**
|
|
426
453
|
|
|
427
454
|
```typescript
|
|
@@ -524,6 +551,23 @@ AuthModule.forRootAsync({
|
|
|
524
551
|
|
|
525
552
|
`email.branding` is required when using the default template renderer. If you provide a custom `email.templateRenderer`, branding can be omitted.
|
|
526
553
|
|
|
554
|
+
**What the package sends.** Exactly three emails, each fired directly by an auth
|
|
555
|
+
flow the package owns:
|
|
556
|
+
|
|
557
|
+
| Email | Fired by |
|
|
558
|
+
|---|---|
|
|
559
|
+
| Verification | `signup`, `resendVerificationEmail` |
|
|
560
|
+
| Password reset | `requestPasswordReset` |
|
|
561
|
+
| Password changed | `resetPassword`, `changePassword` |
|
|
562
|
+
|
|
563
|
+
Everything else — welcome, account-locked, account-linked/unlinked,
|
|
564
|
+
login-from-new-device — is **consumer-owned**, wired from [lifecycle
|
|
565
|
+
hooks](#lifecycle-hooks). The package does not fire them, so it does not
|
|
566
|
+
declare them on `IEmailService`. The branded templates for several of them are
|
|
567
|
+
still available on `IEmailTemplateRenderer` (`renderWelcomeEmail`,
|
|
568
|
+
`renderAccountLockedEmail`, `renderEmailChangedEmail`), so a hook can render
|
|
569
|
+
with the package's styling and hand the result to your own `IEmailSender`.
|
|
570
|
+
|
|
527
571
|
**Built-in senders:** `SendGridEmailSender`, `ResendEmailSender`, `NoOpEmailSender`
|
|
528
572
|
|
|
529
573
|
**Custom sender:** Implement `IEmailSender`:
|
|
@@ -556,18 +600,72 @@ email: {
|
|
|
556
600
|
|
|
557
601
|
Two modes for email verification and password reset:
|
|
558
602
|
|
|
559
|
-
- **`'code'`** (default) -- 6-digit numeric codes
|
|
560
|
-
- **`'token'`** --
|
|
603
|
+
- **`'code'`** (default) -- 6-digit numeric codes, entered by the user
|
|
604
|
+
- **`'token'`** -- high-entropy tokens delivered as a clickable link
|
|
561
605
|
|
|
562
606
|
```typescript
|
|
563
607
|
features: { verificationMode: 'token' },
|
|
564
608
|
verification: {
|
|
565
|
-
baseUrl: 'https://app.example.com',
|
|
609
|
+
baseUrl: 'https://app.example.com', // REQUIRED in token mode
|
|
566
610
|
tokenLength: 64, // bytes, default: 64
|
|
567
611
|
tokenExpiresInMinutes: 60, // default: 60
|
|
568
612
|
},
|
|
569
613
|
```
|
|
570
614
|
|
|
615
|
+
| | `'code'` | `'token'` |
|
|
616
|
+
|---|---|---|
|
|
617
|
+
| Credential | 6-digit code | `tokenLength`-byte hex token |
|
|
618
|
+
| Delivered as | code in the email body | link, no readable code |
|
|
619
|
+
| Default expiry | 15 minutes | 60 minutes |
|
|
620
|
+
| Hashing | HMAC-SHA256 | SHA-256 |
|
|
621
|
+
| Attempts before invalidation | 3 | 3 |
|
|
622
|
+
| `verification.baseUrl` | optional | **required** |
|
|
623
|
+
|
|
624
|
+
**The modes differ in where the secret lives, not just in formatting.** In
|
|
625
|
+
token mode the credential *is* the link — it travels in the query string and
|
|
626
|
+
the email body contains no readable code. In code mode the credential travels
|
|
627
|
+
in the body and the link is a bare page URL carrying no secret at all, so it
|
|
628
|
+
never reaches browser history, referrer headers, or proxy logs. This is why
|
|
629
|
+
code mode does not simply put the 6-digit code into the URL, and why the
|
|
630
|
+
package refuses to render both in one email.
|
|
631
|
+
|
|
632
|
+
**Link format (token mode).** The package builds the URL and both query
|
|
633
|
+
parameters; you supply only `baseUrl`:
|
|
634
|
+
|
|
635
|
+
```
|
|
636
|
+
https://app.example.com/verify-email?token=<token>&email=<email>
|
|
637
|
+
https://app.example.com/reset-password?token=<token>&email=<email>
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
Your frontend reads both parameters and passes them to `verifyEmail` /
|
|
641
|
+
`resetPassword` — validation is per user keyed on email, so a link missing
|
|
642
|
+
either parameter cannot be redeemed.
|
|
643
|
+
|
|
644
|
+
**`baseUrl` is required in token mode**, enforced at boot with
|
|
645
|
+
`InvalidAuthConfigException`. Token-mode emails contain no readable credential,
|
|
646
|
+
so a missing base URL would produce mail that delivers successfully and cannot
|
|
647
|
+
be acted on. Failing at startup makes that a deploy-time error instead of a
|
|
648
|
+
support ticket.
|
|
649
|
+
|
|
650
|
+
In `'code'` mode `baseUrl` stays optional. When set, it is used for the bare
|
|
651
|
+
page link; otherwise the package falls back to the `FRONTEND_URL` environment
|
|
652
|
+
variable (deprecated — logged once) and finally to a placeholder. The code
|
|
653
|
+
itself is never placed in a query string in either mode.
|
|
654
|
+
|
|
655
|
+
**Custom `IEmailService` implementations.** Both send methods receive the
|
|
656
|
+
credential and its URL, and the mode determines which one is actionable:
|
|
657
|
+
|
|
658
|
+
```typescript
|
|
659
|
+
sendVerificationEmail(email, code, expiresInMinutes, verificationUrl?)
|
|
660
|
+
sendPasswordResetEmail(email, resetToken, resetUrl, expiresInMinutes?)
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
In token mode, render `verificationUrl` / `resetUrl` — never the raw token,
|
|
664
|
+
which the recipient cannot type. In code mode, render the code and omit the
|
|
665
|
+
link. `expiresInMinutes` reflects the mode's real expiry, so render it rather
|
|
666
|
+
than hardcoding a duration. Both trailing parameters are optional, so existing
|
|
667
|
+
implementations continue to compile.
|
|
668
|
+
|
|
571
669
|
### OAuth
|
|
572
670
|
|
|
573
671
|
Google and Facebook OAuth with encrypted token storage (AES-256-GCM).
|
|
@@ -599,6 +697,104 @@ The `OAuthController` is automatically registered and handles OAuth callback rou
|
|
|
599
697
|
|
|
600
698
|
When `features.bruteForceProtection` is enabled and a `bruteForceRepositoryInstance` is provided, the module tracks failed login attempts and temporarily locks accounts. The lockout policy (attempt thresholds, lockout duration) is determined by your `IBruteForceRepository` implementation.
|
|
601
699
|
|
|
700
|
+
#### IP Rate Limiting
|
|
701
|
+
|
|
702
|
+
`BruteForceProtectionService.checkIpRateLimit(ipAddress, realm?)` enforces a sliding-window rate limit per IP. Opt in by setting `bruteForce.ipRateLimit`:
|
|
703
|
+
|
|
704
|
+
```typescript
|
|
705
|
+
import { RedisRateLimiter } from './redis-rate-limiter'; // your implementation
|
|
706
|
+
|
|
707
|
+
AuthModule.forRootAsync({
|
|
708
|
+
inject: [RedisRateLimiter],
|
|
709
|
+
useFactory: (redisRateLimiter) => ({
|
|
710
|
+
// ...required options
|
|
711
|
+
rateLimiterInstance: redisRateLimiter, // REQUIRED with ipRateLimit
|
|
712
|
+
bruteForce: {
|
|
713
|
+
ipRateLimit: { maxAttempts: 10, windowMs: 60_000 }, // 10 per minute per IP
|
|
714
|
+
},
|
|
715
|
+
}),
|
|
716
|
+
})
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
When `bruteForce.ipRateLimit` is configured, `AuthModule.forRootAsync` requires an explicit `rateLimiterInstance` and throws `InvalidAuthConfigException` at boot otherwise:
|
|
720
|
+
|
|
721
|
+
```
|
|
722
|
+
[AuthModule] bruteForce.ipRateLimit is configured but rateLimiterInstance is undefined.
|
|
723
|
+
The default InMemoryRateLimiterService is unsafe for multi-replica deployments
|
|
724
|
+
(per-replica counters do not coordinate) and is not auto-applied for IP rate limiting.
|
|
725
|
+
Provide rateLimiterInstance explicitly: a Redis-backed implementation for production,
|
|
726
|
+
or `new InMemoryRateLimiterService()` for single-replica deployments / dev.
|
|
727
|
+
To disable IP rate limiting, remove the bruteForce.ipRateLimit slot.
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
`checkIpRateLimit` reuses the same `IRateLimiter` infrastructure as password-reset throttling — implement once, reuse everywhere. For multi-replica production, back it with Redis (the same pattern documented for the `RefreshTokenService` cache applies). For single-replica or dev, `new InMemoryRateLimiterService()` is acceptable.
|
|
731
|
+
|
|
732
|
+
Threshold is inclusive: with `maxAttempts: 5`, the user is blocked starting on the 5th attempt within the window (record-then-check ordering means `attempts >= maxAttempts` returns `true`). Tune your config accordingly — `maxAttempts: 5` is the first blocked attempt, not the first allowed-over.
|
|
733
|
+
|
|
734
|
+
When realms are enabled, rate-limit keys are namespaced as `auth:ipRate:<realm>:<ipHash>` so a single shared store serves many realms without collisions. IPs are hashed via `hashIp(ip, salt)` (HMAC-SHA256, 16-char hex prefix) before becoming keys; raw IPs are never written to the store or to logs. The salt is resolved once at service construction with the following priority:
|
|
735
|
+
|
|
736
|
+
1. `AUTH_IP_HASH_SALT` env var (recommended — dedicated, rotatable secret)
|
|
737
|
+
2. `options.jwtSecret` (fallback; one-time INFO log when `bruteForce.ipRateLimit` is configured)
|
|
738
|
+
3. Throws `InvalidAuthConfigException` if `bruteForce.ipRateLimit` is configured but neither salt source is available
|
|
739
|
+
|
|
740
|
+
Note: the salt is read from `AuthModuleOptions` (not from `process.env.JWT_SECRET` directly), so consumers wiring their JWT secret via `@nestjs/config` / a secrets manager work correctly without populating `process.env.JWT_SECRET`.
|
|
741
|
+
|
|
742
|
+
When the limit is exceeded, `SecurityEvent.IP_RATE_LIMIT_EXCEEDED` is logged. The method returns `boolean` and never throws — callers branch and produce their own error envelope (typically HTTP 429).
|
|
743
|
+
|
|
744
|
+
#### Brute-Force Cleanup
|
|
745
|
+
|
|
746
|
+
`BruteForceProtectionService.cleanupOldAttempts(olderThan: Date): Promise<number>` deletes failed-attempt records older than the cutoff and returns the deletion count. The package does NOT ship a scheduler — wire one in your application:
|
|
747
|
+
|
|
748
|
+
```typescript
|
|
749
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
750
|
+
import { Cron } from '@nestjs/schedule';
|
|
751
|
+
import { BruteForceProtectionService } from '@ambushsoftworks/nestjs-auth-graphql';
|
|
752
|
+
|
|
753
|
+
@Injectable()
|
|
754
|
+
export class BruteForceCleanupJob {
|
|
755
|
+
private readonly logger = new Logger(BruteForceCleanupJob.name);
|
|
756
|
+
|
|
757
|
+
constructor(private readonly bruteForce: BruteForceProtectionService) {}
|
|
758
|
+
|
|
759
|
+
@Cron('0 3 * * *') // daily at 03:00
|
|
760
|
+
async cleanup() {
|
|
761
|
+
const ninetyDaysAgo = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000);
|
|
762
|
+
const deleted = await this.bruteForce.cleanupOldAttempts(ninetyDaysAgo);
|
|
763
|
+
this.logger.log(`Cleaned ${deleted} old brute-force-attempt rows`);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
This requires `IBruteForceRepository.deleteOlderThan(beforeDate: Date): Promise<number>` on your repository implementation. Reference Prisma implementation: `return (await this.prisma.bruteForceAttempt.deleteMany({ where: { createdAt: { lt: beforeDate } } })).count;`. Multi-replica deployments should coordinate at the cron layer (advisory lock, leader election) or accept the duplicate-delete cost — `DELETE WHERE createdAt < ?` is idempotent.
|
|
769
|
+
|
|
770
|
+
#### Stay Logged In After Password Change
|
|
771
|
+
|
|
772
|
+
`changePassword` revokes every refresh token for the user (logging out all devices). To keep the current device logged in while logging out the others, compose `changePassword` with `issueAuthSession`:
|
|
773
|
+
|
|
774
|
+
```typescript
|
|
775
|
+
@Mutation(() => ChangePasswordResponse)
|
|
776
|
+
@UseGuards(JwtAuthGuard)
|
|
777
|
+
async changePassword(
|
|
778
|
+
@CurrentUser() user: User,
|
|
779
|
+
@Args('input') input: ChangePasswordInput,
|
|
780
|
+
@Context() ctx: any,
|
|
781
|
+
): Promise<ChangePasswordResponse> {
|
|
782
|
+
const result = await this.performChangePassword(
|
|
783
|
+
user.id,
|
|
784
|
+
input.currentPassword,
|
|
785
|
+
input.newPassword,
|
|
786
|
+
);
|
|
787
|
+
// Mint a fresh session for THIS device. Other devices are already revoked.
|
|
788
|
+
// Use the inherited `getResponseFromContext` helper so cookies work under
|
|
789
|
+
// both Apollo (`ctx.res`) and Express/Fastify (`ctx.req.res`).
|
|
790
|
+
const res = this.getResponseFromContext(ctx);
|
|
791
|
+
const tokens = await this.authService.issueAuthSession(user.id, res);
|
|
792
|
+
return { ...result, accessToken: tokens.accessToken, refreshToken: tokens.refreshToken };
|
|
793
|
+
}
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
`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`.
|
|
797
|
+
|
|
602
798
|
### Password Policy
|
|
603
799
|
|
|
604
800
|
```typescript
|
|
@@ -636,10 +832,15 @@ lifecycleHooksInstance: {
|
|
|
636
832
|
async onPasswordChanged(user) { /* notify user */ },
|
|
637
833
|
async onAccountDelete(user) { /* cleanup user data */ },
|
|
638
834
|
onAuthFailure(email, ip, reason) { /* security monitoring */ },
|
|
835
|
+
onAccountLocked(userId, email, ip, lockSeconds, realm) {
|
|
836
|
+
/* send account-locked notification email, alert security tooling */
|
|
837
|
+
},
|
|
639
838
|
}
|
|
640
839
|
```
|
|
641
840
|
|
|
642
|
-
All hooks are optional. Async hooks are awaited but failures are logged and do not block the auth operation. `onAuthFailure`
|
|
841
|
+
All hooks are optional. Async hooks are awaited but failures are logged and do not block the auth operation. `onAuthFailure` and `onAccountLocked` are synchronous (fire-and-forget) — both are security signals and the underlying state (lock recorded, attempt logged) is already persisted by the time the hook fires, so hook failures must not roll it back.
|
|
842
|
+
|
|
843
|
+
`onAccountLocked` fires from `BruteForceProtectionService.recordFailedAttempt` on the transition from "not locked" → "locked". Subsequent failed attempts while the account remains locked do **not** re-fire (use `onAuthFailure` for per-attempt signal). Wire it to send the account-locked email yourself — the package no longer ships a `sendAccountLockedEmail` method on `IEmailService`; notification surface area beyond verification, password-reset, and password-changed is consumer-owned via this hook.
|
|
643
844
|
|
|
644
845
|
### JWT Validation Modes
|
|
645
846
|
|
|
@@ -688,6 +889,7 @@ jwtPayloadFactoryInstance: {
|
|
|
688
889
|
| `nodeEnv` | `string` | `'production'` | Environment identifier |
|
|
689
890
|
| `passwordPolicy` | `PasswordPolicyConfig` | See [Password Policy](#password-policy) | Password strength rules |
|
|
690
891
|
| `encryptionKey` | `string` | -- | 32-byte hex string for AES-256-GCM (OAuth token encryption) |
|
|
892
|
+
| `bruteForce.ipRateLimit` | `{ maxAttempts, windowMs }` | -- | IP rate-limit policy for `checkIpRateLimit`. When set, `rateLimiterInstance` MUST also be provided explicitly (boot fails with `InvalidAuthConfigException` otherwise). See [IP Rate Limiting](#ip-rate-limiting). |
|
|
691
893
|
|
|
692
894
|
### Feature Flags (`features`)
|
|
693
895
|
|
|
@@ -755,7 +957,7 @@ When `email` is provided, it takes precedence over `emailServiceInstance`.
|
|
|
755
957
|
|--------|------|---------|-------------|
|
|
756
958
|
| `tokenLength` | `number` | `64` | Token length in bytes (token mode) |
|
|
757
959
|
| `tokenExpiresInMinutes` | `number` | `60` | Token expiration (token mode) |
|
|
758
|
-
| `baseUrl` | `string` | -- |
|
|
960
|
+
| `baseUrl` | `string` | -- | Absolute http(s) base URL for verification/reset links. **Required when `verificationMode` is `'token'`** (validated at boot); optional in code mode |
|
|
759
961
|
|
|
760
962
|
### Optional Instance Options
|
|
761
963
|
|
|
@@ -773,7 +975,7 @@ When `email` is provided, it takes precedence over `emailServiceInstance`.
|
|
|
773
975
|
| `resourcePermissionRepositoryInstance` | `IResourcePermissionRepository` | `null` |
|
|
774
976
|
| `jwtPayloadFactoryInstance` | `IJwtPayloadFactory` | `DefaultJwtPayloadFactory` |
|
|
775
977
|
| `apiKeyRepositoryInstance` | `IApiKeyRepository` | `null` |
|
|
776
|
-
| `rateLimiterInstance` | `IRateLimiter` | `InMemoryRateLimiterService` |
|
|
978
|
+
| `rateLimiterInstance` | `IRateLimiter` | `InMemoryRateLimiterService` (Note: this fallback does NOT apply when `bruteForce.ipRateLimit` is configured — boot fails with `InvalidAuthConfigException` and an explicit `rateLimiterInstance` is required. See [IP Rate Limiting](#ip-rate-limiting) above.) |
|
|
777
979
|
| `realmExtractorInstance` | `IRealmExtractor` | `null` (realms disabled) |
|
|
778
980
|
|
|
779
981
|
---
|
|
@@ -854,6 +1056,65 @@ export class CustomGuard implements CanActivate {
|
|
|
854
1056
|
- **Separate HMAC secrets** for refresh tokens, verification codes, and OAuth state
|
|
855
1057
|
- **Realm-based identity isolation** with JWT realm claim validation and realm-scoped rate limiting
|
|
856
1058
|
|
|
1059
|
+
## Migrating to v0.9.0
|
|
1060
|
+
|
|
1061
|
+
**Breaking changes — interface surface trim.** The package no longer ships notification methods it never fired on its own. If you implement `IEmailService` or `IEmailTemplateRenderer` directly, remove the methods below; if you use the bundled `ConfigurableEmailService` / `SendGridEmailService` / `NoOpEmailService`, no action is needed.
|
|
1062
|
+
|
|
1063
|
+
`IEmailService` — removed (consumer-owned via lifecycle hooks):
|
|
1064
|
+
|
|
1065
|
+
- `sendAccountLockedEmail` → wire from new `onAccountLocked` hook
|
|
1066
|
+
- `sendWelcomeEmail` → wire from `onSignup` (or `onEmailVerified`)
|
|
1067
|
+
- `sendAccountLinkedEmail` → wire from `onOAuthAccountLinked`
|
|
1068
|
+
- `sendAccountUnlinkedEmail` → wire from your unlink resolver directly
|
|
1069
|
+
|
|
1070
|
+
`IEmailTemplateRenderer` — removed:
|
|
1071
|
+
|
|
1072
|
+
- `renderLoginAlertEmail` — the package never fired a login-from-new-device event. If you want this feature, implement device-fingerprint storage on your side and call your own email sender from a wrapper around `performLogin`.
|
|
1073
|
+
|
|
1074
|
+
**Action required if you use `verificationMode: 'token'`.** Token mode was
|
|
1075
|
+
non-functional before this release — verification and password-reset emails
|
|
1076
|
+
were delivered with links that could not be redeemed. It now works end to end.
|
|
1077
|
+
Three things to check, in the order they are most likely to bite:
|
|
1078
|
+
|
|
1079
|
+
1. **`resetPassword` and `verifyEmail` now receive a token, not a 6-digit
|
|
1080
|
+
code.** With the default `tokenLength: 64`, that is a **128-character hex
|
|
1081
|
+
string** arriving in the field your code-mode flow used for six digits. If
|
|
1082
|
+
your GraphQL input DTO constrains it — `@Length(6)`, `@IsNumberString()`, a
|
|
1083
|
+
narrow column — relax it, or valid tokens will be rejected. Consider a
|
|
1084
|
+
comment on the field noting that its shape is determined by
|
|
1085
|
+
`verificationMode` and must stay unconstrained.
|
|
1086
|
+
|
|
1087
|
+
2. **Replace any direct `generateCode` / `generateToken` call with
|
|
1088
|
+
`issueCredential`.** ⚠️ **This fails at runtime, not at compile time.** If
|
|
1089
|
+
you wrapped the low-level primitives — for example to mint a link yourself
|
|
1090
|
+
in a non-production branch — that code silently assumes one mode. It was
|
|
1091
|
+
harmless while token mode was non-functional, because both settings reached
|
|
1092
|
+
the same path. Now that the flag is honoured, a wrapper calling
|
|
1093
|
+
`generateCode` in token mode issues a 6-digit code as a `token`, and
|
|
1094
|
+
`resetPassword` answers `Invalid verification token`. There is no compile
|
|
1095
|
+
error, and the failure appears only in whichever environment exercises that
|
|
1096
|
+
branch. `issueCredential` / `verifyCredential` hold no opinion about the
|
|
1097
|
+
mode; see [Verification Modes](#verification-modes).
|
|
1098
|
+
|
|
1099
|
+
3. **`verification.baseUrl` is now required and validated at boot.** If it is
|
|
1100
|
+
missing, or is not an absolute `http(s)` URL, `AuthModule.forRootAsync`
|
|
1101
|
+
throws `InvalidAuthConfigException` at startup instead of sending mail
|
|
1102
|
+
nobody can act on. Set it before you deploy the bump.
|
|
1103
|
+
|
|
1104
|
+
Token TTL also differs from code TTL: 60 minutes by default
|
|
1105
|
+
(`verification.tokenExpiresInMinutes`) versus 15 for codes.
|
|
1106
|
+
|
|
1107
|
+
**`verificationMode: 'code'` — the default — is unaffected.** Behaviour is
|
|
1108
|
+
identical, including the legacy `FRONTEND_URL` fallback for the reset page URL.
|
|
1109
|
+
|
|
1110
|
+
**Additive — new behaviour, no action required.**
|
|
1111
|
+
|
|
1112
|
+
- `BaseAuthResolver.performRefreshToken` and `performLogout` now fall back to the refresh cookie when `features.cookieAuth: true` and `input.refreshToken` is empty. Your consumer `RefreshTokenInput` / `LogoutInput` DTOs should mark `refreshToken` `@IsOptional()` for browser flows. Explicit `input.refreshToken` still wins for non-browser callers.
|
|
1113
|
+
- `AuthService.readRefreshTokenFromCookie(req)` is the new public symmetric counterpart to `setAuthCookies` / `clearAuthCookies`. Useful if you wrap the resolver methods yourself.
|
|
1114
|
+
- New lifecycle hook `onAccountLocked(userId, email, ipAddress, lockDurationSeconds, realm?)` fires on the not-locked → locked transition from `BruteForceProtectionService`. Fire-and-forget (void); throws are logged and swallowed.
|
|
1115
|
+
- `NoOpEmailSender` now logs every link in the email it would have sent, so an auth flow can be walked locally without a mailbox: trigger it, read the link from the log, open it.
|
|
1116
|
+
- Custom `IEmailService` implementations receive two new optional trailing parameters: `sendVerificationEmail(..., verificationUrl?)` and `sendPasswordResetEmail(..., expiresInMinutes?)`. Optional, so existing implementations compile unchanged; render `verificationUrl` in token mode rather than the raw credential.
|
|
1117
|
+
|
|
857
1118
|
## License
|
|
858
1119
|
|
|
859
1120
|
MIT -- see [LICENSE](./LICENSE).
|
package/dist/auth.module.d.ts
CHANGED
|
@@ -100,6 +100,12 @@ export interface AuthModuleOptions {
|
|
|
100
100
|
encryptionKey?: string;
|
|
101
101
|
passwordPolicy?: PasswordPolicyConfig;
|
|
102
102
|
rateLimiterInstance?: IRateLimiter;
|
|
103
|
+
bruteForce?: {
|
|
104
|
+
ipRateLimit?: {
|
|
105
|
+
maxAttempts: number;
|
|
106
|
+
windowMs: number;
|
|
107
|
+
};
|
|
108
|
+
};
|
|
103
109
|
email?: {
|
|
104
110
|
sender: IEmailSender;
|
|
105
111
|
from: {
|
|
@@ -119,6 +125,7 @@ export interface AuthModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'>
|
|
|
119
125
|
useFactory: (...args: any[]) => Promise<AuthModuleOptions> | AuthModuleOptions;
|
|
120
126
|
inject?: any[];
|
|
121
127
|
}
|
|
128
|
+
export declare function validateAuthModuleOptions(opts: AuthModuleOptions): void;
|
|
122
129
|
export { AUTH_MODULE_OPTIONS, USER_REPOSITORY, REFRESH_TOKEN_REPOSITORY, EMAIL_SERVICE, SMS_SERVICE, AUTH_LIFECYCLE_HOOKS, VERIFICATION_REPOSITORY, BRUTE_FORCE_REPOSITORY, BIOMETRIC_REPOSITORY, AUTH_LOGGER, RATE_LIMITER, TENANT_REPOSITORY, TENANT_EXTRACTOR, RESOURCE_PERMISSION_REPOSITORY, JWT_PAYLOAD_FACTORY, API_KEY_REPOSITORY, } from './constants';
|
|
123
130
|
export declare class AuthModule implements NestModule {
|
|
124
131
|
configure(consumer: MiddlewareConsumer): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../src/auth.module.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,kBAAkB,EAElB,UAAU,EAGX,MAAM,gBAAgB,CAAC;AAIxB,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AA0B3D,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,sCAAsC,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAC;AAClF,OAAO,EAAE,uBAAuB,EAAE,MAAM,iDAAiD,CAAC;AAC1F,OAAO,EAAE,uBAAuB,EAAE,MAAM,gDAAgD,CAAC;AACzF,OAAO,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AACtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,6CAA6C,CAAC;AACnF,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAWjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0CAA0C,CAAC;AAC7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAC3E,OAAO,EAAE,eAAe,EAAE,MAAM,wCAAwC,CAAC;AACzE,OAAO,EAAE,6BAA6B,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,EAAE,kBAAkB,EAAE,MAAM,4CAA4C,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AA8B9E,OAAO,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAC;AAGrF,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAInE,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,MAAM,gDAAgD,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../src/auth.module.ts"],"names":[],"mappings":"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"}
|
package/dist/auth.module.js
CHANGED
|
@@ -8,6 +8,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var AuthModule_1;
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.AuthModule = exports.API_KEY_REPOSITORY = exports.JWT_PAYLOAD_FACTORY = exports.RESOURCE_PERMISSION_REPOSITORY = exports.TENANT_EXTRACTOR = exports.TENANT_REPOSITORY = exports.RATE_LIMITER = exports.AUTH_LOGGER = exports.BIOMETRIC_REPOSITORY = exports.BRUTE_FORCE_REPOSITORY = exports.VERIFICATION_REPOSITORY = exports.AUTH_LIFECYCLE_HOOKS = exports.SMS_SERVICE = exports.EMAIL_SERVICE = exports.REFRESH_TOKEN_REPOSITORY = exports.USER_REPOSITORY = exports.AUTH_MODULE_OPTIONS = void 0;
|
|
11
|
+
exports.validateAuthModuleOptions = validateAuthModuleOptions;
|
|
11
12
|
const common_1 = require("@nestjs/common");
|
|
12
13
|
const realm_middleware_1 = require("./middleware/realm.middleware");
|
|
13
14
|
const jwt_1 = require("@nestjs/jwt");
|
|
@@ -39,8 +40,59 @@ const api_key_strategy_1 = require("./strategies/api-key.strategy");
|
|
|
39
40
|
const default_jwt_payload_factory_1 = require("./services/default-jwt-payload-factory");
|
|
40
41
|
const constants_1 = require("./constants");
|
|
41
42
|
const in_memory_rate_limiter_service_1 = require("./services/in-memory-rate-limiter.service");
|
|
43
|
+
const invalid_auth_config_exception_1 = require("./exceptions/invalid-auth-config.exception");
|
|
42
44
|
const configurable_email_service_1 = require("./services/configurable-email.service");
|
|
43
45
|
const default_email_template_renderer_1 = require("./services/default-email-template-renderer");
|
|
46
|
+
function validateAuthModuleOptions(opts) {
|
|
47
|
+
validateVerificationOptions(opts);
|
|
48
|
+
const ipRateLimit = opts.bruteForce?.ipRateLimit;
|
|
49
|
+
if (ipRateLimit && !opts.rateLimiterInstance) {
|
|
50
|
+
throw new invalid_auth_config_exception_1.InvalidAuthConfigException('[AuthModule] bruteForce.ipRateLimit is configured but rateLimiterInstance ' +
|
|
51
|
+
'is undefined. The default InMemoryRateLimiterService is unsafe for ' +
|
|
52
|
+
'multi-replica deployments (per-replica counters do not coordinate) ' +
|
|
53
|
+
'and is not auto-applied for IP rate limiting. Provide rateLimiterInstance ' +
|
|
54
|
+
'explicitly: a Redis-backed implementation for production, or ' +
|
|
55
|
+
'`new InMemoryRateLimiterService()` for single-replica deployments / dev. ' +
|
|
56
|
+
'To disable IP rate limiting, remove the bruteForce.ipRateLimit slot.');
|
|
57
|
+
}
|
|
58
|
+
if (ipRateLimit) {
|
|
59
|
+
const { maxAttempts, windowMs } = ipRateLimit;
|
|
60
|
+
if (!Number.isInteger(maxAttempts) || maxAttempts <= 0) {
|
|
61
|
+
throw new invalid_auth_config_exception_1.InvalidAuthConfigException(`[AuthModule] bruteForce.ipRateLimit.maxAttempts must be a positive integer; received ${String(maxAttempts)}. Zero would block every request; negative / fractional values are ` +
|
|
62
|
+
'invalid. Use a positive integer such as 10 (10 attempts per window).');
|
|
63
|
+
}
|
|
64
|
+
if (!Number.isInteger(windowMs) || windowMs <= 0) {
|
|
65
|
+
throw new invalid_auth_config_exception_1.InvalidAuthConfigException(`[AuthModule] bruteForce.ipRateLimit.windowMs must be a positive integer; received ${String(windowMs)}. Use a positive integer in milliseconds such as 60000 (1-minute window).`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function validateVerificationOptions(opts) {
|
|
70
|
+
if ((opts.features?.verificationMode ?? 'code') !== 'token') {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const baseUrl = opts.verification?.baseUrl;
|
|
74
|
+
if (!baseUrl) {
|
|
75
|
+
throw new invalid_auth_config_exception_1.InvalidAuthConfigException("[AuthModule] features.verificationMode is 'token' but verification.baseUrl " +
|
|
76
|
+
'is not set. Token-mode emails contain a link and no readable code, so ' +
|
|
77
|
+
'without a base URL every verification and password-reset email would be ' +
|
|
78
|
+
'unusable. Set verification.baseUrl to the absolute URL of your frontend ' +
|
|
79
|
+
"(e.g. 'https://app.example.com'); the package appends /verify-email and " +
|
|
80
|
+
'/reset-password with `token` and `email` query parameters. To keep using ' +
|
|
81
|
+
"6-digit codes instead, set features.verificationMode to 'code'.");
|
|
82
|
+
}
|
|
83
|
+
let parsed;
|
|
84
|
+
try {
|
|
85
|
+
parsed = new URL(baseUrl);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
throw new invalid_auth_config_exception_1.InvalidAuthConfigException(`[AuthModule] verification.baseUrl must be an absolute URL; received "${baseUrl}". ` +
|
|
89
|
+
"Include the scheme and host, e.g. 'https://app.example.com'.");
|
|
90
|
+
}
|
|
91
|
+
if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {
|
|
92
|
+
throw new invalid_auth_config_exception_1.InvalidAuthConfigException(`[AuthModule] verification.baseUrl must use http or https; received "${baseUrl}". ` +
|
|
93
|
+
'Verification links are opened in a browser from an email client.');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
44
96
|
var constants_2 = require("./constants");
|
|
45
97
|
Object.defineProperty(exports, "AUTH_MODULE_OPTIONS", { enumerable: true, get: function () { return constants_2.AUTH_MODULE_OPTIONS; } });
|
|
46
98
|
Object.defineProperty(exports, "USER_REPOSITORY", { enumerable: true, get: function () { return constants_2.USER_REPOSITORY; } });
|
|
@@ -67,6 +119,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
|
|
|
67
119
|
provide: constants_1.AUTH_MODULE_OPTIONS,
|
|
68
120
|
useFactory: async (...args) => {
|
|
69
121
|
const opts = await options.useFactory(...args);
|
|
122
|
+
validateAuthModuleOptions(opts);
|
|
70
123
|
if (opts.cookie?.useHostPrefix) {
|
|
71
124
|
if (opts.cookie.secure === false) {
|
|
72
125
|
throw new Error('[AuthModule] cookie.useHostPrefix requires cookie.secure to be true (or unset). ' +
|
package/dist/auth.module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../src/auth.module.ts"],"names":[],"mappings":";;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../src/auth.module.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invalid-auth-config.exception.d.ts","sourceRoot":"","sources":["../../src/exceptions/invalid-auth-config.exception.ts"],"names":[],"mappings":"AAkBA,qBAAa,0BAA2B,SAAQ,KAAK;gBAKvC,OAAO,EAAE,MAAM;CAO5B"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InvalidAuthConfigException = void 0;
|
|
4
|
+
class InvalidAuthConfigException extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'InvalidAuthConfigException';
|
|
8
|
+
Object.setPrototypeOf(this, InvalidAuthConfigException.prototype);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.InvalidAuthConfigException = InvalidAuthConfigException;
|
|
12
|
+
//# sourceMappingURL=invalid-auth-config.exception.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invalid-auth-config.exception.js","sourceRoot":"","sources":["../../src/exceptions/invalid-auth-config.exception.ts"],"names":[],"mappings":";;;AAkBA,MAAa,0BAA2B,SAAQ,KAAK;IAKnD,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,4BAA4B,CAAC;QAGzC,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,0BAA0B,CAAC,SAAS,CAAC,CAAC;IACpE,CAAC;CACF;AAZD,gEAYC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.guard.d.ts","sourceRoot":"","sources":["../../src/guards/tenant.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,gBAAgB,EAQjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAc9E,qBACa,WAAY,YAAW,WAAW;IAI3C,OAAO,CAAC,QAAQ,CAAC,SAAS;IACY,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAL3E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;gBAGpC,SAAS,EAAE,SAAS,EACkB,eAAe,CAAC,EAAE,gBAAgB,YAAA,EACjC,gBAAgB,CAAC,EAAE,iBAAiB,YAAA;IAGxF,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"tenant.guard.d.ts","sourceRoot":"","sources":["../../src/guards/tenant.guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EACX,gBAAgB,EAQjB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAIzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAc9E,qBACa,WAAY,YAAW,WAAW;IAI3C,OAAO,CAAC,QAAQ,CAAC,SAAS;IACY,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAC;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC;IAL3E,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAgC;gBAGpC,SAAS,EAAE,SAAS,EACkB,eAAe,CAAC,EAAE,gBAAgB,YAAA,EACjC,gBAAgB,CAAC,EAAE,iBAAiB,YAAA;IAGxF,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC;CAmF/D"}
|
|
@@ -45,7 +45,15 @@ let TenantGuard = TenantGuard_1 = class TenantGuard {
|
|
|
45
45
|
if (!this.tenantExtractor) {
|
|
46
46
|
throw new common_1.BadRequestException('Tenant ID required');
|
|
47
47
|
}
|
|
48
|
-
|
|
48
|
+
let tenantId;
|
|
49
|
+
try {
|
|
50
|
+
const result = this.tenantExtractor.extractTenantId(request);
|
|
51
|
+
tenantId = result instanceof Promise ? await result : result;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
this.logger.error(`Tenant extractor failed: ${err instanceof Error ? err.message : String(err)}`, err instanceof Error ? err.stack : undefined);
|
|
55
|
+
throw new common_1.BadRequestException('Tenant ID required');
|
|
56
|
+
}
|
|
49
57
|
if (!tenantId) {
|
|
50
58
|
throw new common_1.BadRequestException('Tenant ID required');
|
|
51
59
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tenant.guard.js","sourceRoot":"","sources":["../../src/guards/tenant.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAWwB;AACxB,uCAAyC;AACzC,qEAA+D;AAC/D,4CAAoF;AACpF,kEAAmE;AAiB5D,IAAM,WAAW,mBAAjB,MAAM,WAAW;IAGtB,YACmB,SAAoB,EACC,eAAmD,EAClD,gBAAqD;QAF3E,cAAS,GAAT,SAAS,CAAW;QACkB,oBAAe,GAAf,eAAe,CAAmB;QACjC,qBAAgB,GAAhB,gBAAgB,CAAoB;QAL7E,WAAM,GAAG,IAAI,eAAM,CAAC,aAAW,CAAC,IAAI,CAAC,CAAC;IAMpD,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QAEzC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAU,gCAAa,EAAE;YACxE,OAAO,CAAC,UAAU,EAAE;YACpB,OAAO,CAAC,QAAQ,EAAE;SACnB,CAAC,CAAC;QACH,IAAI,QAAQ;YAAE,OAAO,IAAI,CAAC;QAG1B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAU,2BAAe,EAAE;YAC5E,OAAO,CAAC,UAAU,EAAE;YACpB,OAAO,CAAC,QAAQ,EAAE;SACnB,CAAC,CAAC;QACH,IAAI,UAAU;YAAE,OAAO,IAAI,CAAC;QAG5B,IAAI,CAAC,IAAI,CAAC,gBAAgB;YAAE,OAAO,IAAI,CAAC;QAExC,MAAM,OAAO,GAAG,IAAA,yCAAqB,EAAC,OAAO,CAAC,CAAC;QAG/C,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,CAAC;YAC1B,MAAM,IAAI,4BAAmB,CAAC,oBAAoB,CAAC,CAAC;QACtD,CAAC;
|
|
1
|
+
{"version":3,"file":"tenant.guard.js","sourceRoot":"","sources":["../../src/guards/tenant.guard.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -112,6 +112,7 @@ export * from './exceptions/account-locked.exception';
|
|
|
112
112
|
export * from './exceptions/verification.exceptions';
|
|
113
113
|
export * from './exceptions/oauth.exceptions';
|
|
114
114
|
export * from './exceptions/password-reset.exceptions';
|
|
115
|
+
export * from './exceptions/invalid-auth-config.exception';
|
|
115
116
|
export * from './enums/verification-type.enum';
|
|
116
117
|
export * from './types/oauth.types';
|
|
117
118
|
export * from './utils/provider-helpers';
|
|
@@ -119,4 +120,5 @@ export * from './utils/cookie-options';
|
|
|
119
120
|
export * from './utils/escape-html';
|
|
120
121
|
export * from './utils/execution-context';
|
|
121
122
|
export * from './utils/request-helpers';
|
|
123
|
+
export * from './utils/ip-hash';
|
|
122
124
|
//# sourceMappingURL=index.d.ts.map
|