@techelusive/nest-auth 0.0.0-stage → 0.2.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.
- package/CHANGELOG.md +64 -0
- package/LICENSE +21 -0
- package/README.md +204 -2
- package/api/openapi.json +4399 -0
- package/api/techelusive-authentication.postman_collection.json +3178 -0
- package/dist/auth-options.d.ts +156 -0
- package/dist/auth-options.js +3 -0
- package/dist/auth-options.js.map +1 -0
- package/dist/auth-options.validation.d.ts +9 -0
- package/dist/auth-options.validation.js +74 -0
- package/dist/auth-options.validation.js.map +1 -0
- package/dist/auth.module.d.ts +38 -0
- package/dist/auth.module.js +173 -0
- package/dist/auth.module.js.map +1 -0
- package/dist/common/auth-error.d.ts +16 -0
- package/dist/common/auth-error.js +23 -0
- package/dist/common/auth-error.js.map +1 -0
- package/dist/common/auth-exception.filter.d.ts +24 -0
- package/dist/common/auth-exception.filter.js +66 -0
- package/dist/common/auth-exception.filter.js.map +1 -0
- package/dist/common/crypto/jwt.d.ts +89 -0
- package/dist/common/crypto/jwt.js +144 -0
- package/dist/common/crypto/jwt.js.map +1 -0
- package/dist/common/crypto/random-secrets.d.ts +78 -0
- package/dist/common/crypto/random-secrets.js +113 -0
- package/dist/common/crypto/random-secrets.js.map +1 -0
- package/dist/common/crypto/remote-public-keys.d.ts +49 -0
- package/dist/common/crypto/remote-public-keys.js +105 -0
- package/dist/common/crypto/remote-public-keys.js.map +1 -0
- package/dist/common/crypto/totp.d.ts +68 -0
- package/dist/common/crypto/totp.js +148 -0
- package/dist/common/crypto/totp.js.map +1 -0
- package/dist/common/encryption.service.d.ts +36 -0
- package/dist/common/encryption.service.js +111 -0
- package/dist/common/encryption.service.js.map +1 -0
- package/dist/common/identifiers.d.ts +48 -0
- package/dist/common/identifiers.js +89 -0
- package/dist/common/identifiers.js.map +1 -0
- package/dist/common/network-safety.d.ts +19 -0
- package/dist/common/network-safety.js +72 -0
- package/dist/common/network-safety.js.map +1 -0
- package/dist/common/rate-limiter.service.d.ts +63 -0
- package/dist/common/rate-limiter.service.js +158 -0
- package/dist/common/rate-limiter.service.js.map +1 -0
- package/dist/common/refresh-token-cookie.d.ts +78 -0
- package/dist/common/refresh-token-cookie.js +185 -0
- package/dist/common/refresh-token-cookie.js.map +1 -0
- package/dist/common/request-context.d.ts +14 -0
- package/dist/common/request-context.js +22 -0
- package/dist/common/request-context.js.map +1 -0
- package/dist/common/strict-validation.pipe.d.ts +8 -0
- package/dist/common/strict-validation.pipe.js +16 -0
- package/dist/common/strict-validation.pipe.js.map +1 -0
- package/dist/common/time.d.ts +15 -0
- package/dist/common/time.js +34 -0
- package/dist/common/time.js.map +1 -0
- package/dist/constants.d.ts +1326 -0
- package/dist/constants.js +1142 -0
- package/dist/constants.js.map +1 -0
- package/dist/database/create-auth-tables.migration.d.ts +26 -0
- package/dist/database/create-auth-tables.migration.js +291 -0
- package/dist/database/create-auth-tables.migration.js.map +1 -0
- package/dist/database/email-change-and-retention.migration.d.ts +25 -0
- package/dist/database/email-change-and-retention.migration.js +54 -0
- package/dist/database/email-change-and-retention.migration.js.map +1 -0
- package/dist/database/entities/api-key.entity.d.ts +17 -0
- package/dist/database/entities/api-key.entity.js +80 -0
- package/dist/database/entities/api-key.entity.js.map +1 -0
- package/dist/database/entities/audit-log.entity.d.ts +13 -0
- package/dist/database/entities/audit-log.entity.js +64 -0
- package/dist/database/entities/audit-log.entity.js.map +1 -0
- package/dist/database/entities/backup-code.entity.d.ts +9 -0
- package/dist/database/entities/backup-code.entity.js +52 -0
- package/dist/database/entities/backup-code.entity.js.map +1 -0
- package/dist/database/entities/index.d.ts +43 -0
- package/dist/database/entities/index.js +82 -0
- package/dist/database/entities/index.js.map +1 -0
- package/dist/database/entities/json-object.d.ts +12 -0
- package/dist/database/entities/json-object.js +3 -0
- package/dist/database/entities/json-object.js.map +1 -0
- package/dist/database/entities/link-token.entity.d.ts +15 -0
- package/dist/database/entities/link-token.entity.js +64 -0
- package/dist/database/entities/link-token.entity.js.map +1 -0
- package/dist/database/entities/one-time-code.entity.d.ts +16 -0
- package/dist/database/entities/one-time-code.entity.js +69 -0
- package/dist/database/entities/one-time-code.entity.js.map +1 -0
- package/dist/database/entities/organization-invitation.entity.d.ts +14 -0
- package/dist/database/entities/organization-invitation.entity.js +73 -0
- package/dist/database/entities/organization-invitation.entity.js.map +1 -0
- package/dist/database/entities/organization-member.entity.d.ts +9 -0
- package/dist/database/entities/organization-member.entity.js +48 -0
- package/dist/database/entities/organization-member.entity.js.map +1 -0
- package/dist/database/entities/organization.entity.d.ts +12 -0
- package/dist/database/entities/organization.entity.js +58 -0
- package/dist/database/entities/organization.entity.js.map +1 -0
- package/dist/database/entities/passkey.entity.d.ts +19 -0
- package/dist/database/entities/passkey.entity.js +78 -0
- package/dist/database/entities/passkey.entity.js.map +1 -0
- package/dist/database/entities/password.entity.d.ts +6 -0
- package/dist/database/entities/password.entity.js +36 -0
- package/dist/database/entities/password.entity.js.map +1 -0
- package/dist/database/entities/refresh-token.entity.d.ts +9 -0
- package/dist/database/entities/refresh-token.entity.js +53 -0
- package/dist/database/entities/refresh-token.entity.js.map +1 -0
- package/dist/database/entities/session.entity.d.ts +20 -0
- package/dist/database/entities/session.entity.js +87 -0
- package/dist/database/entities/session.entity.js.map +1 -0
- package/dist/database/entities/settings.entity.d.ts +7 -0
- package/dist/database/entities/settings.entity.js +41 -0
- package/dist/database/entities/settings.entity.js.map +1 -0
- package/dist/database/entities/sign-in-challenge.entity.d.ts +23 -0
- package/dist/database/entities/sign-in-challenge.entity.js +80 -0
- package/dist/database/entities/sign-in-challenge.entity.js.map +1 -0
- package/dist/database/entities/signing-key.entity.d.ts +11 -0
- package/dist/database/entities/signing-key.entity.js +54 -0
- package/dist/database/entities/signing-key.entity.js.map +1 -0
- package/dist/database/entities/social-account.entity.d.ts +13 -0
- package/dist/database/entities/social-account.entity.js +54 -0
- package/dist/database/entities/social-account.entity.js.map +1 -0
- package/dist/database/entities/two-factor-secret.entity.d.ts +10 -0
- package/dist/database/entities/two-factor-secret.entity.js +48 -0
- package/dist/database/entities/two-factor-secret.entity.js.map +1 -0
- package/dist/database/entities/user.entity.d.ts +34 -0
- package/dist/database/entities/user.entity.js +129 -0
- package/dist/database/entities/user.entity.js.map +1 -0
- package/dist/database/entities/webhook-delivery.entity.d.ts +17 -0
- package/dist/database/entities/webhook-delivery.entity.js +79 -0
- package/dist/database/entities/webhook-delivery.entity.js.map +1 -0
- package/dist/database/entities/webhook.entity.d.ts +12 -0
- package/dist/database/entities/webhook.entity.js +58 -0
- package/dist/database/entities/webhook.entity.js.map +1 -0
- package/dist/database/typeorm-row.d.ts +21 -0
- package/dist/database/typeorm-row.js +29 -0
- package/dist/database/typeorm-row.js.map +1 -0
- package/dist/features/account/account.controller.d.ts +259 -0
- package/dist/features/account/account.controller.js +372 -0
- package/dist/features/account/account.controller.js.map +1 -0
- package/dist/features/account/account.dto.d.ts +16 -0
- package/dist/features/account/account.dto.js +61 -0
- package/dist/features/account/account.dto.js.map +1 -0
- package/dist/features/account/data-export.service.d.ts +85 -0
- package/dist/features/account/data-export.service.js +125 -0
- package/dist/features/account/data-export.service.js.map +1 -0
- package/dist/features/account/health.service.d.ts +30 -0
- package/dist/features/account/health.service.js +59 -0
- package/dist/features/account/health.service.js.map +1 -0
- package/dist/features/admin/admin.controller.d.ts +382 -0
- package/dist/features/admin/admin.controller.js +623 -0
- package/dist/features/admin/admin.controller.js.map +1 -0
- package/dist/features/admin/admin.dto.d.ts +61 -0
- package/dist/features/admin/admin.dto.js +253 -0
- package/dist/features/admin/admin.dto.js.map +1 -0
- package/dist/features/admin/admin.service.d.ts +142 -0
- package/dist/features/admin/admin.service.js +227 -0
- package/dist/features/admin/admin.service.js.map +1 -0
- package/dist/features/api-keys/api-keys.service.d.ts +67 -0
- package/dist/features/api-keys/api-keys.service.js +144 -0
- package/dist/features/api-keys/api-keys.service.js.map +1 -0
- package/dist/features/email-change/email-change.controller.d.ts +38 -0
- package/dist/features/email-change/email-change.controller.js +85 -0
- package/dist/features/email-change/email-change.controller.js.map +1 -0
- package/dist/features/email-change/email-change.dto.d.ts +10 -0
- package/dist/features/email-change/email-change.dto.js +43 -0
- package/dist/features/email-change/email-change.dto.js.map +1 -0
- package/dist/features/email-change/email-change.service.d.ts +74 -0
- package/dist/features/email-change/email-change.service.js +179 -0
- package/dist/features/email-change/email-change.service.js.map +1 -0
- package/dist/features/email-links/email-verification.service.d.ts +46 -0
- package/dist/features/email-links/email-verification.service.js +94 -0
- package/dist/features/email-links/email-verification.service.js.map +1 -0
- package/dist/features/email-links/link-tokens.service.d.ts +44 -0
- package/dist/features/email-links/link-tokens.service.js +122 -0
- package/dist/features/email-links/link-tokens.service.js.map +1 -0
- package/dist/features/events/auth-events.service.d.ts +52 -0
- package/dist/features/events/auth-events.service.js +117 -0
- package/dist/features/events/auth-events.service.js.map +1 -0
- package/dist/features/maintenance/data-retention.service.d.ts +60 -0
- package/dist/features/maintenance/data-retention.service.js +182 -0
- package/dist/features/maintenance/data-retention.service.js.map +1 -0
- package/dist/features/notifications/notifications.service.d.ts +40 -0
- package/dist/features/notifications/notifications.service.js +94 -0
- package/dist/features/notifications/notifications.service.js.map +1 -0
- package/dist/features/one-time-codes/one-time-codes.service.d.ts +40 -0
- package/dist/features/one-time-codes/one-time-codes.service.js +102 -0
- package/dist/features/one-time-codes/one-time-codes.service.js.map +1 -0
- package/dist/features/organizations/organizations.controller.d.ts +220 -0
- package/dist/features/organizations/organizations.controller.js +337 -0
- package/dist/features/organizations/organizations.controller.js.map +1 -0
- package/dist/features/organizations/organizations.dto.d.ts +31 -0
- package/dist/features/organizations/organizations.dto.js +100 -0
- package/dist/features/organizations/organizations.dto.js.map +1 -0
- package/dist/features/organizations/organizations.service.d.ts +274 -0
- package/dist/features/organizations/organizations.service.js +481 -0
- package/dist/features/organizations/organizations.service.js.map +1 -0
- package/dist/features/passkeys/passkeys.controller.d.ts +92 -0
- package/dist/features/passkeys/passkeys.controller.js +183 -0
- package/dist/features/passkeys/passkeys.controller.js.map +1 -0
- package/dist/features/passkeys/passkeys.dto.d.ts +13 -0
- package/dist/features/passkeys/passkeys.dto.js +50 -0
- package/dist/features/passkeys/passkeys.dto.js.map +1 -0
- package/dist/features/passkeys/passkeys.service.d.ts +159 -0
- package/dist/features/passkeys/passkeys.service.js +350 -0
- package/dist/features/passkeys/passkeys.service.js.map +1 -0
- package/dist/features/passwordless/passwordless-auth.service.d.ts +152 -0
- package/dist/features/passwordless/passwordless-auth.service.js +304 -0
- package/dist/features/passwordless/passwordless-auth.service.js.map +1 -0
- package/dist/features/passwordless/passwordless.controller.d.ts +75 -0
- package/dist/features/passwordless/passwordless.controller.js +163 -0
- package/dist/features/passwordless/passwordless.controller.js.map +1 -0
- package/dist/features/passwordless/passwordless.dto.d.ts +29 -0
- package/dist/features/passwordless/passwordless.dto.js +101 -0
- package/dist/features/passwordless/passwordless.dto.js.map +1 -0
- package/dist/features/passwords/password-auth.service.d.ts +154 -0
- package/dist/features/passwords/password-auth.service.js +317 -0
- package/dist/features/passwords/password-auth.service.js.map +1 -0
- package/dist/features/passwords/password-policy.service.d.ts +63 -0
- package/dist/features/passwords/password-policy.service.js +175 -0
- package/dist/features/passwords/password-policy.service.js.map +1 -0
- package/dist/features/passwords/password.controller.d.ts +95 -0
- package/dist/features/passwords/password.controller.js +193 -0
- package/dist/features/passwords/password.controller.js.map +1 -0
- package/dist/features/passwords/password.dto.d.ts +36 -0
- package/dist/features/passwords/password.dto.js +140 -0
- package/dist/features/passwords/password.dto.js.map +1 -0
- package/dist/features/sessions/session.service.d.ts +132 -0
- package/dist/features/sessions/session.service.js +308 -0
- package/dist/features/sessions/session.service.js.map +1 -0
- package/dist/features/sessions/session.types.d.ts +34 -0
- package/dist/features/sessions/session.types.js +3 -0
- package/dist/features/sessions/session.types.js.map +1 -0
- package/dist/features/settings/settings.defaults.d.ts +2 -0
- package/dist/features/settings/settings.defaults.js +7 -0
- package/dist/features/settings/settings.defaults.js.map +1 -0
- package/dist/features/settings/settings.service.d.ts +44 -0
- package/dist/features/settings/settings.service.js +112 -0
- package/dist/features/settings/settings.service.js.map +1 -0
- package/dist/features/settings/settings.types.d.ts +133 -0
- package/dist/features/settings/settings.types.js +3 -0
- package/dist/features/settings/settings.types.js.map +1 -0
- package/dist/features/settings/settings.validation.d.ts +33 -0
- package/dist/features/settings/settings.validation.js +614 -0
- package/dist/features/settings/settings.validation.js.map +1 -0
- package/dist/features/sign-in/sign-in-challenges.service.d.ts +54 -0
- package/dist/features/sign-in/sign-in-challenges.service.js +104 -0
- package/dist/features/sign-in/sign-in-challenges.service.js.map +1 -0
- package/dist/features/sign-in/sign-in.service.d.ts +127 -0
- package/dist/features/sign-in/sign-in.service.js +248 -0
- package/dist/features/sign-in/sign-in.service.js.map +1 -0
- package/dist/features/sign-in/sign-in.types.d.ts +22 -0
- package/dist/features/sign-in/sign-in.types.js +3 -0
- package/dist/features/sign-in/sign-in.types.js.map +1 -0
- package/dist/features/signing-keys/jwks.controller.d.ts +17 -0
- package/dist/features/signing-keys/jwks.controller.js +51 -0
- package/dist/features/signing-keys/jwks.controller.js.map +1 -0
- package/dist/features/signing-keys/signing-keys.service.d.ts +67 -0
- package/dist/features/signing-keys/signing-keys.service.js +142 -0
- package/dist/features/signing-keys/signing-keys.service.js.map +1 -0
- package/dist/features/social/social-auth.service.d.ts +100 -0
- package/dist/features/social/social-auth.service.js +232 -0
- package/dist/features/social/social-auth.service.js.map +1 -0
- package/dist/features/social/social-providers.service.d.ts +97 -0
- package/dist/features/social/social-providers.service.js +214 -0
- package/dist/features/social/social-providers.service.js.map +1 -0
- package/dist/features/social/social.controller.d.ts +54 -0
- package/dist/features/social/social.controller.js +124 -0
- package/dist/features/social/social.controller.js.map +1 -0
- package/dist/features/social/social.dto.d.ts +11 -0
- package/dist/features/social/social.dto.js +38 -0
- package/dist/features/social/social.dto.js.map +1 -0
- package/dist/features/two-factor/two-factor.controller.d.ts +84 -0
- package/dist/features/two-factor/two-factor.controller.js +174 -0
- package/dist/features/two-factor/two-factor.controller.js.map +1 -0
- package/dist/features/two-factor/two-factor.dto.d.ts +10 -0
- package/dist/features/two-factor/two-factor.dto.js +37 -0
- package/dist/features/two-factor/two-factor.dto.js.map +1 -0
- package/dist/features/two-factor/two-factor.service.d.ts +171 -0
- package/dist/features/two-factor/two-factor.service.js +348 -0
- package/dist/features/two-factor/two-factor.service.js.map +1 -0
- package/dist/features/users/sign-in-methods.service.d.ts +40 -0
- package/dist/features/users/sign-in-methods.service.js +81 -0
- package/dist/features/users/sign-in-methods.service.js.map +1 -0
- package/dist/features/users/sign-up-rules.d.ts +12 -0
- package/dist/features/users/sign-up-rules.js +29 -0
- package/dist/features/users/sign-up-rules.js.map +1 -0
- package/dist/features/users/users.service.d.ts +246 -0
- package/dist/features/users/users.service.js +509 -0
- package/dist/features/users/users.service.js.map +1 -0
- package/dist/features/users/users.types.d.ts +24 -0
- package/dist/features/users/users.types.js +3 -0
- package/dist/features/users/users.types.js.map +1 -0
- package/dist/features/webhooks/webhook-delivery.worker.d.ts +55 -0
- package/dist/features/webhooks/webhook-delivery.worker.js +175 -0
- package/dist/features/webhooks/webhook-delivery.worker.js.map +1 -0
- package/dist/features/webhooks/webhook-signature.d.ts +28 -0
- package/dist/features/webhooks/webhook-signature.js +85 -0
- package/dist/features/webhooks/webhook-signature.js.map +1 -0
- package/dist/features/webhooks/webhooks.service.d.ts +144 -0
- package/dist/features/webhooks/webhooks.service.js +238 -0
- package/dist/features/webhooks/webhooks.service.js.map +1 -0
- package/dist/guards/auth.decorators.d.ts +65 -0
- package/dist/guards/auth.decorators.js +68 -0
- package/dist/guards/auth.decorators.js.map +1 -0
- package/dist/guards/auth.guard.d.ts +47 -0
- package/dist/guards/auth.guard.js +132 -0
- package/dist/guards/auth.guard.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/console-email-sender.d.ts +15 -0
- package/dist/integrations/console-email-sender.js +26 -0
- package/dist/integrations/console-email-sender.js.map +1 -0
- package/dist/integrations/msg91-sms-sender.d.ts +28 -0
- package/dist/integrations/msg91-sms-sender.js +46 -0
- package/dist/integrations/msg91-sms-sender.js.map +1 -0
- package/docs/README.md +39 -0
- package/docs/admin-api-keys-webhooks.md +147 -0
- package/docs/api-reference.md +175 -0
- package/docs/architecture.md +119 -0
- package/docs/client-integration.md +192 -0
- package/docs/code-walkthrough.md +273 -0
- package/docs/configuration.md +229 -0
- package/docs/endpoints/README.md +151 -0
- package/docs/endpoints/account.md +363 -0
- package/docs/endpoints/admin.md +422 -0
- package/docs/endpoints/email-password.md +261 -0
- package/docs/endpoints/organizations.md +192 -0
- package/docs/endpoints/passkeys.md +195 -0
- package/docs/endpoints/passwordless.md +214 -0
- package/docs/endpoints/social.md +116 -0
- package/docs/endpoints/two-factor.md +146 -0
- package/docs/error-codes.md +100 -0
- package/docs/events.md +83 -0
- package/docs/getting-started.md +139 -0
- package/docs/manual-testing.md +215 -0
- package/docs/organizations.md +86 -0
- package/docs/production-checklist.md +60 -0
- package/docs/protecting-routes.md +147 -0
- package/docs/security.md +61 -0
- package/docs/sign-in-flows.md +294 -0
- package/package.json +92 -4
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RateLimiterService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const constants_1 = require("../constants.js");
|
|
18
|
+
const auth_error_1 = require("./auth-error");
|
|
19
|
+
const time_1 = require("./time");
|
|
20
|
+
/**
|
|
21
|
+
* Counts attempts per key (an IP address, an email, a user id) inside a time window and blocks
|
|
22
|
+
* once there are too many. Counters live in Redis when `redisUrl` is set (shared by every server),
|
|
23
|
+
* otherwise in this server's memory.
|
|
24
|
+
*/
|
|
25
|
+
let RateLimiterService = class RateLimiterService {
|
|
26
|
+
logger = new common_1.Logger(constants_1.LOG_CONTEXT.rateLimiter);
|
|
27
|
+
redisClient;
|
|
28
|
+
countersInMemory = new Map();
|
|
29
|
+
constructor(options) {
|
|
30
|
+
if (options.redisUrl) {
|
|
31
|
+
// Loaded only when Redis is used, so apps without Redis don't need the package installed.
|
|
32
|
+
const ioredis = require('ioredis');
|
|
33
|
+
// ioredis 5 exports the class itself; newer versions export it as `Redis` (and `default`).
|
|
34
|
+
const Redis = ioredis.Redis ?? ioredis.default ?? ioredis;
|
|
35
|
+
this.redisClient = new Redis(options.redisUrl, { maxRetriesPerRequest: 1 });
|
|
36
|
+
}
|
|
37
|
+
else if (process.env.NODE_ENV === constants_1.PRODUCTION_ENVIRONMENT) {
|
|
38
|
+
// Only reachable with singleServer: true (startup validation requires redisUrl otherwise).
|
|
39
|
+
this.logger.log('Rate limits are counted in memory (singleServer: true).');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* For the health check: is Redis answering?
|
|
44
|
+
*
|
|
45
|
+
* @returns "ok", "error", or "not_used" when rate limits live in memory.
|
|
46
|
+
*/
|
|
47
|
+
async checkRedis() {
|
|
48
|
+
if (!this.redisClient)
|
|
49
|
+
return constants_1.HEALTH_STATUS.notUsed;
|
|
50
|
+
try {
|
|
51
|
+
await (0, time_1.withTimeout)(this.redisClient.ping(), constants_1.HEALTH_CHECK_TIMEOUT_MS);
|
|
52
|
+
return constants_1.HEALTH_STATUS.ok;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return constants_1.HEALTH_STATUS.error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Closes the Redis connection when the app shuts down, so the process can exit cleanly.
|
|
60
|
+
*/
|
|
61
|
+
async onModuleDestroy() {
|
|
62
|
+
await this.redisClient?.quit();
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Records one attempt for `key` and blocks when there have been too many inside the rule's window.
|
|
66
|
+
*
|
|
67
|
+
* @param key - What is being limited, e.g. an IP address, an email or a user id.
|
|
68
|
+
* @param rule - How many attempts are allowed and in how long a window.
|
|
69
|
+
* @throws AuthError TOO_MANY_REQUESTS (with `retryAfterSeconds`) when the limit is exceeded.
|
|
70
|
+
* @example
|
|
71
|
+
* await rateLimiter.consume(`sign-in:ip:${ipAddress}`, RATE_LIMITS.signInPerIp);
|
|
72
|
+
*/
|
|
73
|
+
async consume(key, rule) {
|
|
74
|
+
const secondsToWait = this.redisClient ? await this.countInRedis(key, rule) : this.countInMemory(key, rule);
|
|
75
|
+
if (secondsToWait > 0)
|
|
76
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.tooManyRequests, { retryAfterSeconds: secondsToWait });
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Forgets the attempts for `key`, e.g. the per-account sign-in counter after a successful sign-in.
|
|
80
|
+
*
|
|
81
|
+
* @param key - The same key that was passed to `consume`.
|
|
82
|
+
*/
|
|
83
|
+
async reset(key) {
|
|
84
|
+
if (this.redisClient)
|
|
85
|
+
await this.redisClient.del(constants_1.RATE_LIMIT_REDIS_KEY_PREFIX + key);
|
|
86
|
+
else
|
|
87
|
+
this.countersInMemory.delete(key);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Counts an attempt in Redis (shared by every server).
|
|
91
|
+
* If Redis is down the request is blocked, rather than allowing unlimited password or code guessing.
|
|
92
|
+
*
|
|
93
|
+
* @param key - What is being limited.
|
|
94
|
+
* @param rule - The limit to apply.
|
|
95
|
+
* @returns 0 when allowed, otherwise the seconds until the window resets.
|
|
96
|
+
*/
|
|
97
|
+
async countInRedis(key, rule) {
|
|
98
|
+
const redisKey = constants_1.RATE_LIMIT_REDIS_KEY_PREFIX + key;
|
|
99
|
+
try {
|
|
100
|
+
const attempts = await this.redisClient.incr(redisKey);
|
|
101
|
+
if (attempts === 1)
|
|
102
|
+
await this.redisClient.expire(redisKey, rule.windowSeconds);
|
|
103
|
+
if (attempts <= rule.maximumAttempts)
|
|
104
|
+
return 0;
|
|
105
|
+
const secondsLeft = await this.redisClient.ttl(redisKey);
|
|
106
|
+
if (secondsLeft < 0) {
|
|
107
|
+
// The expiry was lost (e.g. a crash between INCR and EXPIRE); set it so the key can't block forever.
|
|
108
|
+
await this.redisClient.expire(redisKey, rule.windowSeconds);
|
|
109
|
+
return rule.windowSeconds;
|
|
110
|
+
}
|
|
111
|
+
return Math.max(secondsLeft, 1);
|
|
112
|
+
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
// If Redis is down we block, rather than allow unlimited password or code guessing.
|
|
115
|
+
this.logger.error(`Redis unavailable, blocking request: ${error.message}`);
|
|
116
|
+
return rule.windowSeconds;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Counts an attempt in this server's memory (used when there is no Redis).
|
|
121
|
+
*
|
|
122
|
+
* @param key - What is being limited.
|
|
123
|
+
* @param rule - The limit to apply.
|
|
124
|
+
* @returns 0 when allowed, otherwise the seconds until the window resets.
|
|
125
|
+
*/
|
|
126
|
+
countInMemory(key, rule) {
|
|
127
|
+
const now = Date.now();
|
|
128
|
+
let counter = this.countersInMemory.get(key);
|
|
129
|
+
if (!counter || counter.windowEndsAt <= now) {
|
|
130
|
+
counter = { attempts: 0, windowEndsAt: now + rule.windowSeconds * constants_1.SECOND_IN_MILLISECONDS };
|
|
131
|
+
this.countersInMemory.set(key, counter);
|
|
132
|
+
}
|
|
133
|
+
counter.attempts += 1;
|
|
134
|
+
this.pruneExpiredCounters(now);
|
|
135
|
+
return counter.attempts > rule.maximumAttempts ? Math.ceil((counter.windowEndsAt - now) / constants_1.SECOND_IN_MILLISECONDS) : 0;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Removes finished windows once the map gets large, so memory stays bounded on long-running servers.
|
|
139
|
+
*
|
|
140
|
+
* @param now - The current time in milliseconds.
|
|
141
|
+
*/
|
|
142
|
+
pruneExpiredCounters(now) {
|
|
143
|
+
const cleanUpThreshold = constants_1.IN_MEMORY_RATE_LIMIT_MAX_ENTRIES;
|
|
144
|
+
if (this.countersInMemory.size < cleanUpThreshold)
|
|
145
|
+
return;
|
|
146
|
+
for (const [key, counter] of this.countersInMemory) {
|
|
147
|
+
if (counter.windowEndsAt <= now)
|
|
148
|
+
this.countersInMemory.delete(key);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
exports.RateLimiterService = RateLimiterService;
|
|
153
|
+
exports.RateLimiterService = RateLimiterService = __decorate([
|
|
154
|
+
(0, common_1.Injectable)(),
|
|
155
|
+
__param(0, (0, common_1.Inject)(constants_1.AUTH_OPTIONS_TOKEN)),
|
|
156
|
+
__metadata("design:paramtypes", [Object])
|
|
157
|
+
], RateLimiterService);
|
|
158
|
+
//# sourceMappingURL=rate-limiter.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.service.js","sourceRoot":"","sources":["../../src/common/rate-limiter.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
|
|
2
|
+
import { Reflector } from '@nestjs/core';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
import { AuthOptions, RefreshTokenCookieOptions } from '../auth-options.js';
|
|
5
|
+
/** The minimal request/response surface used here (works with Express and Fastify). */
|
|
6
|
+
interface HttpRequestLike {
|
|
7
|
+
headers: Record<string, string | string[] | undefined>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Marks the library's own controllers, so the refresh-token-cookie interceptor never touches the app's routes.
|
|
11
|
+
*
|
|
12
|
+
* @returns A class decorator.
|
|
13
|
+
*/
|
|
14
|
+
export declare const LibraryController: () => import("@nestjs/common").CustomDecorator<"techelusive-auth:library-controller">;
|
|
15
|
+
/**
|
|
16
|
+
* Marks a route (logout) after which the browser should forget the refresh token cookie.
|
|
17
|
+
*
|
|
18
|
+
* @returns A method decorator.
|
|
19
|
+
*/
|
|
20
|
+
export declare const ClearsRefreshTokenCookie: () => import("@nestjs/common").CustomDecorator<"techelusive-auth:clears-refresh-token-cookie">;
|
|
21
|
+
/**
|
|
22
|
+
* Builds the Set-Cookie value for the refresh token:
|
|
23
|
+
* "name=value; Max-Age=...; Path=/; HttpOnly; Secure; SameSite=Strict"
|
|
24
|
+
*
|
|
25
|
+
* @param options - The app's cookie settings (name, path, domain, sameSite).
|
|
26
|
+
* @param value - The refresh token, or "" when deleting the cookie.
|
|
27
|
+
* @param maxAgeSeconds - How long the browser should keep it; 0 deletes the cookie.
|
|
28
|
+
* @returns The cookie string for the Set-Cookie header.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildRefreshCookie(options: RefreshTokenCookieOptions, value: string, maxAgeSeconds: number): string;
|
|
31
|
+
/**
|
|
32
|
+
* Reads the refresh token from the request's Cookie header.
|
|
33
|
+
*
|
|
34
|
+
* @param options - The app's cookie settings (to know the cookie's name).
|
|
35
|
+
* @param request - The incoming request.
|
|
36
|
+
* @returns The refresh token, or undefined if the cookie is missing or malformed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function readRefreshCookie(options: RefreshTokenCookieOptions, request: HttpRequestLike): string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Cross-site request protection for cookie refreshes: the browser always sends the Origin header
|
|
41
|
+
* on these requests, and it must be one of the app's own websites.
|
|
42
|
+
*
|
|
43
|
+
* @param options - The app's cookie settings, including `allowedOrigins`.
|
|
44
|
+
* @param request - The incoming request.
|
|
45
|
+
* @throws AuthError FORBIDDEN when the origin is missing or not in the list.
|
|
46
|
+
*/
|
|
47
|
+
export declare function assertAllowedOrigin(options: RefreshTokenCookieOptions, request: HttpRequestLike): void;
|
|
48
|
+
/**
|
|
49
|
+
* Checks whether the request comes from one of the web app's own origins (browsers send Origin).
|
|
50
|
+
*
|
|
51
|
+
* @param options - The app's cookie settings, including `allowedOrigins`.
|
|
52
|
+
* @param request - The incoming request.
|
|
53
|
+
* @returns True when the Origin header is in the allowed list.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isAllowedOrigin(options: RefreshTokenCookieOptions, request: HttpRequestLike): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Active only when AuthOptions.refreshTokenCookie is set, and only for requests from the allowed
|
|
58
|
+
* web origins, so mobile apps and servers on the same API keep getting the token in the body.
|
|
59
|
+
* On the library's routes:
|
|
60
|
+
* - a response carrying a refresh token: the token moves into the HttpOnly cookie and the body
|
|
61
|
+
* says `refreshTokenInCookie: true` instead,
|
|
62
|
+
* - routes marked @ClearsRefreshTokenCookie() (logout): the cookie is deleted.
|
|
63
|
+
*/
|
|
64
|
+
export declare class RefreshTokenCookieInterceptor implements NestInterceptor {
|
|
65
|
+
private readonly options;
|
|
66
|
+
private readonly reflector;
|
|
67
|
+
constructor(options: AuthOptions, reflector: Reflector);
|
|
68
|
+
/**
|
|
69
|
+
* Moves the refresh token from the response body into the cookie (or clears the cookie on logout).
|
|
70
|
+
* Requests that don't qualify pass through untouched.
|
|
71
|
+
*
|
|
72
|
+
* @param context - Nest's view of the current request.
|
|
73
|
+
* @param next - The route handler.
|
|
74
|
+
* @returns The response body, with `refreshToken` replaced by `refreshTokenInCookie: true` when it was moved.
|
|
75
|
+
*/
|
|
76
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
|
|
77
|
+
}
|
|
78
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RefreshTokenCookieInterceptor = exports.ClearsRefreshTokenCookie = exports.LibraryController = void 0;
|
|
16
|
+
exports.buildRefreshCookie = buildRefreshCookie;
|
|
17
|
+
exports.readRefreshCookie = readRefreshCookie;
|
|
18
|
+
exports.assertAllowedOrigin = assertAllowedOrigin;
|
|
19
|
+
exports.isAllowedOrigin = isAllowedOrigin;
|
|
20
|
+
const common_1 = require("@nestjs/common");
|
|
21
|
+
const core_1 = require("@nestjs/core");
|
|
22
|
+
const rxjs_1 = require("rxjs");
|
|
23
|
+
const constants_1 = require("../constants.js");
|
|
24
|
+
const auth_error_1 = require("./auth-error");
|
|
25
|
+
/**
|
|
26
|
+
* Marks the library's own controllers, so the refresh-token-cookie interceptor never touches the app's routes.
|
|
27
|
+
*
|
|
28
|
+
* @returns A class decorator.
|
|
29
|
+
*/
|
|
30
|
+
const LibraryController = () => (0, common_1.SetMetadata)(constants_1.METADATA_KEY.libraryController, true);
|
|
31
|
+
exports.LibraryController = LibraryController;
|
|
32
|
+
/**
|
|
33
|
+
* Marks a route (logout) after which the browser should forget the refresh token cookie.
|
|
34
|
+
*
|
|
35
|
+
* @returns A method decorator.
|
|
36
|
+
*/
|
|
37
|
+
const ClearsRefreshTokenCookie = () => (0, common_1.SetMetadata)(constants_1.METADATA_KEY.clearsRefreshTokenCookie, true);
|
|
38
|
+
exports.ClearsRefreshTokenCookie = ClearsRefreshTokenCookie;
|
|
39
|
+
/**
|
|
40
|
+
* Builds the Set-Cookie value for the refresh token:
|
|
41
|
+
* "name=value; Max-Age=...; Path=/; HttpOnly; Secure; SameSite=Strict"
|
|
42
|
+
*
|
|
43
|
+
* @param options - The app's cookie settings (name, path, domain, sameSite).
|
|
44
|
+
* @param value - The refresh token, or "" when deleting the cookie.
|
|
45
|
+
* @param maxAgeSeconds - How long the browser should keep it; 0 deletes the cookie.
|
|
46
|
+
* @returns The cookie string for the Set-Cookie header.
|
|
47
|
+
*/
|
|
48
|
+
function buildRefreshCookie(options, value, maxAgeSeconds) {
|
|
49
|
+
const parts = [
|
|
50
|
+
`${cookieName(options)}${constants_1.COOKIE_NAME_VALUE_SEPARATOR}${encodeURIComponent(value)}`,
|
|
51
|
+
`${constants_1.COOKIE_ATTRIBUTE.maxAge}${constants_1.COOKIE_NAME_VALUE_SEPARATOR}${Math.max(Math.floor(maxAgeSeconds), 0)}`,
|
|
52
|
+
`${constants_1.COOKIE_ATTRIBUTE.path}${constants_1.COOKIE_NAME_VALUE_SEPARATOR}${options.path ?? constants_1.REFRESH_TOKEN_COOKIE_DEFAULTS.path}`,
|
|
53
|
+
...(options.domain ? [`${constants_1.COOKIE_ATTRIBUTE.domain}${constants_1.COOKIE_NAME_VALUE_SEPARATOR}${options.domain}`] : []),
|
|
54
|
+
`${constants_1.COOKIE_ATTRIBUTE.sameSite}${constants_1.COOKIE_NAME_VALUE_SEPARATOR}${options.sameSite ?? constants_1.REFRESH_TOKEN_COOKIE_DEFAULTS.sameSite}`,
|
|
55
|
+
constants_1.COOKIE_ATTRIBUTE.httpOnly,
|
|
56
|
+
constants_1.COOKIE_ATTRIBUTE.secure,
|
|
57
|
+
];
|
|
58
|
+
return parts.join(constants_1.COOKIE_ATTRIBUTE_SEPARATOR);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Reads the refresh token from the request's Cookie header.
|
|
62
|
+
*
|
|
63
|
+
* @param options - The app's cookie settings (to know the cookie's name).
|
|
64
|
+
* @param request - The incoming request.
|
|
65
|
+
* @returns The refresh token, or undefined if the cookie is missing or malformed.
|
|
66
|
+
*/
|
|
67
|
+
function readRefreshCookie(options, request) {
|
|
68
|
+
const header = request.headers[constants_1.HTTP_HEADER.cookie];
|
|
69
|
+
const cookieHeader = Array.isArray(header) ? header.join(constants_1.COOKIE_PAIR_SEPARATOR) : header;
|
|
70
|
+
if (!cookieHeader)
|
|
71
|
+
return undefined;
|
|
72
|
+
const wantedName = cookieName(options);
|
|
73
|
+
for (const pair of cookieHeader.split(constants_1.COOKIE_PAIR_SEPARATOR)) {
|
|
74
|
+
const separatorIndex = pair.indexOf(constants_1.COOKIE_NAME_VALUE_SEPARATOR);
|
|
75
|
+
if (separatorIndex > 0 && pair.slice(0, separatorIndex).trim() === wantedName) {
|
|
76
|
+
try {
|
|
77
|
+
return decodeURIComponent(pair.slice(separatorIndex + 1).trim());
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Cross-site request protection for cookie refreshes: the browser always sends the Origin header
|
|
88
|
+
* on these requests, and it must be one of the app's own websites.
|
|
89
|
+
*
|
|
90
|
+
* @param options - The app's cookie settings, including `allowedOrigins`.
|
|
91
|
+
* @param request - The incoming request.
|
|
92
|
+
* @throws AuthError FORBIDDEN when the origin is missing or not in the list.
|
|
93
|
+
*/
|
|
94
|
+
function assertAllowedOrigin(options, request) {
|
|
95
|
+
if (!isAllowedOrigin(options, request))
|
|
96
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.originNotAllowed);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* @param options - The app's cookie settings.
|
|
100
|
+
* @returns The configured cookie name, or the default one.
|
|
101
|
+
*/
|
|
102
|
+
function cookieName(options) {
|
|
103
|
+
return options.name ?? constants_1.REFRESH_TOKEN_COOKIE_DEFAULTS.name;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Adds a Set-Cookie header without removing cookies that other code has already set.
|
|
107
|
+
*
|
|
108
|
+
* @param response - The Express or Fastify response.
|
|
109
|
+
* @param cookie - The full cookie string from `buildRefreshCookie`.
|
|
110
|
+
*/
|
|
111
|
+
function setCookieHeader(response, cookie) {
|
|
112
|
+
// Express: append() keeps cookies set by other code. Fastify: header() appends Set-Cookie itself.
|
|
113
|
+
if (typeof response.append === 'function')
|
|
114
|
+
response.append(constants_1.HTTP_HEADER.setCookie, cookie);
|
|
115
|
+
else
|
|
116
|
+
response.header(constants_1.HTTP_HEADER.setCookie, cookie);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Checks whether the request comes from one of the web app's own origins (browsers send Origin).
|
|
120
|
+
*
|
|
121
|
+
* @param options - The app's cookie settings, including `allowedOrigins`.
|
|
122
|
+
* @param request - The incoming request.
|
|
123
|
+
* @returns True when the Origin header is in the allowed list.
|
|
124
|
+
*/
|
|
125
|
+
function isAllowedOrigin(options, request) {
|
|
126
|
+
const origin = request.headers[constants_1.HTTP_HEADER.origin];
|
|
127
|
+
return typeof origin === 'string' && options.allowedOrigins.includes(origin);
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Active only when AuthOptions.refreshTokenCookie is set, and only for requests from the allowed
|
|
131
|
+
* web origins, so mobile apps and servers on the same API keep getting the token in the body.
|
|
132
|
+
* On the library's routes:
|
|
133
|
+
* - a response carrying a refresh token: the token moves into the HttpOnly cookie and the body
|
|
134
|
+
* says `refreshTokenInCookie: true` instead,
|
|
135
|
+
* - routes marked @ClearsRefreshTokenCookie() (logout): the cookie is deleted.
|
|
136
|
+
*/
|
|
137
|
+
let RefreshTokenCookieInterceptor = class RefreshTokenCookieInterceptor {
|
|
138
|
+
options;
|
|
139
|
+
reflector;
|
|
140
|
+
constructor(options, reflector) {
|
|
141
|
+
this.options = options;
|
|
142
|
+
this.reflector = reflector;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Moves the refresh token from the response body into the cookie (or clears the cookie on logout).
|
|
146
|
+
* Requests that don't qualify pass through untouched.
|
|
147
|
+
*
|
|
148
|
+
* @param context - Nest's view of the current request.
|
|
149
|
+
* @param next - The route handler.
|
|
150
|
+
* @returns The response body, with `refreshToken` replaced by `refreshTokenInCookie: true` when it was moved.
|
|
151
|
+
*/
|
|
152
|
+
intercept(context, next) {
|
|
153
|
+
const cookieOptions = this.options.refreshTokenCookie;
|
|
154
|
+
if (!cookieOptions || context.getType() !== constants_1.HTTP_CONTEXT_TYPE)
|
|
155
|
+
return next.handle();
|
|
156
|
+
if (this.reflector.get(constants_1.METADATA_KEY.libraryController, context.getClass()) !== true)
|
|
157
|
+
return next.handle();
|
|
158
|
+
if (!isAllowedOrigin(cookieOptions, context.switchToHttp().getRequest()))
|
|
159
|
+
return next.handle();
|
|
160
|
+
const response = context.switchToHttp().getResponse();
|
|
161
|
+
const clearsCookie = this.reflector.get(constants_1.METADATA_KEY.clearsRefreshTokenCookie, context.getHandler()) === true;
|
|
162
|
+
return next.handle().pipe((0, rxjs_1.map)((body) => {
|
|
163
|
+
if (clearsCookie) {
|
|
164
|
+
setCookieHeader(response, buildRefreshCookie(cookieOptions, '', 0));
|
|
165
|
+
return body;
|
|
166
|
+
}
|
|
167
|
+
if (!body || typeof body !== 'object')
|
|
168
|
+
return body;
|
|
169
|
+
const { [constants_1.REFRESH_TOKEN_RESPONSE_FIELD]: refreshToken, ...rest } = body;
|
|
170
|
+
if (typeof refreshToken !== 'string')
|
|
171
|
+
return body;
|
|
172
|
+
const expiresAt = Date.parse(String(rest[constants_1.REFRESH_TOKEN_EXPIRES_AT_RESPONSE_FIELD]));
|
|
173
|
+
const maxAgeSeconds = Number.isNaN(expiresAt) ? 0 : (expiresAt - Date.now()) / constants_1.SECOND_IN_MILLISECONDS;
|
|
174
|
+
setCookieHeader(response, buildRefreshCookie(cookieOptions, refreshToken, maxAgeSeconds));
|
|
175
|
+
return { ...rest, [constants_1.REFRESH_TOKEN_IN_COOKIE_FIELD]: true };
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
exports.RefreshTokenCookieInterceptor = RefreshTokenCookieInterceptor;
|
|
180
|
+
exports.RefreshTokenCookieInterceptor = RefreshTokenCookieInterceptor = __decorate([
|
|
181
|
+
(0, common_1.Injectable)(),
|
|
182
|
+
__param(0, (0, common_1.Inject)(constants_1.AUTH_OPTIONS_TOKEN)),
|
|
183
|
+
__metadata("design:paramtypes", [Object, core_1.Reflector])
|
|
184
|
+
], RefreshTokenCookieInterceptor);
|
|
185
|
+
//# sourceMappingURL=refresh-token-cookie.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh-token-cookie.js","sourceRoot":"","sources":["../../src/common/refresh-token-cookie.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Who is making the request. Used for rate limits, the device list and the audit log. */
|
|
2
|
+
export interface RequestContext {
|
|
3
|
+
ipAddress: string | null;
|
|
4
|
+
userAgent: string | null;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Controller parameter decorator: @CurrentRequest() request: RequestContext
|
|
8
|
+
*
|
|
9
|
+
* The IP comes from Express's `request.ip`. Behind a load balancer, set app.set('trust proxy', 1)
|
|
10
|
+
* in your main.ts, or every request will appear to come from the load balancer.
|
|
11
|
+
*/
|
|
12
|
+
export declare const CurrentRequest: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
13
|
+
/** For calls made from your own code (scripts, admin tools) rather than an HTTP request. */
|
|
14
|
+
export declare const SYSTEM_REQUEST: RequestContext;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SYSTEM_REQUEST = exports.CurrentRequest = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
const constants_1 = require("../constants.js");
|
|
6
|
+
/**
|
|
7
|
+
* Controller parameter decorator: @CurrentRequest() request: RequestContext
|
|
8
|
+
*
|
|
9
|
+
* The IP comes from Express's `request.ip`. Behind a load balancer, set app.set('trust proxy', 1)
|
|
10
|
+
* in your main.ts, or every request will appear to come from the load balancer.
|
|
11
|
+
*/
|
|
12
|
+
exports.CurrentRequest = (0, common_1.createParamDecorator)((_unused, executionContext) => {
|
|
13
|
+
const request = executionContext.switchToHttp().getRequest();
|
|
14
|
+
const userAgent = request.headers?.[constants_1.HTTP_HEADER.userAgent];
|
|
15
|
+
return {
|
|
16
|
+
ipAddress: request.ip ?? null,
|
|
17
|
+
userAgent: userAgent ? userAgent.slice(0, constants_1.USER_AGENT_MAX_LENGTH) : null,
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
/** For calls made from your own code (scripts, admin tools) rather than an HTTP request. */
|
|
21
|
+
exports.SYSTEM_REQUEST = { ipAddress: null, userAgent: constants_1.SYSTEM_USER_AGENT };
|
|
22
|
+
//# sourceMappingURL=request-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-context.js","sourceRoot":"","sources":["../../src/common/request-context.ts"],"names":[],"mappings":";;;AAAA,2CAAwE;AACxE,2CAAoF;AAQpF;;;;;GAKG;AACU,QAAA,cAAc,GAAG,IAAA,6BAAoB,EAAC,CAAC,OAAgB,EAAE,gBAAkC,EAAkB,EAAE;IAC1H,MAAM,OAAO,GAAG,gBAAgB,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;IAC7D,MAAM,SAAS,GAAuB,OAAO,CAAC,OAAO,EAAE,CAAC,uBAAW,CAAC,SAAS,CAAC,CAAC;IAC/E,OAAO;QACL,SAAS,EAAE,OAAO,CAAC,EAAE,IAAI,IAAI;QAC7B,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,iCAAqB,CAAC,CAAC,CAAC,CAAC,IAAI;KACxE,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,4FAA4F;AAC/E,QAAA,cAAc,GAAmB,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,6BAAiB,EAAE,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ValidationPipe } from '@nestjs/common';
|
|
2
|
+
/**
|
|
3
|
+
* Checks request bodies against their DTO class.
|
|
4
|
+
* - whitelist + forbidNonWhitelisted: unknown fields are rejected, so a client cannot sneak in
|
|
5
|
+
* fields such as "roles" or "status".
|
|
6
|
+
* - transform: the body becomes an instance of the DTO, so @Transform (e.g. trimming) runs.
|
|
7
|
+
*/
|
|
8
|
+
export declare const strictValidationPipe: ValidationPipe;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.strictValidationPipe = void 0;
|
|
4
|
+
const common_1 = require("@nestjs/common");
|
|
5
|
+
/**
|
|
6
|
+
* Checks request bodies against their DTO class.
|
|
7
|
+
* - whitelist + forbidNonWhitelisted: unknown fields are rejected, so a client cannot sneak in
|
|
8
|
+
* fields such as "roles" or "status".
|
|
9
|
+
* - transform: the body becomes an instance of the DTO, so @Transform (e.g. trimming) runs.
|
|
10
|
+
*/
|
|
11
|
+
exports.strictValidationPipe = new common_1.ValidationPipe({
|
|
12
|
+
whitelist: true,
|
|
13
|
+
forbidNonWhitelisted: true,
|
|
14
|
+
transform: true,
|
|
15
|
+
});
|
|
16
|
+
//# sourceMappingURL=strict-validation.pipe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strict-validation.pipe.js","sourceRoot":"","sources":["../../src/common/strict-validation.pipe.ts"],"names":[],"mappings":";;;AAAA,2CAAgD;AAEhD;;;;;GAKG;AACU,QAAA,oBAAoB,GAAG,IAAI,uBAAc,CAAC;IACrD,SAAS,EAAE,IAAI;IACf,oBAAoB,EAAE,IAAI;IAC1B,SAAS,EAAE,IAAI;CAChB,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The current time in whole seconds since 1970 (the unit JWTs, TOTP and webhook signatures use).
|
|
3
|
+
*
|
|
4
|
+
* @returns The Unix time in seconds.
|
|
5
|
+
*/
|
|
6
|
+
export declare function currentUnixTimeSeconds(): number;
|
|
7
|
+
/**
|
|
8
|
+
* Waits for `work`, but gives up if it takes too long (used by health checks).
|
|
9
|
+
*
|
|
10
|
+
* @param work - The promise to wait for.
|
|
11
|
+
* @param timeoutMs - How long to wait, in milliseconds.
|
|
12
|
+
* @returns What `work` resolved to.
|
|
13
|
+
* @throws If `work` rejects, or with "Timed out after N ms" when the time runs out.
|
|
14
|
+
*/
|
|
15
|
+
export declare function withTimeout<Result>(work: Promise<Result>, timeoutMs: number): Promise<Result>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.currentUnixTimeSeconds = currentUnixTimeSeconds;
|
|
4
|
+
exports.withTimeout = withTimeout;
|
|
5
|
+
const constants_1 = require("../constants.js");
|
|
6
|
+
/**
|
|
7
|
+
* The current time in whole seconds since 1970 (the unit JWTs, TOTP and webhook signatures use).
|
|
8
|
+
*
|
|
9
|
+
* @returns The Unix time in seconds.
|
|
10
|
+
*/
|
|
11
|
+
function currentUnixTimeSeconds() {
|
|
12
|
+
return Math.floor(Date.now() / constants_1.SECOND_IN_MILLISECONDS);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Waits for `work`, but gives up if it takes too long (used by health checks).
|
|
16
|
+
*
|
|
17
|
+
* @param work - The promise to wait for.
|
|
18
|
+
* @param timeoutMs - How long to wait, in milliseconds.
|
|
19
|
+
* @returns What `work` resolved to.
|
|
20
|
+
* @throws If `work` rejects, or with "Timed out after N ms" when the time runs out.
|
|
21
|
+
*/
|
|
22
|
+
async function withTimeout(work, timeoutMs) {
|
|
23
|
+
let timer;
|
|
24
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
25
|
+
timer = setTimeout(() => reject(new Error(`Timed out after ${timeoutMs} ms`)), timeoutMs);
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
return await Promise.race([work, timeout]);
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
clearTimeout(timer);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sourceRoot":"","sources":["../../src/common/time.ts"],"names":[],"mappings":";;AAOA,wDAEC;AAUD,kCAUC;AA7BD,2CAAqD;AAErD;;;;GAIG;AACH,SAAgB,sBAAsB;IACpC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,kCAAsB,CAAC,CAAC;AACzD,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,WAAW,CAAS,IAAqB,EAAE,SAAiB;IAChF,IAAI,KAAiC,CAAC;IACtC,MAAM,OAAO,GAAG,IAAI,OAAO,CAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE;QACtD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,SAAS,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IACH,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC"}
|