@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,1142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SIGNING_KEYS_CACHE_SECONDS = exports.SETTINGS_ROW_ID = exports.RATE_LIMIT_REDIS_KEY_PREFIX = exports.PASSKEY_NAME_MAX_LENGTH = exports.MAXIMUM_PASSKEYS_PER_USER = exports.DEFAULT_PASSKEY_NAME = exports.PASSKEY_USER_VERIFICATION = exports.PASSKEY_RESIDENT_KEY = exports.PASSKEY_ATTESTATION_TYPE = exports.RATE_LIMITS = exports.BACKUP_CODE_ALPHABET = exports.BACKUP_CODE_DISPLAY_GROUP_LENGTH = exports.BACKUP_CODE_LENGTH = exports.BACKUP_CODE_COUNT = exports.TOTP_DEFAULT_ISSUER_NAME = exports.TOTP_SECRET_BYTES = exports.TOTP_ALLOWED_DRIFT_STEPS = exports.TOTP_PERIOD_SECONDS = exports.TOTP_DIGITS = exports.BASE32_ALPHABET = exports.PASSWORD_SYMBOL_PATTERN = exports.BREACHED_PASSWORD_HASH_PREFIX_LENGTH = exports.BREACHED_PASSWORD_CHECK_TIMEOUT_MS = exports.BREACHED_PASSWORD_API_URL = exports.ARGON2_HASHING_OPTIONS = exports.PASSWORD_MIN_LENGTH_FLOOR = exports.PASSWORD_MAX_LENGTH = exports.TWO_FACTOR_MAX_ATTEMPTS = exports.ONE_TIME_CODE_MAX_ATTEMPTS = exports.REACTIVATION_CHALLENGE_LIFETIME_SECONDS = exports.PASSKEY_CHALLENGE_LIFETIME_SECONDS = exports.RECENT_SIGN_IN_SECONDS = exports.UNVERIFIED_ACCOUNT_GRACE_SECONDS = exports.LINK_TOKEN_KINDS = exports.CLOCK_SKEW_TOLERANCE_SECONDS = exports.PROVIDER_ID_TOKEN_ALGORITHM = exports.SIGNING_ALGORITHM = exports.JWT_ALGORITHM = exports.ACCESS_TOKEN_JWT_TYPE = exports.RANDOM_TOKEN_BYTES = exports.TOKEN_PREFIX = exports.AUTH_OPTIONS_TOKEN = exports.AES_GCM_AUTH_TAG_BYTES = exports.AES_GCM_INITIALIZATION_VECTOR_BYTES = exports.LOCAL_ENCRYPTION_KEY_BYTES = exports.POSTGRES_UNIQUE_VIOLATION_CODE = exports.DAY_IN_SECONDS = exports.HOUR_IN_SECONDS = exports.MINUTE_IN_SECONDS = exports.SECOND_IN_MILLISECONDS = void 0;
|
|
4
|
+
exports.EMAIL_PATTERN = exports.EMAIL_MAX_LENGTH = exports.PHONE_NUMBER_PATTERN = exports.SUSPENSION_REASON_MAX_LENGTH = exports.MAXIMUM_ROLES_PER_USER = exports.DEFAULT_ADMIN_ROLE = exports.DEFAULT_USER_ROLE = exports.ROLE_NAME_PATTERN = exports.MAXIMUM_ORGANIZATIONS_PER_USER = exports.ORGANIZATION_ROLE_RANK = exports.ORGANIZATION_ROLES = exports.ORGANIZATION_ROLE = exports.ACCOUNT_STATUSES = exports.ACCOUNT_STATUS = exports.BLOCKED_IPV6_RANGES = exports.BLOCKED_IPV4_RANGES = exports.WEBHOOK_USER_AGENT = exports.WEBHOOK_DESCRIPTION_MAX_LENGTH = exports.WEBHOOK_URL_MAX_LENGTH = exports.MAXIMUM_WEBHOOKS = exports.WEBHOOK_ERROR_MAX_LENGTH = exports.WEBHOOK_CLAIM_TIMEOUT_SECONDS = exports.WEBHOOK_SECRET_PREFIX = exports.WEBHOOK_SECRET_BYTES = exports.WEBHOOK_WORKER_BATCH_SIZE = exports.WEBHOOK_WORKER_INTERVAL_MS = exports.WEBHOOK_RETRY_DELAYS_SECONDS = exports.WEBHOOK_REQUEST_TIMEOUT_MS = exports.WEBHOOK_SIGNATURE_TOLERANCE_SECONDS = exports.WEBHOOK_DELIVERY_ID_HEADER = exports.WEBHOOK_EVENT_HEADER = exports.WEBHOOK_SIGNATURE_HEADER = exports.MSG91_REQUEST_TIMEOUT_MS = exports.MSG91_FLOW_API_URL = exports.FACEBOOK_PROFILE_FIELDS = exports.NONCE_INPUT_MAX_LENGTH = exports.SOCIAL_TOKEN_INPUT_MAX_LENGTH = exports.UNKNOWN_KEY_REFETCH_WAIT_SECONDS = exports.SOCIAL_PROVIDER_TIMEOUT_MS = exports.SOCIAL_PUBLIC_KEYS_CACHE_SECONDS = exports.FACEBOOK_GRAPH_API_URL = exports.APPLE_TOKEN_ISSUER = exports.APPLE_PUBLIC_KEYS_URL = exports.GOOGLE_TOKEN_ISSUERS = exports.GOOGLE_PUBLIC_KEYS_URL = exports.SOCIAL_PROVIDERS = exports.SOCIAL_PROVIDER = exports.RETIRED_SIGNING_KEY_PUBLISH_SECONDS = exports.SESSION_CHECK_CACHE_SECONDS = exports.SETTINGS_CACHE_SECONDS = void 0;
|
|
5
|
+
exports.ROUTES = exports.ROUTE_PARAM = exports.TOTP_URL_ALGORITHM_NAME = exports.URL_QUERY_SEPARATOR = exports.AUTHENTICATOR_APP_URL_PREFIX = exports.SHORT_CODE_SALT_BYTES = exports.ENCRYPTED_VALUE_SEPARATOR = exports.JWK_USE_SIGNATURE = exports.JWK_KEY_TYPE = exports.JWK_EXPORT_FORMAT = exports.SIGNING_KEY_CURVE = exports.SIGNING_KEY_PAIR_TYPE = exports.ECDSA_SIGNATURE_ENCODING = exports.LOCAL_ENCRYPTION_CIPHER = exports.HASH_ALGORITHM = exports.TEXT_ENCODING = exports.PRODUCTION_ENVIRONMENT = exports.FIRST_PAGE = exports.SORT_ORDER = exports.ENCRYPTED_VALUE_METHOD = exports.ENCRYPTION_PURPOSE = exports.PERFORMED_BY_PREFIX = exports.REMOVABLE_SIGN_IN_METHOD = exports.PASSWORD_CHANGE_KIND = exports.SIGN_IN_FAILURE_REASON = exports.SESSION_END_REASON = exports.EMAIL_TYPE = exports.WEBHOOK_TEST_EVENT_NAME = exports.WEBHOOK_DELIVERY_STATUS = exports.CODE_CHANNEL = exports.LINK_PURPOSE = exports.CHALLENGE_PURPOSE = exports.PENDING_VERIFICATION = exports.SIGN_UP_DETAIL = exports.SIGN_IN_STATUS = exports.TWO_FACTOR_AMR_VALUE = exports.SIGN_IN_METHOD = exports.OAUTH_CLIENT_ID_PATTERN = exports.COUNTRY_CALLING_CODE_PATTERN = exports.EMAIL_DOMAIN_PATTERN = exports.SETTINGS_LIMITS = exports.MAXIMUM_PAGE_SIZE = exports.DEFAULT_PAGE_SIZE = exports.USER_AGENT_MAX_LENGTH = exports.ORGANIZATION_SLUG_PATTERN = exports.ORGANIZATION_NAME_MAX_LENGTH = exports.CODE_INPUT_MAX_LENGTH = exports.RANDOM_TOKEN_INPUT_MAX_LENGTH = exports.PHONE_NUMBER_INPUT_MAX_LENGTH = exports.DISPLAY_NAME_MAX_LENGTH = void 0;
|
|
6
|
+
exports.DECIMAL_BASE = exports.SALTED_CODE_SEPARATOR = exports.RANDOM_TOKEN_BASE64URL_LENGTH = exports.TOKEN_PREFIX_SEPARATOR = exports.JWT_PART_COUNT = exports.JWT_PART_SEPARATOR = exports.MSG91_API = exports.APPLE_TRUE_STRING = exports.FACEBOOK_API = exports.BREACHED_PASSWORD_API = exports.URL_IPV6_BRACKETS_PATTERN = exports.IPV4_MAPPED_IPV6_PATTERN = exports.IP_VERSION_6 = exports.IP_FAMILY = exports.LOCAL_DEVELOPMENT_HOSTS = exports.URL_PROTOCOL = exports.SUCCESS_STATUS_RANGE = exports.FETCH_REDIRECT_MODE_MANUAL = exports.HTTP_METHOD = exports.WEBHOOK_TEST_MESSAGE = exports.WEBHOOK_DELIVERY_ERROR = exports.URL_SAFETY_PROBLEM = exports.OPTIONS_PROBLEM = exports.VALIDATION_PROBLEM = exports.DIGIT_PATTERN = exports.LOWERCASE_LETTER_PATTERN = exports.UPPERCASE_LETTER_PATTERN = exports.SETTINGS_PATH_SEPARATOR = exports.SQL_LIKE_ANY_TEXT = exports.SQL_LIKE_ESCAPE_CHARACTER = exports.SQL_LIKE_SPECIAL_CHARACTERS_PATTERN = exports.DIGITS_ONLY_PATTERN = exports.BASE64URL_TEXT_PATTERN = exports.LEADING_PLUS_PATTERN = exports.BASE32_PADDING_PATTERN = exports.WHITESPACE_PATTERN = exports.BACKUP_CODE_FORMATTING_CHARACTERS_PATTERN = exports.PHONE_FORMATTING_CHARACTERS_PATTERN = exports.TRAILING_SLASHES_PATTERN = exports.PASSWORD_REQUIREMENT = exports.IN_MEMORY_RATE_LIMIT_MAX_ENTRIES = exports.SESSION_CHECK_CACHE_MAX_ENTRIES = exports.LOG_CONTEXT = exports.SYSTEM_USER_AGENT = exports.PERFORMED_BY = exports.RATE_LIMIT_KEY = exports.JWKS_CACHE_CONTROL = exports.HTTP_CONTEXT_TYPE = exports.JSON_CONTENT_TYPE = exports.HTTP_HEADER = void 0;
|
|
7
|
+
exports.ADMIN_STATS_WINDOW_SECONDS = exports.DATA_EXPORT_AUDIT_LOG_LIMIT = exports.HEALTH_SERVICE_UNAVAILABLE_STATUS = exports.HEALTH_CHECK_QUERY = exports.HEALTH_CHECK_TIMEOUT_MS = exports.HEALTH_STATUS = exports.REFRESH_TOKEN_EXPIRES_AT_RESPONSE_FIELD = exports.REFRESH_TOKEN_IN_COOKIE_FIELD = exports.REFRESH_TOKEN_RESPONSE_FIELD = exports.COOKIE_NAME_VALUE_SEPARATOR = exports.COOKIE_ATTRIBUTE_SEPARATOR = exports.COOKIE_PAIR_SEPARATOR = exports.COOKIE_ATTRIBUTE = exports.REFRESH_TOKEN_COOKIE_DEFAULTS = exports.COOKIE_SAME_SITE = exports.HOST_COOKIE_PREFIX = exports.DATA_RETENTION_BATCH_CONDITION = exports.DATA_RETENTION_OLD_ROW_CONDITION = exports.DATA_RETENTION_STEP = exports.DATA_RETENTION_MAX_BATCHES_PER_TABLE = exports.DATA_RETENTION_BATCH_SIZE = exports.DATA_RETENTION_FIRST_RUN_DELAY_MS = exports.DATA_RETENTION_INTERVAL_MS = exports.DEFAULT_SETTINGS = exports.ERROR_CODE_BY_HTTP_STATUS = exports.AUTH_ERRORS = exports.SUCCESS_MESSAGES = exports.AUTH_EVENTS = exports.API_KEY_HEADER = exports.API_KEY_LAST_USED_UPDATE_SECONDS = exports.API_KEY_PREVIEW_LENGTH = exports.API_KEY_SCOPE_PATTERN = exports.MAXIMUM_API_KEY_SCOPES = exports.API_KEY_NAME_MAX_LENGTH = exports.BEARER_TOKEN_PATTERN = exports.CALLER_REQUEST_PROPERTY = exports.METADATA_KEY = exports.WEBHOOK_SIGNATURE_FORMAT = exports.RELYING_PARTY_ID_FORBIDDEN_CHARACTERS = exports.PHONE_MASK = exports.EMAIL_MASK = exports.DISPLAY_NAME_SEPARATOR = exports.BACKUP_CODE_DISPLAY_SEPARATOR = exports.BYTE_MASK = exports.BASE32_CHARACTER_MASK = exports.BITS_PER_BYTE = exports.BASE32_BITS_PER_CHARACTER = exports.TOTP_LABEL_SEPARATOR = exports.TOTP_CODE_PAD_CHARACTER = exports.TOTP_COUNTER_BYTES = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Every fixed number, name and limit used by the library lives in this file.
|
|
10
|
+
*
|
|
11
|
+
* Values that an app may want to change at runtime (password length, token lifetimes, which
|
|
12
|
+
* sign-in methods are on) are NOT here: they are settings, see `features/settings/settings.defaults.ts`.
|
|
13
|
+
* What stays here are security limits that should only change with a code review.
|
|
14
|
+
*/
|
|
15
|
+
// ---------------------------------------------------------------------------------------------
|
|
16
|
+
// Time units (in seconds) so lifetimes below read naturally, e.g. 15 * MINUTE_IN_SECONDS.
|
|
17
|
+
// ---------------------------------------------------------------------------------------------
|
|
18
|
+
exports.SECOND_IN_MILLISECONDS = 1000;
|
|
19
|
+
exports.MINUTE_IN_SECONDS = 60;
|
|
20
|
+
exports.HOUR_IN_SECONDS = 60 * exports.MINUTE_IN_SECONDS;
|
|
21
|
+
exports.DAY_IN_SECONDS = 24 * exports.HOUR_IN_SECONDS;
|
|
22
|
+
// ---------------------------------------------------------------------------------------------
|
|
23
|
+
// Dependency-injection token for the options passed to AuthModule.forRoot().
|
|
24
|
+
// ---------------------------------------------------------------------------------------------
|
|
25
|
+
/** Postgres error code for "a unique index would be broken", e.g. an email that is already used. */
|
|
26
|
+
exports.POSTGRES_UNIQUE_VIOLATION_CODE = '23505';
|
|
27
|
+
/** Size of the development-only local encryption key (AES-256). */
|
|
28
|
+
exports.LOCAL_ENCRYPTION_KEY_BYTES = 32;
|
|
29
|
+
/** AES-GCM: 12-byte random IV per value, 16-byte authentication tag. */
|
|
30
|
+
exports.AES_GCM_INITIALIZATION_VECTOR_BYTES = 12;
|
|
31
|
+
exports.AES_GCM_AUTH_TAG_BYTES = 16;
|
|
32
|
+
exports.AUTH_OPTIONS_TOKEN = 'TECHELUSIVE_AUTH_OPTIONS';
|
|
33
|
+
// ---------------------------------------------------------------------------------------------
|
|
34
|
+
// Tokens we hand out. We store only a SHA-256 hash of each, never the token itself.
|
|
35
|
+
// The prefix makes a leaked token easy to recognise (e.g. by secret scanners) and to route.
|
|
36
|
+
// ---------------------------------------------------------------------------------------------
|
|
37
|
+
exports.TOKEN_PREFIX = {
|
|
38
|
+
refreshToken: 'rt',
|
|
39
|
+
emailVerificationLink: 'ev',
|
|
40
|
+
passwordResetLink: 'pr',
|
|
41
|
+
magicSignInLink: 'ml',
|
|
42
|
+
emailChangeLink: 'ec',
|
|
43
|
+
organizationInvitation: 'oi',
|
|
44
|
+
signInChallenge: 'ch',
|
|
45
|
+
apiKey: 'ak',
|
|
46
|
+
};
|
|
47
|
+
/** Random bytes in every token above: 32 bytes = 256 bits, impossible to guess. */
|
|
48
|
+
exports.RANDOM_TOKEN_BYTES = 32;
|
|
49
|
+
/** Access tokens use the JWT "typ" from RFC 9068, so an access token can't be confused with other JWTs. */
|
|
50
|
+
exports.ACCESS_TOKEN_JWT_TYPE = 'at+jwt';
|
|
51
|
+
/** The only algorithm we sign with: ECDSA P-256 + SHA-256. */
|
|
52
|
+
/** JWT signature algorithms: ES256 for our own tokens, RS256 for Google and Apple ID tokens. */
|
|
53
|
+
exports.JWT_ALGORITHM = { es256: 'ES256', rs256: 'RS256' };
|
|
54
|
+
exports.SIGNING_ALGORITHM = exports.JWT_ALGORITHM.es256;
|
|
55
|
+
/** Algorithm Google and Apple sign their ID tokens with. */
|
|
56
|
+
exports.PROVIDER_ID_TOKEN_ALGORITHM = exports.JWT_ALGORITHM.rs256;
|
|
57
|
+
/** Allowed difference between our clock and the token's timestamps. */
|
|
58
|
+
exports.CLOCK_SKEW_TOLERANCE_SECONDS = 30;
|
|
59
|
+
// ---------------------------------------------------------------------------------------------
|
|
60
|
+
// Single-use links and short-lived challenges. How long each link works is a setting
|
|
61
|
+
// (emailVerification, passwordReset, magicLink, emailChange); see DEFAULT_SETTINGS.
|
|
62
|
+
// ---------------------------------------------------------------------------------------------
|
|
63
|
+
/** For each kind of emailed link: the prefix of its token. */
|
|
64
|
+
exports.LINK_TOKEN_KINDS = {
|
|
65
|
+
verify_email: { prefix: exports.TOKEN_PREFIX.emailVerificationLink },
|
|
66
|
+
reset_password: { prefix: exports.TOKEN_PREFIX.passwordResetLink },
|
|
67
|
+
magic_sign_in: { prefix: exports.TOKEN_PREFIX.magicSignInLink },
|
|
68
|
+
change_email: { prefix: exports.TOKEN_PREFIX.emailChangeLink },
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* An account whose email is still unverified after this long is treated as possibly created by
|
|
72
|
+
* someone else; if the real owner then proves the email by code, link or Google, any password set
|
|
73
|
+
* on it is removed (see UsersService.removeUnprovenPassword).
|
|
74
|
+
*/
|
|
75
|
+
exports.UNVERIFIED_ACCOUNT_GRACE_SECONDS = 24 * exports.HOUR_IN_SECONDS;
|
|
76
|
+
/**
|
|
77
|
+
* Users without a password must have signed in this recently to change their email (proof that
|
|
78
|
+
* the person holding the session is the owner, not someone who found an unlocked device).
|
|
79
|
+
*/
|
|
80
|
+
exports.RECENT_SIGN_IN_SECONDS = 10 * exports.MINUTE_IN_SECONDS;
|
|
81
|
+
/** A passkey ceremony (browser prompt) must finish within this time. */
|
|
82
|
+
exports.PASSKEY_CHALLENGE_LIFETIME_SECONDS = 5 * exports.MINUTE_IN_SECONDS;
|
|
83
|
+
/** How long a deactivated user has, after proving who they are, to confirm reactivation. */
|
|
84
|
+
exports.REACTIVATION_CHALLENGE_LIFETIME_SECONDS = 10 * exports.MINUTE_IN_SECONDS;
|
|
85
|
+
// ---------------------------------------------------------------------------------------------
|
|
86
|
+
// One-time codes sent by email or SMS.
|
|
87
|
+
// ---------------------------------------------------------------------------------------------
|
|
88
|
+
/** Wrong guesses allowed per code before it stops working. 5 guesses of a 6-digit code = 1 in 200,000 chance. */
|
|
89
|
+
exports.ONE_TIME_CODE_MAX_ATTEMPTS = 5;
|
|
90
|
+
/** Wrong 2FA codes allowed per sign-in attempt before the user must start again. */
|
|
91
|
+
exports.TWO_FACTOR_MAX_ATTEMPTS = 5;
|
|
92
|
+
// ---------------------------------------------------------------------------------------------
|
|
93
|
+
// Passwords.
|
|
94
|
+
// ---------------------------------------------------------------------------------------------
|
|
95
|
+
/** Longer passwords are refused so nobody can make the server hash megabytes of text. */
|
|
96
|
+
exports.PASSWORD_MAX_LENGTH = 128;
|
|
97
|
+
/** The lowest minimum length an app may configure (NIST SP 800-63B). */
|
|
98
|
+
exports.PASSWORD_MIN_LENGTH_FLOOR = 8;
|
|
99
|
+
/** Argon2id settings recommended by OWASP: 64 MB of memory, 3 passes, 1 thread. */
|
|
100
|
+
exports.ARGON2_HASHING_OPTIONS = { memoryCost: 64 * 1024, timeCost: 3, parallelism: 1 };
|
|
101
|
+
/** Have I Been Pwned range API. Only the first 5 characters of the password's SHA-1 are sent. */
|
|
102
|
+
exports.BREACHED_PASSWORD_API_URL = 'https://api.pwnedpasswords.com/range/';
|
|
103
|
+
/** If the breach check is slower than this, skip it rather than block the user. */
|
|
104
|
+
exports.BREACHED_PASSWORD_CHECK_TIMEOUT_MS = 2000;
|
|
105
|
+
/** Characters of the password's SHA-1 hash sent to the breach service (its "k-anonymity" range). */
|
|
106
|
+
exports.BREACHED_PASSWORD_HASH_PREFIX_LENGTH = 5;
|
|
107
|
+
/** What counts as a "symbol" when settings.password.requireSymbol is on: anything not a letter, digit or space. */
|
|
108
|
+
exports.PASSWORD_SYMBOL_PATTERN = /[^\p{L}\p{N}\s]/u;
|
|
109
|
+
// ---------------------------------------------------------------------------------------------
|
|
110
|
+
// Two-factor authentication (authenticator apps, RFC 6238).
|
|
111
|
+
// ---------------------------------------------------------------------------------------------
|
|
112
|
+
/** RFC 4648 base32 alphabet, used for authenticator-app secrets. */
|
|
113
|
+
exports.BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
|
|
114
|
+
exports.TOTP_DIGITS = 6;
|
|
115
|
+
exports.TOTP_PERIOD_SECONDS = 30;
|
|
116
|
+
/** Accept codes from one 30-second step before or after now, for phones with slightly wrong clocks. */
|
|
117
|
+
exports.TOTP_ALLOWED_DRIFT_STEPS = 1;
|
|
118
|
+
/** 20 random bytes = 160 bits, the size RFC 4226 recommends. */
|
|
119
|
+
exports.TOTP_SECRET_BYTES = 20;
|
|
120
|
+
/** Name shown in the user's authenticator app next to the code. Apps can override it. */
|
|
121
|
+
exports.TOTP_DEFAULT_ISSUER_NAME = 'Techelusive';
|
|
122
|
+
exports.BACKUP_CODE_COUNT = 10;
|
|
123
|
+
/** Backup codes look like "k7m2-x9p4": 8 characters from an alphabet without look-alikes (no 0/o, 1/l/i). */
|
|
124
|
+
exports.BACKUP_CODE_LENGTH = 8;
|
|
125
|
+
/** Backup codes are shown split in two for readability: "k7m2-x9p4". */
|
|
126
|
+
exports.BACKUP_CODE_DISPLAY_GROUP_LENGTH = exports.BACKUP_CODE_LENGTH / 2;
|
|
127
|
+
exports.BACKUP_CODE_ALPHABET = 'abcdefghjkmnpqrstuvwxyz23456789';
|
|
128
|
+
exports.RATE_LIMITS = {
|
|
129
|
+
signUpPerIp: { maximumAttempts: 10, windowSeconds: exports.HOUR_IN_SECONDS },
|
|
130
|
+
signInPerIp: { maximumAttempts: 30, windowSeconds: 15 * exports.MINUTE_IN_SECONDS },
|
|
131
|
+
signInPerAccount: { maximumAttempts: 10, windowSeconds: 15 * exports.MINUTE_IN_SECONDS },
|
|
132
|
+
sendCodePerIp: { maximumAttempts: 10, windowSeconds: exports.HOUR_IN_SECONDS },
|
|
133
|
+
sendCodePerDestination: { maximumAttempts: 5, windowSeconds: exports.HOUR_IN_SECONDS },
|
|
134
|
+
sendEmailLinkPerIp: { maximumAttempts: 10, windowSeconds: exports.HOUR_IN_SECONDS },
|
|
135
|
+
sendEmailLinkPerEmail: { maximumAttempts: 3, windowSeconds: exports.HOUR_IN_SECONDS },
|
|
136
|
+
refreshPerIp: { maximumAttempts: 120, windowSeconds: exports.MINUTE_IN_SECONDS },
|
|
137
|
+
twoFactorPerUser: { maximumAttempts: 10, windowSeconds: 15 * exports.MINUTE_IN_SECONDS },
|
|
138
|
+
changePasswordPerUser: { maximumAttempts: 5, windowSeconds: 15 * exports.MINUTE_IN_SECONDS },
|
|
139
|
+
changeEmailPerUser: { maximumAttempts: 5, windowSeconds: exports.HOUR_IN_SECONDS },
|
|
140
|
+
exportDataPerUser: { maximumAttempts: 5, windowSeconds: exports.HOUR_IN_SECONDS },
|
|
141
|
+
};
|
|
142
|
+
/** Passkeys: no attestation (privacy), discoverable credentials, and fingerprint/face/PIN always required. */
|
|
143
|
+
exports.PASSKEY_ATTESTATION_TYPE = 'none';
|
|
144
|
+
exports.PASSKEY_RESIDENT_KEY = 'required';
|
|
145
|
+
exports.PASSKEY_USER_VERIFICATION = 'required';
|
|
146
|
+
/** Name given to a passkey when the user doesn't type one. */
|
|
147
|
+
exports.DEFAULT_PASSKEY_NAME = 'Passkey';
|
|
148
|
+
/** Most passkeys one user may register. */
|
|
149
|
+
exports.MAXIMUM_PASSKEYS_PER_USER = 20;
|
|
150
|
+
/** Longest passkey name. */
|
|
151
|
+
exports.PASSKEY_NAME_MAX_LENGTH = 60;
|
|
152
|
+
/** Prefix for rate-limit counters in Redis, so they never clash with the app's own keys. */
|
|
153
|
+
exports.RATE_LIMIT_REDIS_KEY_PREFIX = 'auth:rate-limit:';
|
|
154
|
+
// ---------------------------------------------------------------------------------------------
|
|
155
|
+
// Caching inside one server process.
|
|
156
|
+
// ---------------------------------------------------------------------------------------------
|
|
157
|
+
/** The settings table holds exactly one row, with this id. */
|
|
158
|
+
exports.SETTINGS_ROW_ID = 1;
|
|
159
|
+
/** Signing keys are re-read this often, so a key rotation reaches every server quickly. */
|
|
160
|
+
exports.SIGNING_KEYS_CACHE_SECONDS = 60;
|
|
161
|
+
/** Settings are re-read this often, so an admin's change reaches every server quickly. */
|
|
162
|
+
exports.SETTINGS_CACHE_SECONDS = 10;
|
|
163
|
+
/**
|
|
164
|
+
* How long the guard trusts its last "session still active and user still ACTIVE" check.
|
|
165
|
+
* Signing out a device or suspending a user takes effect within this time.
|
|
166
|
+
*/
|
|
167
|
+
exports.SESSION_CHECK_CACHE_SECONDS = 5;
|
|
168
|
+
/** Retired signing keys stay published this long, so tokens they signed keep verifying until they expire. */
|
|
169
|
+
exports.RETIRED_SIGNING_KEY_PUBLISH_SECONDS = 2 * exports.DAY_IN_SECONDS;
|
|
170
|
+
// ---------------------------------------------------------------------------------------------
|
|
171
|
+
// Sign in with Google / Apple / Facebook: where their public keys and APIs live.
|
|
172
|
+
// ---------------------------------------------------------------------------------------------
|
|
173
|
+
/** Social providers this library supports (Instagram has no consumer sign-in API since Dec 2024). */
|
|
174
|
+
exports.SOCIAL_PROVIDER = { google: 'google', apple: 'apple', facebook: 'facebook' };
|
|
175
|
+
exports.SOCIAL_PROVIDERS = Object.values(exports.SOCIAL_PROVIDER);
|
|
176
|
+
exports.GOOGLE_PUBLIC_KEYS_URL = 'https://www.googleapis.com/oauth2/v3/certs';
|
|
177
|
+
exports.GOOGLE_TOKEN_ISSUERS = ['https://accounts.google.com', 'accounts.google.com'];
|
|
178
|
+
exports.APPLE_PUBLIC_KEYS_URL = 'https://appleid.apple.com/auth/keys';
|
|
179
|
+
exports.APPLE_TOKEN_ISSUER = 'https://appleid.apple.com';
|
|
180
|
+
/** Graph API version 26 (July 2026). Meta retires versions about two years after release; bump when needed. */
|
|
181
|
+
exports.FACEBOOK_GRAPH_API_URL = 'https://graph.facebook.com/v26.0';
|
|
182
|
+
/** Provider public keys are cached this long (providers rotate them rarely and announce them early). */
|
|
183
|
+
exports.SOCIAL_PUBLIC_KEYS_CACHE_SECONDS = 6 * exports.HOUR_IN_SECONDS;
|
|
184
|
+
/** Give up on a provider API call after this long. */
|
|
185
|
+
exports.SOCIAL_PROVIDER_TIMEOUT_MS = 5000;
|
|
186
|
+
/** A token signed by a key we don't know triggers a fresh key download at most this often. */
|
|
187
|
+
exports.UNKNOWN_KEY_REFETCH_WAIT_SECONDS = 60;
|
|
188
|
+
/** Longest ID / access token accepted from a social provider. */
|
|
189
|
+
exports.SOCIAL_TOKEN_INPUT_MAX_LENGTH = 8192;
|
|
190
|
+
/** Longest nonce accepted with an Apple / Google ID token. */
|
|
191
|
+
exports.NONCE_INPUT_MAX_LENGTH = 256;
|
|
192
|
+
/** Facebook profile fields requested after sign-in. */
|
|
193
|
+
exports.FACEBOOK_PROFILE_FIELDS = 'id,email,first_name,last_name';
|
|
194
|
+
// ---------------------------------------------------------------------------------------------
|
|
195
|
+
// Ready-made senders (src/integrations).
|
|
196
|
+
// ---------------------------------------------------------------------------------------------
|
|
197
|
+
/** MSG91 v5 Flow API: sends an SMS from a DLT-approved template. */
|
|
198
|
+
exports.MSG91_FLOW_API_URL = 'https://control.msg91.com/api/v5/flow';
|
|
199
|
+
exports.MSG91_REQUEST_TIMEOUT_MS = 10_000;
|
|
200
|
+
// ---------------------------------------------------------------------------------------------
|
|
201
|
+
// Webhooks.
|
|
202
|
+
// ---------------------------------------------------------------------------------------------
|
|
203
|
+
/** Header carrying the signature: "t=<unix time>,v1=<hex HMAC-SHA256 of '<t>.<body>'>". */
|
|
204
|
+
exports.WEBHOOK_SIGNATURE_HEADER = 'X-Techelusive-Signature';
|
|
205
|
+
exports.WEBHOOK_EVENT_HEADER = 'X-Techelusive-Event';
|
|
206
|
+
exports.WEBHOOK_DELIVERY_ID_HEADER = 'X-Techelusive-Delivery';
|
|
207
|
+
/** Receivers should reject signatures older than this (stops replays). */
|
|
208
|
+
exports.WEBHOOK_SIGNATURE_TOLERANCE_SECONDS = 5 * exports.MINUTE_IN_SECONDS;
|
|
209
|
+
exports.WEBHOOK_REQUEST_TIMEOUT_MS = 10_000;
|
|
210
|
+
/** Failed deliveries are retried with growing waits: 1 min, 5 min, 30 min, 2 h, 6 h, 12 h, 24 h. */
|
|
211
|
+
exports.WEBHOOK_RETRY_DELAYS_SECONDS = [60, 300, 1800, 7200, 21600, 43200, 86400];
|
|
212
|
+
/** How often the delivery worker looks for webhooks to send. */
|
|
213
|
+
exports.WEBHOOK_WORKER_INTERVAL_MS = 5000;
|
|
214
|
+
/** Deliveries handled per worker run. */
|
|
215
|
+
exports.WEBHOOK_WORKER_BATCH_SIZE = 20;
|
|
216
|
+
exports.WEBHOOK_SECRET_BYTES = 32;
|
|
217
|
+
/** Prefix of webhook signing secrets, so they are easy to recognise (e.g. in secret scanners). */
|
|
218
|
+
exports.WEBHOOK_SECRET_PREFIX = 'whsec_';
|
|
219
|
+
/** A delivery claimed by a worker is retried after this long if that worker crashes mid-send. */
|
|
220
|
+
exports.WEBHOOK_CLAIM_TIMEOUT_SECONDS = 60;
|
|
221
|
+
/** Longest error text / response kept for a failed delivery. */
|
|
222
|
+
exports.WEBHOOK_ERROR_MAX_LENGTH = 500;
|
|
223
|
+
/** Most webhooks one app may register. */
|
|
224
|
+
exports.MAXIMUM_WEBHOOKS = 50;
|
|
225
|
+
exports.WEBHOOK_URL_MAX_LENGTH = 2048;
|
|
226
|
+
exports.WEBHOOK_DESCRIPTION_MAX_LENGTH = 200;
|
|
227
|
+
/** Webhook delivery requests carry this User-Agent. */
|
|
228
|
+
exports.WEBHOOK_USER_AGENT = 'techelusive-nest-auth-webhooks/1.0';
|
|
229
|
+
/**
|
|
230
|
+
* Addresses webhooks may never be sent to (blocks SSRF: tricking the server into calling its own
|
|
231
|
+
* private network or the cloud metadata service at 169.254.169.254).
|
|
232
|
+
*/
|
|
233
|
+
exports.BLOCKED_IPV4_RANGES = [
|
|
234
|
+
['0.0.0.0', 8], // "this network"
|
|
235
|
+
['10.0.0.0', 8], // private
|
|
236
|
+
['100.64.0.0', 10], // carrier-grade NAT
|
|
237
|
+
['127.0.0.0', 8], // loopback
|
|
238
|
+
['169.254.0.0', 16], // link-local, includes cloud metadata
|
|
239
|
+
['172.16.0.0', 12], // private
|
|
240
|
+
['192.0.0.0', 24], // IETF protocol assignments
|
|
241
|
+
['192.168.0.0', 16], // private
|
|
242
|
+
['198.18.0.0', 15], // benchmarking
|
|
243
|
+
['224.0.0.0', 4], // multicast
|
|
244
|
+
['240.0.0.0', 4], // reserved and broadcast
|
|
245
|
+
];
|
|
246
|
+
exports.BLOCKED_IPV6_RANGES = [
|
|
247
|
+
['::', 128], // unspecified
|
|
248
|
+
['::1', 128], // loopback
|
|
249
|
+
['64:ff9b::', 96], // IPv4/IPv6 translation
|
|
250
|
+
['fc00::', 7], // unique local (private)
|
|
251
|
+
['fe80::', 10], // link-local
|
|
252
|
+
['ff00::', 8], // multicast
|
|
253
|
+
// IPv4-mapped addresses (::ffff:a.b.c.d) are checked against the IPv4 list in network-safety.ts.
|
|
254
|
+
];
|
|
255
|
+
// ---------------------------------------------------------------------------------------------
|
|
256
|
+
// Account status. The backend alone moves an account between these; the app never sets them.
|
|
257
|
+
//
|
|
258
|
+
// pending_verification -> active when the required verifications are done
|
|
259
|
+
// active -> suspended an admin blocks the account (reason recorded)
|
|
260
|
+
// active -> deactivated -> active the user pauses and later reactivates
|
|
261
|
+
// any -> deleted personal data is removed; the row stays as a tombstone
|
|
262
|
+
// ---------------------------------------------------------------------------------------------
|
|
263
|
+
exports.ACCOUNT_STATUS = {
|
|
264
|
+
pendingVerification: 'PENDING_VERIFICATION',
|
|
265
|
+
active: 'ACTIVE',
|
|
266
|
+
suspended: 'SUSPENDED',
|
|
267
|
+
deactivated: 'DEACTIVATED',
|
|
268
|
+
deleted: 'DELETED',
|
|
269
|
+
};
|
|
270
|
+
exports.ACCOUNT_STATUSES = Object.values(exports.ACCOUNT_STATUS);
|
|
271
|
+
// ---------------------------------------------------------------------------------------------
|
|
272
|
+
// Organizations and roles.
|
|
273
|
+
// ---------------------------------------------------------------------------------------------
|
|
274
|
+
/** Roles inside an organization, from most to least powerful. */
|
|
275
|
+
exports.ORGANIZATION_ROLE = { owner: 'owner', admin: 'admin', member: 'member' };
|
|
276
|
+
exports.ORGANIZATION_ROLES = Object.values(exports.ORGANIZATION_ROLE);
|
|
277
|
+
/** Higher number = more power. An admin can manage members and admins; only owners can manage owners. */
|
|
278
|
+
exports.ORGANIZATION_ROLE_RANK = {
|
|
279
|
+
[exports.ORGANIZATION_ROLE.owner]: 3,
|
|
280
|
+
[exports.ORGANIZATION_ROLE.admin]: 2,
|
|
281
|
+
[exports.ORGANIZATION_ROLE.member]: 1,
|
|
282
|
+
};
|
|
283
|
+
/** Most organizations one user may belong to. */
|
|
284
|
+
exports.MAXIMUM_ORGANIZATIONS_PER_USER = 100;
|
|
285
|
+
/** Role names: letters, digits, "_", "-", ":" (e.g. "user", "seller", "orders:read"). */
|
|
286
|
+
exports.ROLE_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_:-]{0,49}$/;
|
|
287
|
+
/** Role every new user gets unless the app's settings say otherwise. */
|
|
288
|
+
exports.DEFAULT_USER_ROLE = 'user';
|
|
289
|
+
/** Role allowed to use the /auth/admin endpoints unless the app's settings say otherwise. */
|
|
290
|
+
exports.DEFAULT_ADMIN_ROLE = 'admin';
|
|
291
|
+
exports.MAXIMUM_ROLES_PER_USER = 20;
|
|
292
|
+
/** Longest suspension reason an admin may record. */
|
|
293
|
+
exports.SUSPENSION_REASON_MAX_LENGTH = 500;
|
|
294
|
+
// ---------------------------------------------------------------------------------------------
|
|
295
|
+
// Input formats.
|
|
296
|
+
// ---------------------------------------------------------------------------------------------
|
|
297
|
+
/** Phone numbers must be in international E.164 form, e.g. +919876543210. */
|
|
298
|
+
exports.PHONE_NUMBER_PATTERN = /^\+[1-9]\d{7,14}$/;
|
|
299
|
+
exports.EMAIL_MAX_LENGTH = 254;
|
|
300
|
+
/**
|
|
301
|
+
* A practical email check: something@domain.tld with no spaces. Real proof that an address works
|
|
302
|
+
* is the verification email, not a regular expression.
|
|
303
|
+
*/
|
|
304
|
+
exports.EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;
|
|
305
|
+
exports.DISPLAY_NAME_MAX_LENGTH = 100;
|
|
306
|
+
/** Longest phone number accepted as typed (spaces and dashes are removed afterwards). */
|
|
307
|
+
exports.PHONE_NUMBER_INPUT_MAX_LENGTH = 32;
|
|
308
|
+
/** Longest token accepted in a request body; real tokens are much shorter, this just blocks junk. */
|
|
309
|
+
exports.RANDOM_TOKEN_INPUT_MAX_LENGTH = 128;
|
|
310
|
+
/** Longest one-time or 2FA code accepted in a request body. */
|
|
311
|
+
exports.CODE_INPUT_MAX_LENGTH = 16;
|
|
312
|
+
exports.ORGANIZATION_NAME_MAX_LENGTH = 100;
|
|
313
|
+
/** Organization handles such as "acme-india". */
|
|
314
|
+
exports.ORGANIZATION_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{1,48}[a-z0-9]$/;
|
|
315
|
+
/** User agent strings are cut to this length before saving. */
|
|
316
|
+
exports.USER_AGENT_MAX_LENGTH = 300;
|
|
317
|
+
// ---------------------------------------------------------------------------------------------
|
|
318
|
+
// Pagination for admin lists.
|
|
319
|
+
// ---------------------------------------------------------------------------------------------
|
|
320
|
+
exports.DEFAULT_PAGE_SIZE = 50;
|
|
321
|
+
exports.MAXIMUM_PAGE_SIZE = 200;
|
|
322
|
+
// ---------------------------------------------------------------------------------------------
|
|
323
|
+
// Limits for settings changed by admins at runtime (so a typo can't, say, make tokens last a year).
|
|
324
|
+
// ---------------------------------------------------------------------------------------------
|
|
325
|
+
exports.SETTINGS_LIMITS = {
|
|
326
|
+
maximumRolesInList: 10,
|
|
327
|
+
maximumAllowedEmailDomains: 100,
|
|
328
|
+
maximumCountryCodes: 250,
|
|
329
|
+
maximumClientIds: 10,
|
|
330
|
+
facebookAppIdMaxLength: 50,
|
|
331
|
+
codeLength: { minimum: 6, maximum: 8 },
|
|
332
|
+
codeExpiresInMinutes: { minimum: 1, maximum: 15 },
|
|
333
|
+
accessTokenMinutes: { minimum: 5, maximum: 60 },
|
|
334
|
+
refreshTokenDays: { minimum: 1, maximum: 90 },
|
|
335
|
+
sessionDays: { minimum: 1, maximum: 365 },
|
|
336
|
+
resendAfterSeconds: { minimum: 30, maximum: 300 },
|
|
337
|
+
emailVerificationLinkHours: { minimum: 1, maximum: 72 },
|
|
338
|
+
passwordResetLinkMinutes: { minimum: 10, maximum: 180 },
|
|
339
|
+
magicLinkMinutes: { minimum: 5, maximum: 60 },
|
|
340
|
+
emailChangeLinkMinutes: { minimum: 10, maximum: 240 },
|
|
341
|
+
invitationDays: { minimum: 1, maximum: 30 },
|
|
342
|
+
twoFactorChallengeMinutes: { minimum: 2, maximum: 15 },
|
|
343
|
+
expiredRecordDays: { minimum: 1, maximum: 90 },
|
|
344
|
+
webhookDeliveryDays: { minimum: 1, maximum: 365 },
|
|
345
|
+
/** 0 keeps the audit log forever. */
|
|
346
|
+
auditLogDays: { minimum: 0, maximum: 3650 },
|
|
347
|
+
};
|
|
348
|
+
/** An email domain such as "myapp.com". */
|
|
349
|
+
exports.EMAIL_DOMAIN_PATTERN = /^[a-z0-9-]+(\.[a-z0-9-]+)+$/;
|
|
350
|
+
/** A country calling code such as "+91". */
|
|
351
|
+
exports.COUNTRY_CALLING_CODE_PATTERN = /^\+[1-9]\d{0,3}$/;
|
|
352
|
+
/** An OAuth client id (Google client id, Apple bundle / services id). */
|
|
353
|
+
exports.OAUTH_CLIENT_ID_PATTERN = /^[A-Za-z0-9._:\/-]{3,255}$/;
|
|
354
|
+
// ---------------------------------------------------------------------------------------------
|
|
355
|
+
// Named values. Every fixed value the code compares against or sends lives here, and the
|
|
356
|
+
// TypeScript types are derived from these objects, so a typo cannot compile.
|
|
357
|
+
// ---------------------------------------------------------------------------------------------
|
|
358
|
+
/** How a user proved who they are. Stored on the session and in the token's "amr" claim. */
|
|
359
|
+
exports.SIGN_IN_METHOD = {
|
|
360
|
+
password: 'password',
|
|
361
|
+
emailOtp: 'email_otp',
|
|
362
|
+
magicLink: 'magic_link',
|
|
363
|
+
phoneOtp: 'phone_otp',
|
|
364
|
+
google: 'google',
|
|
365
|
+
apple: 'apple',
|
|
366
|
+
facebook: 'facebook',
|
|
367
|
+
passkey: 'passkey',
|
|
368
|
+
};
|
|
369
|
+
/** Added to "amr" when the user also passed two-factor authentication. */
|
|
370
|
+
exports.TWO_FACTOR_AMR_VALUE = 'two_factor';
|
|
371
|
+
/** The "status" field of sign-in and register responses. */
|
|
372
|
+
exports.SIGN_IN_STATUS = {
|
|
373
|
+
signedIn: 'signed_in',
|
|
374
|
+
twoFactorRequired: 'two_factor_required',
|
|
375
|
+
verificationRequired: 'verification_required',
|
|
376
|
+
};
|
|
377
|
+
/** Details a new account may need, listed in VALIDATION_ERROR's `missingSignUpDetails`. */
|
|
378
|
+
exports.SIGN_UP_DETAIL = {
|
|
379
|
+
firstName: 'firstName',
|
|
380
|
+
lastName: 'lastName',
|
|
381
|
+
acceptTerms: 'acceptTerms',
|
|
382
|
+
acceptPrivacyPolicy: 'acceptPrivacyPolicy',
|
|
383
|
+
email: 'email',
|
|
384
|
+
};
|
|
385
|
+
/** What a user may still have to verify before the account becomes ACTIVE. */
|
|
386
|
+
exports.PENDING_VERIFICATION = { email: 'email', phone: 'phone' };
|
|
387
|
+
/** Half-finished sign-ins (auth_sign_in_challenges.purpose). */
|
|
388
|
+
exports.CHALLENGE_PURPOSE = {
|
|
389
|
+
twoFactor: 'two_factor',
|
|
390
|
+
passkeyRegistration: 'passkey_registration',
|
|
391
|
+
passkeySignIn: 'passkey_sign_in',
|
|
392
|
+
reactivateAccount: 'reactivate_account',
|
|
393
|
+
};
|
|
394
|
+
/** Emailed single-use links (auth_link_tokens.purpose). */
|
|
395
|
+
exports.LINK_PURPOSE = {
|
|
396
|
+
verifyEmail: 'verify_email',
|
|
397
|
+
resetPassword: 'reset_password',
|
|
398
|
+
magicSignIn: 'magic_sign_in',
|
|
399
|
+
changeEmail: 'change_email',
|
|
400
|
+
};
|
|
401
|
+
/** Where a one-time code is sent (auth_one_time_codes.channel). */
|
|
402
|
+
exports.CODE_CHANNEL = { email: 'email', sms: 'sms' };
|
|
403
|
+
/** State of one webhook delivery. */
|
|
404
|
+
exports.WEBHOOK_DELIVERY_STATUS = { pending: 'pending', delivered: 'delivered', failed: 'failed' };
|
|
405
|
+
/** Event name of the delivery sent by "test webhook". */
|
|
406
|
+
exports.WEBHOOK_TEST_EVENT_NAME = 'webhook.test';
|
|
407
|
+
/** The `type` of each email the library asks the app to send. */
|
|
408
|
+
exports.EMAIL_TYPE = {
|
|
409
|
+
verifyEmail: 'verify_email',
|
|
410
|
+
resetPassword: 'reset_password',
|
|
411
|
+
magicSignIn: 'magic_sign_in',
|
|
412
|
+
signInCode: 'sign_in_code',
|
|
413
|
+
organizationInvitation: 'organization_invitation',
|
|
414
|
+
passwordChanged: 'password_changed',
|
|
415
|
+
newSignIn: 'new_sign_in',
|
|
416
|
+
/** To the NEW address: a link that confirms the change. */
|
|
417
|
+
confirmEmailChange: 'confirm_email_change',
|
|
418
|
+
/** To the OLD address after the change, so the owner notices an unwanted change. */
|
|
419
|
+
emailChanged: 'email_changed',
|
|
420
|
+
};
|
|
421
|
+
/** Why a session ended (auth_sessions.revoked_reason, "session.revoked" event). */
|
|
422
|
+
exports.SESSION_END_REASON = {
|
|
423
|
+
signedOut: 'signed_out',
|
|
424
|
+
signedOutEverywhere: 'signed_out_everywhere',
|
|
425
|
+
signedOutByUser: 'signed_out_by_user',
|
|
426
|
+
signedOutByAdmin: 'signed_out_by_admin',
|
|
427
|
+
refreshTokenReused: 'refresh_token_reused',
|
|
428
|
+
accountNotActive: 'account_not_active',
|
|
429
|
+
accountSuspended: 'account_suspended',
|
|
430
|
+
accountDeactivated: 'account_deactivated',
|
|
431
|
+
accountDeleted: 'account_deleted',
|
|
432
|
+
passwordReset: 'password_reset',
|
|
433
|
+
passwordChanged: 'password_changed',
|
|
434
|
+
twoFactorEnabled: 'two_factor_enabled',
|
|
435
|
+
emailProvenByOwner: 'email_proven_by_owner',
|
|
436
|
+
};
|
|
437
|
+
/** Why a sign-in failed ("user.sign_in_failed" event). */
|
|
438
|
+
exports.SIGN_IN_FAILURE_REASON = {
|
|
439
|
+
wrongEmailOrPassword: 'wrong_email_or_password',
|
|
440
|
+
wrongTwoFactorCode: 'wrong_two_factor_code',
|
|
441
|
+
passkeySignatureInvalid: 'passkey_signature_invalid',
|
|
442
|
+
accountNotVerified: 'account_not_verified',
|
|
443
|
+
accountSuspended: 'account_suspended',
|
|
444
|
+
};
|
|
445
|
+
/** How a password changed ("user.password_changed" event, `how`). */
|
|
446
|
+
exports.PASSWORD_CHANGE_KIND = {
|
|
447
|
+
reset: 'reset',
|
|
448
|
+
changed: 'changed',
|
|
449
|
+
firstSet: 'set',
|
|
450
|
+
removedUnverified: 'removed_unverified_password',
|
|
451
|
+
};
|
|
452
|
+
/** A sign-in method a user may remove (checked so they always keep at least one). */
|
|
453
|
+
exports.REMOVABLE_SIGN_IN_METHOD = { password: 'password', social: 'social', passkey: 'passkey' };
|
|
454
|
+
/** Who did something, in the audit log's performedBy: "admin:<id>", "user:<id>". */
|
|
455
|
+
exports.PERFORMED_BY_PREFIX = { admin: 'admin:', user: 'user:' };
|
|
456
|
+
/** Encryption context "purpose" values: an encrypted value only decrypts for the same purpose and row. */
|
|
457
|
+
exports.ENCRYPTION_PURPOSE = {
|
|
458
|
+
signingKey: 'signing_key',
|
|
459
|
+
totpSecret: 'totp_secret',
|
|
460
|
+
webhookSigningSecret: 'webhook_signing_secret',
|
|
461
|
+
};
|
|
462
|
+
/** Stored encrypted values start with "<method>:", telling which method encrypted them. */
|
|
463
|
+
exports.ENCRYPTED_VALUE_METHOD = { kms: 'kms', local: 'local' };
|
|
464
|
+
/** Sort orders used in queries. */
|
|
465
|
+
exports.SORT_ORDER = { newestFirst: 'DESC', oldestFirst: 'ASC' };
|
|
466
|
+
/** Smallest page number in paged lists. */
|
|
467
|
+
exports.FIRST_PAGE = 1;
|
|
468
|
+
/** Value of NODE_ENV in production. */
|
|
469
|
+
exports.PRODUCTION_ENVIRONMENT = 'production';
|
|
470
|
+
// ---------------------------------------------------------------------------------------------
|
|
471
|
+
// Cryptography names (Node crypto / JOSE identifiers).
|
|
472
|
+
// ---------------------------------------------------------------------------------------------
|
|
473
|
+
/** Text encodings used with Buffer. */
|
|
474
|
+
exports.TEXT_ENCODING = { utf8: 'utf8', base64: 'base64', base64Url: 'base64url', hex: 'hex' };
|
|
475
|
+
/** Hash functions. SHA-1 appears only where a standard requires it (TOTP, breached-password API). */
|
|
476
|
+
exports.HASH_ALGORITHM = { sha256: 'sha256', sha1: 'sha1' };
|
|
477
|
+
/** Cipher for the development-only local encryption key. */
|
|
478
|
+
exports.LOCAL_ENCRYPTION_CIPHER = 'aes-256-gcm';
|
|
479
|
+
/** JWT ECDSA signatures use the raw r||s form, not DER. */
|
|
480
|
+
exports.ECDSA_SIGNATURE_ENCODING = 'ieee-p1363';
|
|
481
|
+
/** Token-signing key pair: an elliptic-curve key on P-256 (what ES256 means). */
|
|
482
|
+
exports.SIGNING_KEY_PAIR_TYPE = 'ec';
|
|
483
|
+
exports.SIGNING_KEY_CURVE = 'P-256';
|
|
484
|
+
/** JSON Web Key fields and values. */
|
|
485
|
+
exports.JWK_EXPORT_FORMAT = 'jwk';
|
|
486
|
+
exports.JWK_KEY_TYPE = { ellipticCurve: 'EC', rsa: 'RSA' };
|
|
487
|
+
exports.JWK_USE_SIGNATURE = 'sig';
|
|
488
|
+
/** Separator between an encrypted value's method prefix and its data ("kms:..."). */
|
|
489
|
+
exports.ENCRYPTED_VALUE_SEPARATOR = ':';
|
|
490
|
+
/** Random salt size for hashed short codes. */
|
|
491
|
+
exports.SHORT_CODE_SALT_BYTES = 16;
|
|
492
|
+
/** Authenticator apps read QR codes holding "otpauth://totp/<label>?<parameters>". */
|
|
493
|
+
exports.AUTHENTICATOR_APP_URL_PREFIX = 'otpauth://totp/';
|
|
494
|
+
exports.URL_QUERY_SEPARATOR = '?';
|
|
495
|
+
/** Name of the TOTP hash in authenticator-app URLs (otpauth://...&algorithm=SHA1). */
|
|
496
|
+
exports.TOTP_URL_ALGORITHM_NAME = 'SHA1';
|
|
497
|
+
// ---------------------------------------------------------------------------------------------
|
|
498
|
+
// HTTP routes. Every path the library serves is defined here (and documented in docs/endpoints).
|
|
499
|
+
// Paths with ":name" use the parameter names in ROUTE_PARAM.
|
|
500
|
+
// ---------------------------------------------------------------------------------------------
|
|
501
|
+
exports.ROUTE_PARAM = {
|
|
502
|
+
provider: 'provider',
|
|
503
|
+
sessionId: 'sessionId',
|
|
504
|
+
passkeyId: 'passkeyId',
|
|
505
|
+
organizationId: 'organizationId',
|
|
506
|
+
memberUserId: 'memberUserId',
|
|
507
|
+
invitationId: 'invitationId',
|
|
508
|
+
userId: 'userId',
|
|
509
|
+
apiKeyId: 'apiKeyId',
|
|
510
|
+
webhookId: 'webhookId',
|
|
511
|
+
deliveryId: 'deliveryId',
|
|
512
|
+
};
|
|
513
|
+
exports.ROUTES = {
|
|
514
|
+
/** Email + password, email verification, passwordless and account routes share this prefix. */
|
|
515
|
+
auth: {
|
|
516
|
+
prefix: 'auth',
|
|
517
|
+
register: 'register',
|
|
518
|
+
login: 'login',
|
|
519
|
+
emailSendVerification: 'email/send-verification',
|
|
520
|
+
emailVerify: 'email/verify',
|
|
521
|
+
emailSendOtp: 'email/send-otp',
|
|
522
|
+
emailVerifyOtp: 'email/verify-otp',
|
|
523
|
+
magicLinkSend: 'magic-link/send',
|
|
524
|
+
magicLinkVerify: 'magic-link/verify',
|
|
525
|
+
phoneSendOtp: 'phone/send-otp',
|
|
526
|
+
phoneVerifyOtp: 'phone/verify-otp',
|
|
527
|
+
passwordForgot: 'password/forgot',
|
|
528
|
+
passwordReset: 'password/reset',
|
|
529
|
+
passwordChange: 'password/change',
|
|
530
|
+
refresh: 'refresh',
|
|
531
|
+
logout: 'logout',
|
|
532
|
+
logoutAll: 'logout-all',
|
|
533
|
+
me: 'me',
|
|
534
|
+
signInMethods: 'sign-in-methods',
|
|
535
|
+
sessions: 'sessions',
|
|
536
|
+
session: 'sessions/:sessionId',
|
|
537
|
+
deactivate: 'deactivate',
|
|
538
|
+
reactivate: 'reactivate',
|
|
539
|
+
emailChange: 'email/change',
|
|
540
|
+
emailChangeConfirm: 'email/change/confirm',
|
|
541
|
+
exportMyData: 'me/export',
|
|
542
|
+
health: 'health',
|
|
543
|
+
},
|
|
544
|
+
social: {
|
|
545
|
+
prefix: 'auth/social',
|
|
546
|
+
signIn: ':provider',
|
|
547
|
+
link: ':provider/link',
|
|
548
|
+
unlink: ':provider',
|
|
549
|
+
},
|
|
550
|
+
passkeys: {
|
|
551
|
+
prefix: 'auth/passkeys',
|
|
552
|
+
registerOptions: 'register/options',
|
|
553
|
+
registerVerify: 'register/verify',
|
|
554
|
+
signInOptions: 'sign-in/options',
|
|
555
|
+
signInVerify: 'sign-in/verify',
|
|
556
|
+
passkey: ':passkeyId',
|
|
557
|
+
},
|
|
558
|
+
twoFactor: {
|
|
559
|
+
prefix: 'auth/2fa',
|
|
560
|
+
setup: 'setup',
|
|
561
|
+
enable: 'enable',
|
|
562
|
+
disable: 'disable',
|
|
563
|
+
backupCodes: 'backup-codes',
|
|
564
|
+
verify: 'verify',
|
|
565
|
+
},
|
|
566
|
+
organizations: {
|
|
567
|
+
prefix: 'auth/organizations',
|
|
568
|
+
switch: 'switch',
|
|
569
|
+
acceptInvitation: 'invitations/accept',
|
|
570
|
+
organization: ':organizationId',
|
|
571
|
+
members: ':organizationId/members',
|
|
572
|
+
member: ':organizationId/members/:memberUserId',
|
|
573
|
+
invitations: ':organizationId/invitations',
|
|
574
|
+
invitation: ':organizationId/invitations/:invitationId',
|
|
575
|
+
},
|
|
576
|
+
admin: {
|
|
577
|
+
prefix: 'auth/admin',
|
|
578
|
+
users: 'users',
|
|
579
|
+
user: 'users/:userId',
|
|
580
|
+
userRoles: 'users/:userId/roles',
|
|
581
|
+
userSuspend: 'users/:userId/suspend',
|
|
582
|
+
userUnsuspend: 'users/:userId/unsuspend',
|
|
583
|
+
userVerifyEmail: 'users/:userId/verify-email',
|
|
584
|
+
userResetTwoFactor: 'users/:userId/reset-2fa',
|
|
585
|
+
userLogoutAll: 'users/:userId/logout-all',
|
|
586
|
+
settings: 'settings',
|
|
587
|
+
auditLog: 'audit-log',
|
|
588
|
+
rotateSigningKey: 'signing-keys/rotate',
|
|
589
|
+
apiKeys: 'api-keys',
|
|
590
|
+
apiKey: 'api-keys/:apiKeyId',
|
|
591
|
+
webhooks: 'webhooks',
|
|
592
|
+
webhook: 'webhooks/:webhookId',
|
|
593
|
+
webhookRotateSecret: 'webhooks/:webhookId/rotate-secret',
|
|
594
|
+
webhookTest: 'webhooks/:webhookId/test',
|
|
595
|
+
webhookDeliveries: 'webhooks/:webhookId/deliveries',
|
|
596
|
+
webhookRetryDelivery: 'webhooks/:webhookId/deliveries/:deliveryId/retry',
|
|
597
|
+
stats: 'stats',
|
|
598
|
+
runCleanup: 'maintenance/cleanup',
|
|
599
|
+
},
|
|
600
|
+
wellKnown: {
|
|
601
|
+
prefix: '.well-known',
|
|
602
|
+
jwks: 'jwks.json',
|
|
603
|
+
},
|
|
604
|
+
};
|
|
605
|
+
/** HTTP header names and values the library reads or sends. */
|
|
606
|
+
exports.HTTP_HEADER = {
|
|
607
|
+
authorization: 'authorization',
|
|
608
|
+
userAgent: 'user-agent',
|
|
609
|
+
contentType: 'Content-Type',
|
|
610
|
+
accept: 'accept',
|
|
611
|
+
cacheControl: 'Cache-Control',
|
|
612
|
+
retryAfter: 'Retry-After',
|
|
613
|
+
outgoingUserAgent: 'User-Agent',
|
|
614
|
+
cookie: 'cookie',
|
|
615
|
+
setCookie: 'Set-Cookie',
|
|
616
|
+
origin: 'origin',
|
|
617
|
+
};
|
|
618
|
+
exports.JSON_CONTENT_TYPE = 'application/json';
|
|
619
|
+
/** Requests of other kinds (WebSockets, microservices) are not checked by AuthGuard. */
|
|
620
|
+
exports.HTTP_CONTEXT_TYPE = 'http';
|
|
621
|
+
/** The JWKS document may be cached by clients for 5 minutes. */
|
|
622
|
+
exports.JWKS_CACHE_CONTROL = 'public, max-age=300';
|
|
623
|
+
// ---------------------------------------------------------------------------------------------
|
|
624
|
+
// Rate-limit counter keys. One builder per counter so the same limit always uses the same key.
|
|
625
|
+
// ---------------------------------------------------------------------------------------------
|
|
626
|
+
exports.RATE_LIMIT_KEY = {
|
|
627
|
+
signUpPerIp: (ipAddress) => `sign-up:ip:${ipAddress}`,
|
|
628
|
+
signInPerIp: (ipAddress) => `sign-in:ip:${ipAddress}`,
|
|
629
|
+
signInPerAccount: (email) => `sign-in:account:${email}`,
|
|
630
|
+
sendCodePerIp: (ipAddress) => `send-code:ip:${ipAddress}`,
|
|
631
|
+
sendCodePerDestination: (destination) => `send-code:destination:${destination}`,
|
|
632
|
+
sendEmailLinkPerIp: (ipAddress) => `email-link:ip:${ipAddress}`,
|
|
633
|
+
sendEmailLinkPerEmail: (email) => `email-link:email:${email}`,
|
|
634
|
+
refreshPerIp: (ipAddress) => `refresh:ip:${ipAddress}`,
|
|
635
|
+
twoFactorPerUser: (userId) => `two-factor:user:${userId}`,
|
|
636
|
+
changePasswordPerUser: (userId) => `change-password:user:${userId}`,
|
|
637
|
+
changeEmailPerUser: (userId) => `change-email:user:${userId}`,
|
|
638
|
+
exportDataPerUser: (userId) => `export-data:user:${userId}`,
|
|
639
|
+
};
|
|
640
|
+
/** Builds the audit log's performedBy value. */
|
|
641
|
+
exports.PERFORMED_BY = {
|
|
642
|
+
admin: (adminUserId) => `${exports.PERFORMED_BY_PREFIX.admin}${adminUserId}`,
|
|
643
|
+
user: (userId) => `${exports.PERFORMED_BY_PREFIX.user}${userId}`,
|
|
644
|
+
};
|
|
645
|
+
/** User agent recorded for actions taken by scripts and jobs (SYSTEM_REQUEST). */
|
|
646
|
+
exports.SYSTEM_USER_AGENT = 'system';
|
|
647
|
+
/** Names shown in log lines, one per component. */
|
|
648
|
+
exports.LOG_CONTEXT = {
|
|
649
|
+
exceptionFilter: 'AuthExceptionFilter',
|
|
650
|
+
rateLimiter: 'RateLimiter',
|
|
651
|
+
apiKeys: 'ApiKeys',
|
|
652
|
+
events: 'AuthEvents',
|
|
653
|
+
notifications: 'Notifications',
|
|
654
|
+
passwordPolicy: 'PasswordPolicy',
|
|
655
|
+
sessions: 'Sessions',
|
|
656
|
+
signingKeys: 'SigningKeys',
|
|
657
|
+
socialProviders: 'SocialProviders',
|
|
658
|
+
webhookDelivery: 'WebhookDelivery',
|
|
659
|
+
consoleEmailSender: 'ConsoleEmailSender',
|
|
660
|
+
dataRetention: 'DataRetention',
|
|
661
|
+
};
|
|
662
|
+
/** In-process caches are emptied when they grow past these sizes (protects memory under attack). */
|
|
663
|
+
exports.SESSION_CHECK_CACHE_MAX_ENTRIES = 100_000;
|
|
664
|
+
exports.IN_MEMORY_RATE_LIMIT_MAX_ENTRIES = 50_000;
|
|
665
|
+
// ---------------------------------------------------------------------------------------------
|
|
666
|
+
// Problem texts returned to API clients in `problems` / `requirements` lists.
|
|
667
|
+
// ---------------------------------------------------------------------------------------------
|
|
668
|
+
exports.PASSWORD_REQUIREMENT = {
|
|
669
|
+
minimumLength: (minimumLength) => `at least ${minimumLength} characters`,
|
|
670
|
+
maximumLength: (maximumLength) => `at most ${maximumLength} characters`,
|
|
671
|
+
uppercaseLetter: 'an uppercase letter',
|
|
672
|
+
lowercaseLetter: 'a lowercase letter',
|
|
673
|
+
number: 'a number',
|
|
674
|
+
symbol: 'a symbol',
|
|
675
|
+
differentFromCurrent: 'different from your current password',
|
|
676
|
+
};
|
|
677
|
+
/** Trailing slashes removed from the issuer URL ("https://api.myapp.com/" -> "https://api.myapp.com"). */
|
|
678
|
+
exports.TRAILING_SLASHES_PATTERN = /\/+$/;
|
|
679
|
+
/** Characters removed from typed input before it is checked. */
|
|
680
|
+
exports.PHONE_FORMATTING_CHARACTERS_PATTERN = /[\s\-().]/g;
|
|
681
|
+
exports.BACKUP_CODE_FORMATTING_CHARACTERS_PATTERN = /[\s-]/g;
|
|
682
|
+
exports.WHITESPACE_PATTERN = /\s/g;
|
|
683
|
+
exports.BASE32_PADDING_PATTERN = /=+$/;
|
|
684
|
+
exports.LEADING_PLUS_PATTERN = /^\+/;
|
|
685
|
+
/** Only base64url characters (the body of a random token). */
|
|
686
|
+
exports.BASE64URL_TEXT_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
687
|
+
/** Only digits (a typed TOTP code). */
|
|
688
|
+
exports.DIGITS_ONLY_PATTERN = /^\d+$/;
|
|
689
|
+
/** Characters with special meaning in SQL LIKE patterns, escaped in admin user search. */
|
|
690
|
+
exports.SQL_LIKE_SPECIAL_CHARACTERS_PATTERN = /[\\%_]/g;
|
|
691
|
+
exports.SQL_LIKE_ESCAPE_CHARACTER = '\\';
|
|
692
|
+
exports.SQL_LIKE_ANY_TEXT = '%';
|
|
693
|
+
/** Joins nested settings field names: "password.minimumLength". */
|
|
694
|
+
exports.SETTINGS_PATH_SEPARATOR = '.';
|
|
695
|
+
/** Patterns for the character rules above. */
|
|
696
|
+
exports.UPPERCASE_LETTER_PATTERN = /\p{Lu}/u;
|
|
697
|
+
exports.LOWERCASE_LETTER_PATTERN = /\p{Ll}/u;
|
|
698
|
+
exports.DIGIT_PATTERN = /\d/;
|
|
699
|
+
exports.VALIDATION_PROBLEM = {
|
|
700
|
+
namesRequired: 'firstName and lastName are required',
|
|
701
|
+
tooManyOrganizations: 'too many organizations',
|
|
702
|
+
tooManyPasskeys: 'too many passkeys',
|
|
703
|
+
tooManyWebhooks: 'too many webhooks',
|
|
704
|
+
unknownEventNames: (unknownNames) => `unknown event names: ${unknownNames.join(', ')}`,
|
|
705
|
+
settingsNotAnObject: 'settings must be an object',
|
|
706
|
+
refreshLongerThanSession: 'tokens.refreshTokenDays cannot be more than tokens.sessionDays',
|
|
707
|
+
noSignInMethodEnabled: 'at least one sign-in method must stay enabled',
|
|
708
|
+
googleClientIdsRequired: 'google.clientIds is required when Google is enabled',
|
|
709
|
+
appleClientIdsRequired: 'apple.clientIds is required when Apple is enabled',
|
|
710
|
+
facebookAppIdRequired: 'facebook.appId is required when Facebook is enabled',
|
|
711
|
+
adminRolesRequired: 'admin.adminRoles must name at least one role',
|
|
712
|
+
/** "password.minimumLength must not be less than 8" */
|
|
713
|
+
atPath: (path, message) => `${path}: ${message}`,
|
|
714
|
+
emailDomainFormat: 'each domain must look like example.com',
|
|
715
|
+
countryCodeFormat: 'each code must look like +91',
|
|
716
|
+
};
|
|
717
|
+
/** Problems found in AuthModule options at startup (the app refuses to start and lists them). */
|
|
718
|
+
exports.OPTIONS_PROBLEM = {
|
|
719
|
+
dataSourceRequired: 'dataSource is required (your TypeORM DataSource)',
|
|
720
|
+
emailSenderRequired: 'emailSender with a send() method is required',
|
|
721
|
+
issuerMustBeHttps: 'issuer must be an https URL in production',
|
|
722
|
+
issuerMustBeUrl: 'issuer must be a full URL, e.g. https://api.myapp.com',
|
|
723
|
+
encryptionRequired: 'encryption is required: { awsKmsKeyId, awsRegion } or, for development, { localKey }',
|
|
724
|
+
localKeyNotForProduction: 'encryption.localKey is for development only; use AWS KMS in production',
|
|
725
|
+
localKeyWrongSize: `encryption.localKey must be ${exports.LOCAL_ENCRYPTION_KEY_BYTES} random bytes in base64 (openssl rand -base64 32)`,
|
|
726
|
+
kmsDetailsRequired: 'encryption needs both awsKmsKeyId and awsRegion',
|
|
727
|
+
legacyLocalKeyWrongSize: `encryption.legacyLocalKey must be the old ${exports.LOCAL_ENCRYPTION_KEY_BYTES}-byte base64 local key`,
|
|
728
|
+
relyingPartyIdMustBeDomain: 'passkeys.relyingPartyId must be a bare domain such as "myapp.com"',
|
|
729
|
+
relyingPartyNameRequired: 'passkeys.relyingPartyName is required',
|
|
730
|
+
allowedOriginsRequired: 'passkeys.allowedOrigins needs at least one origin',
|
|
731
|
+
cookieAllowedOriginsRequired: 'refreshTokenCookie.allowedOrigins needs at least one origin, e.g. ["https://app.myapp.com"]',
|
|
732
|
+
hostCookieRules: 'refreshTokenCookie: a name starting with "__Host-" requires path "/" and no domain',
|
|
733
|
+
redisRequiredInProduction: 'redisUrl is required in production so rate limits are shared by every server (set singleServer: true if you really run one process)',
|
|
734
|
+
};
|
|
735
|
+
/** Why a webhook URL was refused (UNSAFE_WEBHOOK_URL `problem`). */
|
|
736
|
+
exports.URL_SAFETY_PROBLEM = {
|
|
737
|
+
invalidUrl: 'not a valid URL',
|
|
738
|
+
hasCredentials: 'URL must not contain a username or password',
|
|
739
|
+
notHttps: 'URL must use https',
|
|
740
|
+
doesNotResolve: 'host name does not resolve',
|
|
741
|
+
privateAddress: 'URL points to a private network address',
|
|
742
|
+
};
|
|
743
|
+
/** lastError of a webhook delivery that could not be sent. */
|
|
744
|
+
exports.WEBHOOK_DELIVERY_ERROR = {
|
|
745
|
+
webhookUnavailable: 'webhook removed or disabled',
|
|
746
|
+
unsafeUrl: (problem) => `unsafe URL: ${problem}`,
|
|
747
|
+
httpStatus: (status) => `HTTP ${status}`,
|
|
748
|
+
};
|
|
749
|
+
/** Message inside the "webhook.test" delivery. */
|
|
750
|
+
exports.WEBHOOK_TEST_MESSAGE = 'This is a test delivery.';
|
|
751
|
+
// ---------------------------------------------------------------------------------------------
|
|
752
|
+
// HTTP details for outgoing calls and the network-safety check.
|
|
753
|
+
// ---------------------------------------------------------------------------------------------
|
|
754
|
+
exports.HTTP_METHOD = { post: 'POST' };
|
|
755
|
+
/** Outgoing webhook calls never follow redirects (a redirect could point at a private address). */
|
|
756
|
+
exports.FETCH_REDIRECT_MODE_MANUAL = 'manual';
|
|
757
|
+
/** Responses in this range count as success. */
|
|
758
|
+
exports.SUCCESS_STATUS_RANGE = { from: 200, toExclusive: 300 };
|
|
759
|
+
exports.URL_PROTOCOL = { https: 'https:', http: 'http:' };
|
|
760
|
+
/** Hosts allowed over plain http outside production, for local webhook testing. */
|
|
761
|
+
exports.LOCAL_DEVELOPMENT_HOSTS = ['localhost', '127.0.0.1'];
|
|
762
|
+
/** Node's names and numbers for IP versions. */
|
|
763
|
+
exports.IP_FAMILY = { ipv4: 'ipv4', ipv6: 'ipv6' };
|
|
764
|
+
exports.IP_VERSION_6 = 6;
|
|
765
|
+
/** "::ffff:10.0.0.1" style IPv4-mapped IPv6 address; group 1 is the IPv4 part. */
|
|
766
|
+
exports.IPV4_MAPPED_IPV6_PATTERN = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i;
|
|
767
|
+
/** Square brackets around IPv6 hosts in URLs ("[::1]"). */
|
|
768
|
+
exports.URL_IPV6_BRACKETS_PATTERN = /^\[|\]$/g;
|
|
769
|
+
// ---------------------------------------------------------------------------------------------
|
|
770
|
+
// Third-party APIs.
|
|
771
|
+
// ---------------------------------------------------------------------------------------------
|
|
772
|
+
/** haveibeenpwned.com range API: padded responses hide which prefix was asked about. */
|
|
773
|
+
exports.BREACHED_PASSWORD_API = {
|
|
774
|
+
paddingHeader: 'Add-Padding',
|
|
775
|
+
paddingHeaderValue: 'true',
|
|
776
|
+
lineSeparator: '\n',
|
|
777
|
+
countSeparator: ':',
|
|
778
|
+
};
|
|
779
|
+
/** Facebook Graph API paths and query parameters. */
|
|
780
|
+
exports.FACEBOOK_API = {
|
|
781
|
+
debugTokenPath: '/debug_token',
|
|
782
|
+
profilePath: '/me',
|
|
783
|
+
inputTokenParameter: 'input_token',
|
|
784
|
+
accessTokenParameter: 'access_token',
|
|
785
|
+
fieldsParameter: 'fields',
|
|
786
|
+
appSecretProofParameter: 'appsecret_proof',
|
|
787
|
+
appAccessToken: (appId, appSecret) => `${appId}|${appSecret}`,
|
|
788
|
+
};
|
|
789
|
+
/** Apple sends email_verified either as a boolean or as this string. */
|
|
790
|
+
exports.APPLE_TRUE_STRING = 'true';
|
|
791
|
+
/** MSG91 request details. */
|
|
792
|
+
exports.MSG91_API = {
|
|
793
|
+
authKeyHeader: 'authkey',
|
|
794
|
+
defaultCodeVariableName: 'otp',
|
|
795
|
+
errorResponseType: 'error',
|
|
796
|
+
};
|
|
797
|
+
// ---------------------------------------------------------------------------------------------
|
|
798
|
+
// Formats.
|
|
799
|
+
// ---------------------------------------------------------------------------------------------
|
|
800
|
+
/** JWT: header.payload.signature */
|
|
801
|
+
exports.JWT_PART_SEPARATOR = '.';
|
|
802
|
+
exports.JWT_PART_COUNT = 3;
|
|
803
|
+
/** Random tokens look like "<prefix>_<43 base64url characters>". */
|
|
804
|
+
exports.TOKEN_PREFIX_SEPARATOR = '_';
|
|
805
|
+
/** Length of 32 random bytes written in base64url. */
|
|
806
|
+
exports.RANDOM_TOKEN_BASE64URL_LENGTH = 43;
|
|
807
|
+
/** Short codes are hashed as "<salt>:<code>". */
|
|
808
|
+
exports.SALTED_CODE_SEPARATOR = ':';
|
|
809
|
+
/** Digits of a numeric code are chosen from 0 to 9. */
|
|
810
|
+
exports.DECIMAL_BASE = 10;
|
|
811
|
+
/** TOTP: an 8-byte big-endian counter; codes are left-padded with zeros. */
|
|
812
|
+
exports.TOTP_COUNTER_BYTES = 8;
|
|
813
|
+
exports.TOTP_CODE_PAD_CHARACTER = '0';
|
|
814
|
+
/** Authenticator-app label "Issuer:account". */
|
|
815
|
+
exports.TOTP_LABEL_SEPARATOR = ':';
|
|
816
|
+
/** Base32 packs 5 bits per character; bytes have 8 bits. */
|
|
817
|
+
exports.BASE32_BITS_PER_CHARACTER = 5;
|
|
818
|
+
exports.BITS_PER_BYTE = 8;
|
|
819
|
+
exports.BASE32_CHARACTER_MASK = 0b11111;
|
|
820
|
+
exports.BYTE_MASK = 0xff;
|
|
821
|
+
/** Backup codes are shown as "k7m2-x9p4". */
|
|
822
|
+
exports.BACKUP_CODE_DISPLAY_SEPARATOR = '-';
|
|
823
|
+
/** First and last name are joined with a space for display. */
|
|
824
|
+
exports.DISPLAY_NAME_SEPARATOR = ' ';
|
|
825
|
+
/** Email masking keeps the first character: "a***@mail.com". */
|
|
826
|
+
exports.EMAIL_MASK = { visibleCharacters: 1, hiddenPart: '***', domainSeparator: '@' };
|
|
827
|
+
/** Phone masking keeps the country code and the last 4 digits: "+91******3210". */
|
|
828
|
+
exports.PHONE_MASK = { visibleStart: 3, visibleEnd: 4, maskCharacter: '*' };
|
|
829
|
+
/** A bare passkey relying-party id is a domain, so it contains neither of these. */
|
|
830
|
+
exports.RELYING_PARTY_ID_FORBIDDEN_CHARACTERS = ['/', ':'];
|
|
831
|
+
/** Webhook signature header: "t=<unix time>,v1=<hex HMAC>" over "<t>.<raw body>". */
|
|
832
|
+
exports.WEBHOOK_SIGNATURE_FORMAT = {
|
|
833
|
+
timestampKey: 't',
|
|
834
|
+
signatureKey: 'v1',
|
|
835
|
+
partSeparator: ',',
|
|
836
|
+
keyValueSeparator: '=',
|
|
837
|
+
signedContentSeparator: '.',
|
|
838
|
+
};
|
|
839
|
+
// ---------------------------------------------------------------------------------------------
|
|
840
|
+
// Request metadata keys used by the guard and decorators.
|
|
841
|
+
// ---------------------------------------------------------------------------------------------
|
|
842
|
+
exports.METADATA_KEY = {
|
|
843
|
+
publicRoute: 'techelusive-auth:public-route',
|
|
844
|
+
requiredRoles: 'techelusive-auth:required-roles',
|
|
845
|
+
checkSessionInDatabase: 'techelusive-auth:check-session',
|
|
846
|
+
apiKeyScopes: 'techelusive-auth:api-key-scopes',
|
|
847
|
+
adminOnly: 'techelusive-auth:admin-only',
|
|
848
|
+
clearsRefreshTokenCookie: 'techelusive-auth:clears-refresh-token-cookie',
|
|
849
|
+
libraryController: 'techelusive-auth:library-controller',
|
|
850
|
+
};
|
|
851
|
+
/** Name of the property the guard puts on the Express request (read by @CurrentUser). */
|
|
852
|
+
exports.CALLER_REQUEST_PROPERTY = 'techelusiveAuthCaller';
|
|
853
|
+
/** "Authorization: Bearer <token>" */
|
|
854
|
+
exports.BEARER_TOKEN_PATTERN = /^Bearer\s+(\S+)$/i;
|
|
855
|
+
/** Longest API key name. */
|
|
856
|
+
exports.API_KEY_NAME_MAX_LENGTH = 100;
|
|
857
|
+
/** Most scopes on one API key. */
|
|
858
|
+
exports.MAXIMUM_API_KEY_SCOPES = 50;
|
|
859
|
+
/** API key scopes look like "rides:read" or "admin". */
|
|
860
|
+
exports.API_KEY_SCOPE_PATTERN = /^[a-z][a-z0-9_.-]{0,49}(:[a-z0-9_.*-]{1,50})?$/;
|
|
861
|
+
/** Characters of an API key (after "ak_") shown in lists so people can tell keys apart. */
|
|
862
|
+
exports.API_KEY_PREVIEW_LENGTH = 6;
|
|
863
|
+
/** "Last used" on an API key is updated at most this often, to avoid a write on every request. */
|
|
864
|
+
exports.API_KEY_LAST_USED_UPDATE_SECONDS = 60;
|
|
865
|
+
/** Header used by servers calling your API with an API key. */
|
|
866
|
+
exports.API_KEY_HEADER = 'x-api-key';
|
|
867
|
+
// ---------------------------------------------------------------------------------------------
|
|
868
|
+
// Events. Emitted inside your app (AuthEvents) and sent to webhooks.
|
|
869
|
+
// ---------------------------------------------------------------------------------------------
|
|
870
|
+
exports.AUTH_EVENTS = {
|
|
871
|
+
userCreated: 'user.created',
|
|
872
|
+
userUpdated: 'user.updated',
|
|
873
|
+
userDeleted: 'user.deleted',
|
|
874
|
+
userSignedIn: 'user.signed_in',
|
|
875
|
+
userSignInFailed: 'user.sign_in_failed',
|
|
876
|
+
userSignedOut: 'user.signed_out',
|
|
877
|
+
userActivated: 'user.activated',
|
|
878
|
+
userSuspended: 'user.suspended',
|
|
879
|
+
userDeactivated: 'user.deactivated',
|
|
880
|
+
userReactivated: 'user.reactivated',
|
|
881
|
+
userRolesChanged: 'user.roles_changed',
|
|
882
|
+
phoneVerified: 'user.phone_verified',
|
|
883
|
+
onboardingCompleted: 'user.onboarding_completed',
|
|
884
|
+
emailVerified: 'user.email_verified',
|
|
885
|
+
emailChangeRequested: 'user.email_change_requested',
|
|
886
|
+
emailChanged: 'user.email_changed',
|
|
887
|
+
passwordChanged: 'user.password_changed',
|
|
888
|
+
passwordResetRequested: 'user.password_reset_requested',
|
|
889
|
+
twoFactorEnabled: 'user.two_factor_enabled',
|
|
890
|
+
twoFactorDisabled: 'user.two_factor_disabled',
|
|
891
|
+
passkeyAdded: 'user.passkey_added',
|
|
892
|
+
passkeyRemoved: 'user.passkey_removed',
|
|
893
|
+
socialAccountLinked: 'user.social_account_linked',
|
|
894
|
+
socialAccountUnlinked: 'user.social_account_unlinked',
|
|
895
|
+
sessionRevoked: 'session.revoked',
|
|
896
|
+
refreshTokenReused: 'session.refresh_token_reused',
|
|
897
|
+
organizationCreated: 'organization.created',
|
|
898
|
+
organizationDeleted: 'organization.deleted',
|
|
899
|
+
organizationMemberAdded: 'organization.member_added',
|
|
900
|
+
organizationMemberRemoved: 'organization.member_removed',
|
|
901
|
+
organizationMemberRoleChanged: 'organization.member_role_changed',
|
|
902
|
+
organizationUpdated: 'organization.updated',
|
|
903
|
+
organizationInvitationSent: 'organization.invitation_sent',
|
|
904
|
+
apiKeyCreated: 'api_key.created',
|
|
905
|
+
apiKeyRevoked: 'api_key.revoked',
|
|
906
|
+
settingsChanged: 'settings.changed',
|
|
907
|
+
};
|
|
908
|
+
// ---------------------------------------------------------------------------------------------
|
|
909
|
+
// Success messages. Several are deliberately the same whether or not an account exists, so the
|
|
910
|
+
// API never tells a stranger which emails or phone numbers are registered.
|
|
911
|
+
// ---------------------------------------------------------------------------------------------
|
|
912
|
+
exports.SUCCESS_MESSAGES = {
|
|
913
|
+
registered: 'Your account has been created. Please verify your details to continue.',
|
|
914
|
+
verificationEmailSent: 'If this email needs verifying, a verification link is on its way.',
|
|
915
|
+
emailVerified: 'Your email has been verified.',
|
|
916
|
+
passwordResetEmailSent: 'If an account exists for this email, a password reset link is on its way.',
|
|
917
|
+
passwordReset: 'Your password has been reset. Please sign in with your new password.',
|
|
918
|
+
passwordChanged: 'Your password has been changed. Other devices have been signed out.',
|
|
919
|
+
codeSent: 'If this destination can receive codes, a code is on its way.',
|
|
920
|
+
magicLinkSent: 'If this email can sign in, a sign-in link is on its way.',
|
|
921
|
+
signedOut: 'You have been signed out.',
|
|
922
|
+
signedOutEverywhere: 'You have been signed out on all devices.',
|
|
923
|
+
accountDeactivated: 'Your account has been deactivated. Sign in again at any time to reactivate it.',
|
|
924
|
+
accountDeleted: 'Your account has been deleted.',
|
|
925
|
+
emailChangeRequested: 'If this email can be used, a confirmation link is on its way to it.',
|
|
926
|
+
emailChanged: 'Your email has been changed.',
|
|
927
|
+
};
|
|
928
|
+
// ---------------------------------------------------------------------------------------------
|
|
929
|
+
// Errors. Every error has an HTTP status, a stable `code` apps can check, and a message safe to
|
|
930
|
+
// show users. Codes are stable, so apps can switch on them. Messages never reveal whether an email or phone
|
|
931
|
+
// number has an account.
|
|
932
|
+
//
|
|
933
|
+
// Response shape: { "success": false, "code": "INVALID_CREDENTIALS", "message": "..." }
|
|
934
|
+
// ---------------------------------------------------------------------------------------------
|
|
935
|
+
exports.AUTH_ERRORS = {
|
|
936
|
+
validationFailed: { status: 400, code: 'VALIDATION_ERROR', message: 'Some fields are missing or invalid.' },
|
|
937
|
+
invalidEmail: { status: 400, code: 'INVALID_EMAIL', message: 'Enter a valid email address.' },
|
|
938
|
+
invalidPhone: { status: 400, code: 'INVALID_PHONE', message: 'Enter a valid phone number with country code, e.g. +919876543210.' },
|
|
939
|
+
invalidPassword: { status: 400, code: 'INVALID_PASSWORD', message: 'This password does not meet the password rules.' },
|
|
940
|
+
breachedPassword: {
|
|
941
|
+
status: 400,
|
|
942
|
+
code: 'INVALID_PASSWORD',
|
|
943
|
+
message: 'This password has appeared in a data breach. Please choose a different one.',
|
|
944
|
+
},
|
|
945
|
+
termsNotAccepted: { status: 400, code: 'TERMS_NOT_ACCEPTED', message: 'Please accept the terms of service.' },
|
|
946
|
+
privacyPolicyNotAccepted: { status: 400, code: 'PRIVACY_POLICY_NOT_ACCEPTED', message: 'Please accept the privacy policy.' },
|
|
947
|
+
emailDomainNotAllowed: { status: 400, code: 'EMAIL_DOMAIN_NOT_ALLOWED', message: 'Sign up is limited to approved email domains.' },
|
|
948
|
+
phoneCountryNotAllowed: { status: 400, code: 'PHONE_COUNTRY_NOT_ALLOWED', message: 'Phone numbers from this country are not supported.' },
|
|
949
|
+
invalidCredentials: { status: 401, code: 'INVALID_CREDENTIALS', message: 'Invalid email or password.' },
|
|
950
|
+
invalidOtp: { status: 400, code: 'INVALID_OTP', message: 'This code is incorrect.' },
|
|
951
|
+
otpExpired: { status: 400, code: 'OTP_EXPIRED', message: 'This code has expired or was used too many times. Please request a new one.' },
|
|
952
|
+
invalidOrExpiredLink: { status: 400, code: 'INVALID_TOKEN', message: 'This link is invalid, expired or already used.' },
|
|
953
|
+
unauthorized: { status: 401, code: 'UNAUTHORIZED', message: 'Please sign in to continue.' },
|
|
954
|
+
invalidAccessToken: { status: 401, code: 'INVALID_TOKEN', message: 'Your session has expired. Please sign in again.' },
|
|
955
|
+
invalidRefreshToken: { status: 401, code: 'INVALID_TOKEN', message: 'Your session has expired. Please sign in again.' },
|
|
956
|
+
invalidApiKey: { status: 401, code: 'INVALID_API_KEY', message: 'The API key is missing, invalid or revoked.' },
|
|
957
|
+
invalidSocialToken: { status: 401, code: 'INVALID_SOCIAL_TOKEN', message: 'We could not confirm your sign-in with this provider.' },
|
|
958
|
+
invalidPasskey: { status: 401, code: 'INVALID_PASSKEY', message: 'We could not confirm this passkey.' },
|
|
959
|
+
invalidTwoFactorCode: { status: 401, code: 'INVALID_OTP', message: 'This code is incorrect.' },
|
|
960
|
+
accountNotVerified: { status: 403, code: 'ACCOUNT_NOT_VERIFIED', message: 'Please finish verifying your account.' },
|
|
961
|
+
accountSuspended: { status: 403, code: 'ACCOUNT_SUSPENDED', message: 'This account has been suspended. Please contact support.' },
|
|
962
|
+
accountDeactivated: { status: 403, code: 'ACCOUNT_DEACTIVATED', message: 'This account is deactivated. Reactivate it to continue.' },
|
|
963
|
+
forbidden: { status: 403, code: 'FORBIDDEN', message: 'You do not have permission to do this.' },
|
|
964
|
+
missingApiKeyScope: { status: 403, code: 'FORBIDDEN', message: 'This API key is not allowed to do this.' },
|
|
965
|
+
signUpClosed: { status: 403, code: 'SIGN_UP_CLOSED', message: 'Sign up is currently closed.' },
|
|
966
|
+
signInMethodDisabled: { status: 403, code: 'SIGN_IN_METHOD_DISABLED', message: 'This sign-in method is not available.' },
|
|
967
|
+
featureDisabled: { status: 403, code: 'FEATURE_DISABLED', message: 'This feature is not turned on.' },
|
|
968
|
+
notFound: { status: 404, code: 'NOT_FOUND', message: 'Not found.' },
|
|
969
|
+
accountAlreadyExists: { status: 409, code: 'ACCOUNT_ALREADY_EXISTS', message: 'An account with these details already exists.' },
|
|
970
|
+
accountExistsWithOtherMethod: {
|
|
971
|
+
status: 409,
|
|
972
|
+
code: 'ACCOUNT_EXISTS_WITH_OTHER_METHOD',
|
|
973
|
+
message: 'An account with this email already exists. Sign in your usual way, then link this account from your profile.',
|
|
974
|
+
},
|
|
975
|
+
socialAccountAlreadyLinked: { status: 409, code: 'SOCIAL_ACCOUNT_IN_USE', message: 'This account is already linked to another user.' },
|
|
976
|
+
cannotRemoveLastSignInMethod: { status: 409, code: 'LAST_SIGN_IN_METHOD', message: 'Add another way to sign in before removing this one.' },
|
|
977
|
+
twoFactorAlreadyEnabled: { status: 409, code: 'TWO_FACTOR_ALREADY_ENABLED', message: 'Two-factor authentication is already on.' },
|
|
978
|
+
twoFactorNotEnabled: { status: 409, code: 'TWO_FACTOR_NOT_ENABLED', message: 'Two-factor authentication is not on.' },
|
|
979
|
+
twoFactorSetupNotStarted: { status: 409, code: 'TWO_FACTOR_SETUP_NOT_STARTED', message: 'Start two-factor setup first.' },
|
|
980
|
+
organizationSlugTaken: { status: 409, code: 'ORGANIZATION_SLUG_TAKEN', message: 'This organization handle is already taken.' },
|
|
981
|
+
alreadyOrganizationMember: { status: 409, code: 'ALREADY_MEMBER', message: 'This person is already a member.' },
|
|
982
|
+
lastOrganizationOwner: { status: 409, code: 'LAST_OWNER', message: 'An organization needs at least one owner.' },
|
|
983
|
+
accountNotDeactivated: { status: 409, code: 'ACCOUNT_NOT_DEACTIVATED', message: 'This account is not deactivated.' },
|
|
984
|
+
emailInUse: { status: 409, code: 'EMAIL_IN_USE', message: 'This email is already used by another account.' },
|
|
985
|
+
emailUnchanged: { status: 400, code: 'EMAIL_UNCHANGED', message: 'This is already your email.' },
|
|
986
|
+
recentSignInRequired: {
|
|
987
|
+
status: 403,
|
|
988
|
+
code: 'RECENT_SIGN_IN_REQUIRED',
|
|
989
|
+
message: 'For your security, please sign in again and then retry.',
|
|
990
|
+
},
|
|
991
|
+
originNotAllowed: { status: 403, code: 'FORBIDDEN', message: 'This request did not come from an allowed website.' },
|
|
992
|
+
tooManyRequests: { status: 429, code: 'TOO_MANY_REQUESTS', message: 'Too many attempts. Please try again later.' },
|
|
993
|
+
resendTooSoon: { status: 429, code: 'TOO_MANY_REQUESTS', message: 'Please wait a minute before requesting another code.' },
|
|
994
|
+
invalidSettings: { status: 400, code: 'INVALID_SETTINGS', message: 'Some settings are not valid.' },
|
|
995
|
+
unsafeWebhookUrl: { status: 400, code: 'UNSAFE_WEBHOOK_URL', message: 'Webhook URLs must be public https addresses.' },
|
|
996
|
+
notConfigured: { status: 501, code: 'NOT_CONFIGURED', message: 'This feature is not set up on the server.' },
|
|
997
|
+
internalError: { status: 500, code: 'INTERNAL_SERVER_ERROR', message: 'Something went wrong. Please try again.' },
|
|
998
|
+
};
|
|
999
|
+
/** Codes for Nest HTTP errors that are not AuthErrors (e.g. thrown by your own code or by Nest itself). */
|
|
1000
|
+
exports.ERROR_CODE_BY_HTTP_STATUS = {
|
|
1001
|
+
400: exports.AUTH_ERRORS.validationFailed.code,
|
|
1002
|
+
401: exports.AUTH_ERRORS.unauthorized.code,
|
|
1003
|
+
403: exports.AUTH_ERRORS.forbidden.code,
|
|
1004
|
+
404: exports.AUTH_ERRORS.notFound.code,
|
|
1005
|
+
409: 'CONFLICT',
|
|
1006
|
+
429: exports.AUTH_ERRORS.tooManyRequests.code,
|
|
1007
|
+
};
|
|
1008
|
+
// ---------------------------------------------------------------------------------------------
|
|
1009
|
+
// Default settings.
|
|
1010
|
+
// ---------------------------------------------------------------------------------------------
|
|
1011
|
+
/**
|
|
1012
|
+
* The settings used when neither your code nor an admin has changed them.
|
|
1013
|
+
* Secure by default: only email + password is on, verification is required, short-lived tokens.
|
|
1014
|
+
*/
|
|
1015
|
+
exports.DEFAULT_SETTINGS = {
|
|
1016
|
+
registration: {
|
|
1017
|
+
enabled: true,
|
|
1018
|
+
defaultRoles: [exports.DEFAULT_USER_ROLE],
|
|
1019
|
+
requireFirstAndLastName: true,
|
|
1020
|
+
requirePhoneNumber: false,
|
|
1021
|
+
requireTermsAcceptance: true,
|
|
1022
|
+
requirePrivacyPolicyAcceptance: true,
|
|
1023
|
+
allowedEmailDomains: [],
|
|
1024
|
+
},
|
|
1025
|
+
activation: {
|
|
1026
|
+
requireVerifiedEmail: true,
|
|
1027
|
+
requireVerifiedPhone: false,
|
|
1028
|
+
},
|
|
1029
|
+
password: {
|
|
1030
|
+
enabled: true,
|
|
1031
|
+
minimumLength: 10,
|
|
1032
|
+
requireUppercaseLetter: false,
|
|
1033
|
+
requireLowercaseLetter: false,
|
|
1034
|
+
requireNumber: false,
|
|
1035
|
+
requireSymbol: false,
|
|
1036
|
+
rejectBreachedPasswords: true,
|
|
1037
|
+
notifyOnChange: true,
|
|
1038
|
+
},
|
|
1039
|
+
emailVerification: { linkExpiresInHours: 24 },
|
|
1040
|
+
passwordReset: { linkExpiresInMinutes: 60 },
|
|
1041
|
+
emailChange: { linkExpiresInMinutes: 60 },
|
|
1042
|
+
emailOtp: { enabled: false, codeLength: 6, expiresInMinutes: 10, resendAfterSeconds: 60, allowSignUp: true },
|
|
1043
|
+
magicLink: { enabled: false, expiresInMinutes: 15, allowSignUp: true },
|
|
1044
|
+
phoneOtp: { enabled: false, codeLength: 6, expiresInMinutes: 10, resendAfterSeconds: 60, allowSignUp: true, allowedCountryCodes: [] },
|
|
1045
|
+
google: { enabled: false, clientIds: [] },
|
|
1046
|
+
apple: { enabled: false, clientIds: [] },
|
|
1047
|
+
facebook: { enabled: false, appId: '' },
|
|
1048
|
+
passkeys: { enabled: false },
|
|
1049
|
+
twoFactor: { enabled: true, challengeExpiresInMinutes: 5 },
|
|
1050
|
+
organizations: { enabled: false, allowUsersToCreate: true, invitationExpiresInDays: 7 },
|
|
1051
|
+
tokens: { accessTokenMinutes: 15, refreshTokenDays: 30, sessionDays: 90 },
|
|
1052
|
+
admin: { adminRoles: [exports.DEFAULT_ADMIN_ROLE] },
|
|
1053
|
+
security: { notifyOnNewSignIn: false },
|
|
1054
|
+
retention: { enabled: true, expiredRecordDays: 7, webhookDeliveryDays: 30, auditLogDays: 365 },
|
|
1055
|
+
};
|
|
1056
|
+
// ---------------------------------------------------------------------------------------------
|
|
1057
|
+
// Data retention (DataRetentionService).
|
|
1058
|
+
// ---------------------------------------------------------------------------------------------
|
|
1059
|
+
/** How often each server runs the cleanup, and how long after start the first run waits. */
|
|
1060
|
+
exports.DATA_RETENTION_INTERVAL_MS = exports.HOUR_IN_SECONDS * exports.SECOND_IN_MILLISECONDS;
|
|
1061
|
+
exports.DATA_RETENTION_FIRST_RUN_DELAY_MS = exports.MINUTE_IN_SECONDS * exports.SECOND_IN_MILLISECONDS;
|
|
1062
|
+
/** Rows deleted per statement, so a cleanup never holds locks for long. */
|
|
1063
|
+
exports.DATA_RETENTION_BATCH_SIZE = 5000;
|
|
1064
|
+
/** Most batches per table per run; the rest waits for the next run. */
|
|
1065
|
+
exports.DATA_RETENTION_MAX_BATCHES_PER_TABLE = 100;
|
|
1066
|
+
/** Names of the cleanup steps, used as keys in the cleanup report. */
|
|
1067
|
+
exports.DATA_RETENTION_STEP = {
|
|
1068
|
+
endedSessions: 'endedSessions',
|
|
1069
|
+
expiredRefreshTokens: 'expiredRefreshTokens',
|
|
1070
|
+
linkTokens: 'linkTokens',
|
|
1071
|
+
oneTimeCodes: 'oneTimeCodes',
|
|
1072
|
+
signInChallenges: 'signInChallenges',
|
|
1073
|
+
organizationInvitations: 'organizationInvitations',
|
|
1074
|
+
webhookDeliveries: 'webhookDeliveries',
|
|
1075
|
+
auditLog: 'auditLog',
|
|
1076
|
+
};
|
|
1077
|
+
/** SQL condition marking a row of each step as old (`:cutoff` = now minus the days to keep). */
|
|
1078
|
+
exports.DATA_RETENTION_OLD_ROW_CONDITION = {
|
|
1079
|
+
endedSessions: '(revoked_at IS NOT NULL AND revoked_at < :cutoff) OR expires_at < :cutoff',
|
|
1080
|
+
expiredRefreshTokens: 'expires_at < :cutoff',
|
|
1081
|
+
linkTokens: 'expires_at < :cutoff OR used_at < :cutoff',
|
|
1082
|
+
oneTimeCodes: 'expires_at < :cutoff OR used_at < :cutoff',
|
|
1083
|
+
signInChallenges: 'expires_at < :cutoff OR used_at < :cutoff',
|
|
1084
|
+
organizationInvitations: 'expires_at < :cutoff OR accepted_at < :cutoff OR revoked_at < :cutoff',
|
|
1085
|
+
// Pending deliveries are never removed: they still have to be sent.
|
|
1086
|
+
webhookDeliveries: `status <> '${exports.WEBHOOK_DELIVERY_STATUS.pending}' AND created_at < :cutoff`,
|
|
1087
|
+
auditLog: 'created_at < :cutoff',
|
|
1088
|
+
};
|
|
1089
|
+
/**
|
|
1090
|
+
* One batch of old rows (by primary key), so each DELETE statement stays short.
|
|
1091
|
+
*
|
|
1092
|
+
* @param tableName - The table to clean (from TypeORM metadata, never user input).
|
|
1093
|
+
* @param oldRowCondition - Which rows count as old, from DATA_RETENTION_OLD_ROW_CONDITION.
|
|
1094
|
+
* @returns A WHERE clause using the :cutoff and :batchSize parameters.
|
|
1095
|
+
*/
|
|
1096
|
+
const DATA_RETENTION_BATCH_CONDITION = (tableName, oldRowCondition) => `id IN (SELECT id FROM ${tableName} WHERE ${oldRowCondition} LIMIT :batchSize)`;
|
|
1097
|
+
exports.DATA_RETENTION_BATCH_CONDITION = DATA_RETENTION_BATCH_CONDITION;
|
|
1098
|
+
// ---------------------------------------------------------------------------------------------
|
|
1099
|
+
// Refresh token cookie (AuthOptions.refreshTokenCookie).
|
|
1100
|
+
// ---------------------------------------------------------------------------------------------
|
|
1101
|
+
/** Cookies named "__Host-..." are locked by the browser to Secure, path "/" and no domain. */
|
|
1102
|
+
exports.HOST_COOKIE_PREFIX = '__Host-';
|
|
1103
|
+
exports.COOKIE_SAME_SITE = { strict: 'Strict', lax: 'Lax', none: 'None' };
|
|
1104
|
+
exports.REFRESH_TOKEN_COOKIE_DEFAULTS = {
|
|
1105
|
+
name: `${exports.HOST_COOKIE_PREFIX}refresh_token`,
|
|
1106
|
+
/** "__Host-" cookies must use path "/" and no domain; with a custom name you may narrow it. */
|
|
1107
|
+
path: '/',
|
|
1108
|
+
sameSite: exports.COOKIE_SAME_SITE.strict,
|
|
1109
|
+
};
|
|
1110
|
+
/** Cookie attribute names, written by serializeCookie(). */
|
|
1111
|
+
exports.COOKIE_ATTRIBUTE = {
|
|
1112
|
+
maxAge: 'Max-Age',
|
|
1113
|
+
path: 'Path',
|
|
1114
|
+
domain: 'Domain',
|
|
1115
|
+
sameSite: 'SameSite',
|
|
1116
|
+
httpOnly: 'HttpOnly',
|
|
1117
|
+
secure: 'Secure',
|
|
1118
|
+
};
|
|
1119
|
+
exports.COOKIE_PAIR_SEPARATOR = ';';
|
|
1120
|
+
/** Between a cookie's attributes in a Set-Cookie header. */
|
|
1121
|
+
exports.COOKIE_ATTRIBUTE_SEPARATOR = '; ';
|
|
1122
|
+
exports.COOKIE_NAME_VALUE_SEPARATOR = '=';
|
|
1123
|
+
/** Response fields holding the refresh token; moved into the cookie when cookie mode is on. */
|
|
1124
|
+
exports.REFRESH_TOKEN_RESPONSE_FIELD = 'refreshToken';
|
|
1125
|
+
/** Response field saying the refresh token is in the cookie instead of the body. */
|
|
1126
|
+
exports.REFRESH_TOKEN_IN_COOKIE_FIELD = 'refreshTokenInCookie';
|
|
1127
|
+
/** Response field with the refresh token's expiry time (sets the cookie's lifetime). */
|
|
1128
|
+
exports.REFRESH_TOKEN_EXPIRES_AT_RESPONSE_FIELD = 'refreshTokenExpiresAt';
|
|
1129
|
+
// ---------------------------------------------------------------------------------------------
|
|
1130
|
+
// Health check and data export.
|
|
1131
|
+
// ---------------------------------------------------------------------------------------------
|
|
1132
|
+
exports.HEALTH_STATUS = { ok: 'ok', error: 'error', notUsed: 'not_used' };
|
|
1133
|
+
/** A health check that takes longer than this counts as failed. */
|
|
1134
|
+
exports.HEALTH_CHECK_TIMEOUT_MS = 2000;
|
|
1135
|
+
/** The cheapest query that proves PostgreSQL answers. */
|
|
1136
|
+
exports.HEALTH_CHECK_QUERY = 'SELECT 1';
|
|
1137
|
+
exports.HEALTH_SERVICE_UNAVAILABLE_STATUS = 503;
|
|
1138
|
+
/** Most audit-log entries included in "download my data" (newest first). */
|
|
1139
|
+
exports.DATA_EXPORT_AUDIT_LOG_LIMIT = 1000;
|
|
1140
|
+
/** Window of the admin stats endpoint ("sign-ins in the last 24 hours"). */
|
|
1141
|
+
exports.ADMIN_STATS_WINDOW_SECONDS = exports.DAY_IN_SECONDS;
|
|
1142
|
+
//# sourceMappingURL=constants.js.map
|