@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,294 @@
|
|
|
1
|
+
# Sign-in flows
|
|
2
|
+
|
|
3
|
+
Each flow below lists the requests your app makes and how to handle each response. Request and response bodies are JSON. All examples assume the default settings unless noted.
|
|
4
|
+
|
|
5
|
+
## Every sign-in ends the same way
|
|
6
|
+
|
|
7
|
+
Whichever method is used, a successful sign-in returns one of two shapes:
|
|
8
|
+
|
|
9
|
+
```jsonc
|
|
10
|
+
// Signed in
|
|
11
|
+
{
|
|
12
|
+
"success": true,
|
|
13
|
+
"status": "signed_in",
|
|
14
|
+
"accessToken": "eyJhbGciOiJFUzI1NiIs...", // send as "Authorization: Bearer ..."
|
|
15
|
+
"expiresIn": 900, // seconds
|
|
16
|
+
"refreshToken": "rt_Q2x...", // store securely, use once
|
|
17
|
+
"refreshTokenExpiresAt": "2026-11-19T10:00:00.000Z",
|
|
18
|
+
"sessionId": "8c1e...",
|
|
19
|
+
"user": { "id": "...", "email": "...", "roles": ["user"], "status": "ACTIVE", "onboardingCompleted": false, ... }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Two-factor authentication is on: ask for the authenticator code
|
|
23
|
+
{ "success": true, "status": "two_factor_required", "challengeToken": "ch_...", "expiresIn": 300 }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The account checks also happen in one place, so every method fails the same way:
|
|
27
|
+
|
|
28
|
+
| Error code | Meaning | What to show |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `ACCOUNT_NOT_VERIFIED` | Required verification missing. Body has `pendingVerifications: ["email"]` or `["phone"]`, plus `email` / `phoneNumber`. | Go to the verification screen |
|
|
31
|
+
| `ACCOUNT_DEACTIVATED` | The user switched their account off. Body has `reactivationToken` (valid 10 min). | "Welcome back, reactivate?" Then call `POST /auth/reactivate` |
|
|
32
|
+
| `ACCOUNT_SUSPENDED` | An admin blocked the account. | "Contact support" |
|
|
33
|
+
|
|
34
|
+
These codes appear only **after** the user has proven who they are (right password, right code), so they never reveal anything to a stranger.
|
|
35
|
+
|
|
36
|
+
## Two-factor step
|
|
37
|
+
|
|
38
|
+
When a sign-in returns `two_factor_required`:
|
|
39
|
+
|
|
40
|
+
```http
|
|
41
|
+
POST /auth/2fa/verify
|
|
42
|
+
{ "challengeToken": "ch_...", "code": "123456" }
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`code` is the 6-digit code from the authenticator app, **or** a backup code such as `k7m2-x9p4`. The response is the normal `signed_in` result. After 5 wrong codes the challenge stops working and the user must sign in again. Passkey sign-ins skip this step, because a passkey already counts as two factors.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Email + password
|
|
50
|
+
|
|
51
|
+
### Register
|
|
52
|
+
|
|
53
|
+
```http
|
|
54
|
+
POST /auth/register
|
|
55
|
+
{
|
|
56
|
+
"email": "asha@example.com",
|
|
57
|
+
"password": "correct horse battery",
|
|
58
|
+
"firstName": "Asha",
|
|
59
|
+
"lastName": "Rao",
|
|
60
|
+
"phoneNumber": "+919876543210", // optional unless registration.requirePhoneNumber
|
|
61
|
+
"acceptTerms": true,
|
|
62
|
+
"acceptPrivacyPolicy": true
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
| Response | Next step |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `201 { status: "verification_required", pendingVerifications: ["email"] }` | Show "check your email" |
|
|
69
|
+
| `201 { status: "signed_in", ... }` | Only when the settings require no verification |
|
|
70
|
+
| `400 INVALID_PASSWORD` with `requirements: [...]` | Show which rules failed |
|
|
71
|
+
| `400 INVALID_PASSWORD` (breach message) | "This password appeared in a data breach" |
|
|
72
|
+
| `400 TERMS_NOT_ACCEPTED` / `PRIVACY_POLICY_NOT_ACCEPTED` / `VALIDATION_ERROR` | Form errors |
|
|
73
|
+
|
|
74
|
+
If the email is **already registered**, the response is the same `verification_required`, and no account is created. A real owner who never verified gets a fresh verification email. This stops the sign-up form from revealing who has an account.
|
|
75
|
+
|
|
76
|
+
### Verify the email
|
|
77
|
+
|
|
78
|
+
The verification email has a token (`ev_...`). Put it in a link to your web page or deep link, and have the page call:
|
|
79
|
+
|
|
80
|
+
```http
|
|
81
|
+
POST /auth/email/verify
|
|
82
|
+
{ "token": "ev_..." }
|
|
83
|
+
→ { "success": true, "status": "ACTIVE", "pendingVerifications": [] }
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Resend with `POST /auth/email/send-verification { "email": "..." }` (always answers success). Instead of a link, you can verify with a code: `POST /auth/email/send-otp` and then `POST /auth/email/verify-otp`. That also signs the user in. See [Email code](#email-code).
|
|
87
|
+
|
|
88
|
+
### Sign in
|
|
89
|
+
|
|
90
|
+
```http
|
|
91
|
+
POST /auth/login
|
|
92
|
+
{ "email": "asha@example.com", "password": "..." }
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
A wrong email and a wrong password both give `401 INVALID_CREDENTIALS` and take the same time.
|
|
96
|
+
|
|
97
|
+
### Forgot / reset password
|
|
98
|
+
|
|
99
|
+
```http
|
|
100
|
+
POST /auth/password/forgot { "email": "asha@example.com" } → always success
|
|
101
|
+
POST /auth/password/reset { "token": "pr_...", "newPassword": "..." }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
A reset signs the user out on every device and marks the email verified (they just proved they read it). The user then signs in with the new password.
|
|
105
|
+
|
|
106
|
+
### Change password (signed in)
|
|
107
|
+
|
|
108
|
+
```http
|
|
109
|
+
POST /auth/password/change
|
|
110
|
+
Authorization: Bearer ...
|
|
111
|
+
{ "currentPassword": "...", "newPassword": "..." }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Other devices are signed out; this one stays signed in. Users who signed up with Google etc. have no password yet. They can leave out `currentPassword` to set one.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Email code
|
|
119
|
+
|
|
120
|
+
Good for passwordless apps, and for verifying an email by code instead of a link. Turn on with `emailOtp.enabled`.
|
|
121
|
+
|
|
122
|
+
```http
|
|
123
|
+
POST /auth/email/send-otp { "email": "asha@example.com" } → always success
|
|
124
|
+
POST /auth/email/verify-otp { "email": "asha@example.com", "code": "482913" }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- **Existing user:** the email is marked verified and the user is signed in.
|
|
128
|
+
- **Unknown email (sign-up):** an account is created. If your settings require names or consent, send them with `verify-otp`: `firstName`, `lastName`, `acceptTerms`, `acceptPrivacyPolicy`. If they're missing, you get `VALIDATION_ERROR` with `missingSignUpDetails: ["firstName", ...]`, and **the code is not used up**, so ask for the details and retry with the same code.
|
|
129
|
+
- **Wrong code:** `INVALID_OTP` with `attemptsLeft`. After 5 wrong tries, `OTP_EXPIRED`, so request a new code.
|
|
130
|
+
- **New code within 60 seconds:** `TOO_MANY_REQUESTS` with `retryAfterSeconds`.
|
|
131
|
+
|
|
132
|
+
## Magic link
|
|
133
|
+
|
|
134
|
+
Turn on with `magicLink.enabled`.
|
|
135
|
+
|
|
136
|
+
```http
|
|
137
|
+
POST /auth/magic-link/send { "email": "asha@example.com" } → always success
|
|
138
|
+
POST /auth/magic-link/verify { "token": "ml_...", "firstName": "...", ... }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Same rules as the email code. A link works once and expires after 15 minutes. For new users, collect the sign-up details on the page **before** calling verify, because the link is used up by the first call.
|
|
142
|
+
|
|
143
|
+
## Phone code (SMS)
|
|
144
|
+
|
|
145
|
+
Turn on with `phoneOtp.enabled` and configure `smsSender`.
|
|
146
|
+
|
|
147
|
+
```http
|
|
148
|
+
POST /auth/phone/send-otp { "phoneNumber": "+91 98765 43210" } → always success
|
|
149
|
+
POST /auth/phone/verify-otp { "phoneNumber": "+919876543210", "code": "482913" }
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Numbers must include the country code. Spaces, dashes and brackets are removed. Verifying also marks the phone verified, which is how a user with `pendingVerifications: ["phone"]` finishes activation.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Google, Apple and Facebook
|
|
157
|
+
|
|
158
|
+
Your app signs the user in with the provider's **own SDK or button** and sends the result to your API. The server never redirects and stores no provider passwords; it verifies the token itself.
|
|
159
|
+
|
|
160
|
+
| Provider | What to send as `token` | Get it with |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| Google | The **ID token** (JWT) | Google Identity Services (web), Credential Manager (Android), Google Sign-In (iOS) |
|
|
163
|
+
| Apple | The **identity token** (JWT) | Sign in with Apple JS / `ASAuthorizationAppleIDCredential.identityToken` |
|
|
164
|
+
| Facebook | The user **access token** | Facebook Login SDK |
|
|
165
|
+
|
|
166
|
+
```http
|
|
167
|
+
POST /auth/social/google
|
|
168
|
+
{ "token": "eyJhbGciOiJSUzI1NiIs...", "nonce": "optional raw nonce" }
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Apple sends the user's name **only to your app, and only the first time**. Pass it along as `firstName` / `lastName`. Using a `nonce` is strongly recommended with Apple: generate a random string, give its SHA-256 to Apple, and send the raw string here.
|
|
172
|
+
|
|
173
|
+
How the account is chosen:
|
|
174
|
+
|
|
175
|
+
1. The provider account was linked before: sign in as that user.
|
|
176
|
+
2. The provider **verified** the email and a user has that email: link it and sign in.
|
|
177
|
+
3. The email is **not** verified by the provider (Facebook never verifies) and a user has that email: `409 ACCOUNT_EXISTS_WITH_OTHER_METHOD`. Tell the user to sign in their usual way and connect the provider from settings. This blocks account takeover.
|
|
178
|
+
4. Nobody has the email: create an account (the same `missingSignUpDetails` rule as codes).
|
|
179
|
+
|
|
180
|
+
If Facebook returns no email (the user declined), sign-up fails with `missingSignUpDetails: ["email"]`.
|
|
181
|
+
|
|
182
|
+
Connect or disconnect while signed in:
|
|
183
|
+
|
|
184
|
+
```http
|
|
185
|
+
POST /auth/social/google/link { "token": "..." }
|
|
186
|
+
DELETE /auth/social/google
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Disconnecting the last way to sign in is refused with `LAST_SIGN_IN_METHOD`.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Passkeys
|
|
194
|
+
|
|
195
|
+
Turn on with `passkeys.enabled` and configure the `passkeys` option. The flow uses the WebAuthn browser API, or the platform passkey APIs on iOS and Android. In a browser, [`@simplewebauthn/browser`](https://simplewebauthn.dev) makes it short.
|
|
196
|
+
|
|
197
|
+
### Add a passkey (signed in)
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
import { startRegistration } from '@simplewebauthn/browser';
|
|
201
|
+
|
|
202
|
+
const { challengeToken, options } = await api.post('/auth/passkeys/register/options');
|
|
203
|
+
const response = await startRegistration({ optionsJSON: options });
|
|
204
|
+
await api.post('/auth/passkeys/register/verify', { challengeToken, response, name: 'My laptop' });
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Sign in with a passkey
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
import { startAuthentication } from '@simplewebauthn/browser';
|
|
211
|
+
|
|
212
|
+
const { challengeToken, options } = await api.post('/auth/passkeys/sign-in/options');
|
|
213
|
+
const response = await startAuthentication({ optionsJSON: options });
|
|
214
|
+
const result = await api.post('/auth/passkeys/sign-in/verify', { challengeToken, response });
|
|
215
|
+
// result.status === "signed_in"
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
No email is needed: the device offers the user's passkeys for your site. Passkeys require a fingerprint, face or PIN, so no 2FA step follows. Manage them with `GET /auth/passkeys`, `PATCH /auth/passkeys/:id { name }` and `DELETE /auth/passkeys/:id`.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Two-factor authentication: turning it on
|
|
223
|
+
|
|
224
|
+
```http
|
|
225
|
+
POST /auth/2fa/setup
|
|
226
|
+
→ { "secret": "JBSWY3DPEHPK3PXP", "authenticatorAppUrl": "otpauth://totp/MyApp:asha%40example.com?..." }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Show `authenticatorAppUrl` as a QR code, with `secret` underneath for manual entry. Then ask for the first code:
|
|
230
|
+
|
|
231
|
+
```http
|
|
232
|
+
POST /auth/2fa/enable { "code": "123456" }
|
|
233
|
+
→ { "backupCodes": ["k7m2-x9p4", ...10 codes] }
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Show the backup codes **once** and ask the user to save them. Turning 2FA on signs out other devices.
|
|
237
|
+
|
|
238
|
+
| Request | Purpose |
|
|
239
|
+
|---|---|
|
|
240
|
+
| `GET /auth/2fa` | `{ isEnabled, remainingBackupCodes }` |
|
|
241
|
+
| `POST /auth/2fa/disable { code }` | Turn off (authenticator or backup code) |
|
|
242
|
+
| `POST /auth/2fa/backup-codes { code }` | New set of backup codes |
|
|
243
|
+
|
|
244
|
+
A user who lost both their phone and backup codes needs an admin: `POST /auth/admin/users/:id/reset-2fa`, after support has confirmed who they are.
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Sessions and tokens
|
|
249
|
+
|
|
250
|
+
### Refreshing
|
|
251
|
+
|
|
252
|
+
When an access token expires (`401 INVALID_TOKEN`), refresh once and retry the request:
|
|
253
|
+
|
|
254
|
+
```http
|
|
255
|
+
POST /auth/refresh { "refreshToken": "rt_..." }
|
|
256
|
+
→ { "success": true, "accessToken": "...", "refreshToken": "rt_NEW...", ... }
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Always replace the stored refresh token with the new one.** Each refresh token works once. If an old one is used again, the server assumes it was stolen and ends that session, and the user must sign in again. If several requests fail at the same moment, make sure only **one** refresh runs. See [Client integration](client-integration.md#refreshing-tokens).
|
|
260
|
+
|
|
261
|
+
### Signing out
|
|
262
|
+
|
|
263
|
+
```http
|
|
264
|
+
POST /auth/logout → this device
|
|
265
|
+
POST /auth/logout-all → every device
|
|
266
|
+
GET /auth/sessions → list devices, with isCurrentDevice
|
|
267
|
+
DELETE /auth/sessions/:id
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
After sign-out, the access token keeps working until it expires (at most 15 minutes by default), except on routes marked `@CheckSession()`, which check the session live. Delete both tokens from the client right away.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Account life cycle
|
|
275
|
+
|
|
276
|
+
```
|
|
277
|
+
register / first sign-in
|
|
278
|
+
│
|
|
279
|
+
▼
|
|
280
|
+
┌──── PENDING_VERIFICATION ────┐
|
|
281
|
+
│ (verify email / phone) │
|
|
282
|
+
▼ │
|
|
283
|
+
ACTIVE ◄──── reactivate ──── DEACTIVATED
|
|
284
|
+
│ ▲ ▲
|
|
285
|
+
suspend │ │ unsuspend (admin) │ POST /auth/deactivate
|
|
286
|
+
(admin) ▼ │ │
|
|
287
|
+
SUSPENDED ACTIVE
|
|
288
|
+
|
|
289
|
+
any state ── DELETE /auth/me or admin ──► DELETED (personal data wiped, permanent)
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
- **Deactivate:** `POST /auth/deactivate` signs out everywhere. Later, signing in returns `ACCOUNT_DEACTIVATED` with a `reactivationToken`; `POST /auth/reactivate { reactivationToken }` turns the account back on and signs in.
|
|
293
|
+
- **Delete:** `DELETE /auth/me` wipes email, phone, names, roles, passwords, linked accounts, passkeys and 2FA, and ends all sessions. The user row stays with status `DELETED`, so your own tables' foreign keys don't break. Delete your app's own data for that user in an `onEvent` handler for `user.deleted`.
|
|
294
|
+
- **Onboarding:** after your profile screens, call `UsersService.completeOnboarding(userId, request)` from your own module. `user.onboardingCompleted` then becomes `true`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,94 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@techelusive/nest-auth",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Reusable, production-grade authentication for NestJS apps: passwords, codes, magic links, social, passkeys, 2FA, organizations and API keys, stored in each app's own database",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/TechElusive/techelusive-authentication.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/TechElusive/techelusive-authentication#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/TechElusive/techelusive-authentication/issues"
|
|
13
|
+
},
|
|
14
|
+
"main": "dist/index.js",
|
|
15
|
+
"types": "dist/index.d.ts",
|
|
16
|
+
"files": [
|
|
17
|
+
"CHANGELOG.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"README.md",
|
|
20
|
+
"api",
|
|
21
|
+
"dist",
|
|
22
|
+
"docs"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
|
|
29
|
+
"test": "node --test --require ts-node/register --require tsconfig-paths/register test/*.test.ts",
|
|
30
|
+
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.playground.json --noEmit",
|
|
31
|
+
"prepublishOnly": "npm run typecheck && npm test && npm run build",
|
|
32
|
+
"e2e": "node scripts/e2e-check.js",
|
|
33
|
+
"check-migration": "node scripts/check-migration.js",
|
|
34
|
+
"api-specs": "node scripts/api/generate-api-specs.js",
|
|
35
|
+
"playground:db": "docker compose -f playground/docker-compose.yml up -d",
|
|
36
|
+
"playground:build": "tsc -p tsconfig.playground.json && tsc-alias -p tsconfig.playground.json",
|
|
37
|
+
"playground": "npm run build && npm run api-specs && npm run playground:build && node dist-playground/playground/main.js",
|
|
38
|
+
"check:http": "node scripts/http-flow-check.js",
|
|
39
|
+
"check:cookies": "node scripts/http-cookie-check.js"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@nestjs/common": "^11.0.0",
|
|
43
|
+
"@nestjs/core": "^11.0.0",
|
|
44
|
+
"class-transformer": "^0.5.1",
|
|
45
|
+
"class-validator": "^0.14.1",
|
|
46
|
+
"reflect-metadata": "^0.2.0",
|
|
47
|
+
"rxjs": "^7.8.0",
|
|
48
|
+
"typeorm": "^0.3.20 || ^1.0.0"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@aws-sdk/client-kms": "^3.700.0",
|
|
52
|
+
"@simplewebauthn/server": "^13.1.0",
|
|
53
|
+
"argon2": "^0.41.1"
|
|
54
|
+
},
|
|
55
|
+
"optionalDependencies": {
|
|
56
|
+
"ioredis": "^5.4.1"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@nestjs/common": "^11.0.0",
|
|
60
|
+
"@nestjs/core": "^11.0.0",
|
|
61
|
+
"@nestjs/platform-express": "^11.0.0",
|
|
62
|
+
"@types/express": "^5.0.0",
|
|
63
|
+
"@types/node": "^22.20.5",
|
|
64
|
+
"class-transformer": "^0.5.1",
|
|
65
|
+
"class-validator": "^0.14.1",
|
|
66
|
+
"pg": "^8.13.0",
|
|
67
|
+
"reflect-metadata": "^0.2.2",
|
|
68
|
+
"rxjs": "^7.8.1",
|
|
69
|
+
"ts-node": "^10.9.2",
|
|
70
|
+
"tsc-alias": "^1.9.8",
|
|
71
|
+
"tsconfig-paths": "^4.2.0",
|
|
72
|
+
"typeorm": "^0.3.20",
|
|
73
|
+
"typescript": "^5.6.3"
|
|
74
|
+
},
|
|
75
|
+
"keywords": [
|
|
76
|
+
"nestjs",
|
|
77
|
+
"authentication",
|
|
78
|
+
"auth",
|
|
79
|
+
"typeorm",
|
|
80
|
+
"postgresql",
|
|
81
|
+
"jwt",
|
|
82
|
+
"passkeys",
|
|
83
|
+
"webauthn",
|
|
84
|
+
"totp",
|
|
85
|
+
"2fa",
|
|
86
|
+
"oauth",
|
|
87
|
+
"otp",
|
|
88
|
+
"magic-link"
|
|
89
|
+
],
|
|
90
|
+
"publishConfig": {
|
|
91
|
+
"access": "public",
|
|
92
|
+
"provenance": true
|
|
93
|
+
}
|
|
94
|
+
}
|