@techelusive/nest-auth 0.0.0-stage → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +71 -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 @@
|
|
|
1
|
+
{"version":3,"file":"jwks.controller.js","sourceRoot":"","sources":["../../../src/features/signing-keys/jwks.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAyD;AACzD,8DAAkD;AAClD,iEAA4D;AAC5D,2CAAsE;AACtE,wEAAkE;AAElE;;;GAGG;AAII,IAAM,cAAc,GAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,WAA+B;QAA/B,gBAAW,GAAX,WAAW,CAAoB;IAAG,CAAC;IAEhE;;;;OAIG;IAGG,AAAN,KAAK,CAAC,aAAa;QACjB,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,EAAE,CAAC;QAC/D,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;CACF,CAAA;AAdY,wCAAc;AAUnB;IAFL,IAAA,YAAG,EAAC,kBAAM,CAAC,SAAS,CAAC,IAAI,CAAC;IAC1B,IAAA,eAAM,EAAC,uBAAW,CAAC,YAAY,EAAE,8BAAkB,CAAC;;;;mDAIpD;yBAbU,cAAc;IAH1B,IAAA,mBAAU,EAAC,kBAAM,CAAC,SAAS,CAAC,MAAM,CAAC;IACnC,IAAA,wCAAiB,GAAE;IACnB,IAAA,wBAAM,GAAE;qCAEmC,yCAAkB;GADjD,cAAc,CAc1B"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { KeyObject } from 'node:crypto';
|
|
2
|
+
import { AuthOptions } from '../../auth-options.js';
|
|
3
|
+
import { EncryptionService } from '../../common/encryption.service.js';
|
|
4
|
+
import { JsonWebKey } from '../../common/crypto/jwt.js';
|
|
5
|
+
export interface LoadedSigningKeys {
|
|
6
|
+
/** The key that signs every new token. */
|
|
7
|
+
currentKey: {
|
|
8
|
+
keyId: string;
|
|
9
|
+
privateKey: KeyObject;
|
|
10
|
+
};
|
|
11
|
+
/** Public keys that can verify tokens: the current one plus recently retired ones. */
|
|
12
|
+
publicKeysById: Map<string, KeyObject>;
|
|
13
|
+
/** The same public keys in JWK form, served at /.well-known/jwks.json. */
|
|
14
|
+
publicJwks: JsonWebKey[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Manages the ES256 keys that sign access tokens.
|
|
18
|
+
*
|
|
19
|
+
* - The first key is created automatically the first time it is needed.
|
|
20
|
+
* - Private keys are encrypted before being saved (EncryptionService).
|
|
21
|
+
* - rotateSigningKey() replaces the key; tokens signed by the old key keep working until they expire.
|
|
22
|
+
*/
|
|
23
|
+
export declare class SigningKeysService {
|
|
24
|
+
private readonly encryption;
|
|
25
|
+
private readonly logger;
|
|
26
|
+
private readonly database;
|
|
27
|
+
private cachedKeys?;
|
|
28
|
+
constructor(options: AuthOptions, encryption: EncryptionService);
|
|
29
|
+
/**
|
|
30
|
+
* Returns the keys, re-reading them from the database at most once a minute.
|
|
31
|
+
* A failed load is not cached, so the next call tries again.
|
|
32
|
+
*
|
|
33
|
+
* @returns The current private key and every public key that may still verify tokens.
|
|
34
|
+
*/
|
|
35
|
+
getSigningKeys(): Promise<LoadedSigningKeys>;
|
|
36
|
+
/**
|
|
37
|
+
* Retires the current key and starts signing with a new one. Run it every few months, or at
|
|
38
|
+
* once if you think a key leaked. Other servers pick up the new key within a minute, and tokens
|
|
39
|
+
* signed with the old key keep working until they expire.
|
|
40
|
+
*/
|
|
41
|
+
rotateSigningKey(): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Reads the current key and recently retired ones from the database, creating the first key if needed.
|
|
44
|
+
*
|
|
45
|
+
* @returns The loaded keys.
|
|
46
|
+
* @throws If there is no current key or it can't be decrypted.
|
|
47
|
+
*/
|
|
48
|
+
private loadSigningKeys;
|
|
49
|
+
/**
|
|
50
|
+
* Creates the very first signing key. Safe when several servers start at once: the database
|
|
51
|
+
* allows only one current key, so the losers simply use the winner's.
|
|
52
|
+
*/
|
|
53
|
+
private createFirstKeyIfMissing;
|
|
54
|
+
/**
|
|
55
|
+
* Binds the encrypted private key to its key id, so it can't be swapped with another row.
|
|
56
|
+
*
|
|
57
|
+
* @param keyId - The key's id.
|
|
58
|
+
* @returns The context for `EncryptionService`.
|
|
59
|
+
*/
|
|
60
|
+
private encryptionContext;
|
|
61
|
+
/**
|
|
62
|
+
* Creates a new ES256 key pair. The key id is the key's RFC 7638 thumbprint.
|
|
63
|
+
*
|
|
64
|
+
* @returns The row to insert: key id, public JWK, and the encrypted private key.
|
|
65
|
+
*/
|
|
66
|
+
private generateSigningKey;
|
|
67
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.SigningKeysService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const node_crypto_1 = require("node:crypto");
|
|
18
|
+
const typeorm_1 = require("typeorm");
|
|
19
|
+
const encryption_service_1 = require("../../common/encryption.service.js");
|
|
20
|
+
const jwt_1 = require("../../common/crypto/jwt.js");
|
|
21
|
+
const constants_1 = require("../../constants.js");
|
|
22
|
+
const entities_1 = require("../../database/entities/index.js");
|
|
23
|
+
/**
|
|
24
|
+
* Manages the ES256 keys that sign access tokens.
|
|
25
|
+
*
|
|
26
|
+
* - The first key is created automatically the first time it is needed.
|
|
27
|
+
* - Private keys are encrypted before being saved (EncryptionService).
|
|
28
|
+
* - rotateSigningKey() replaces the key; tokens signed by the old key keep working until they expire.
|
|
29
|
+
*/
|
|
30
|
+
let SigningKeysService = class SigningKeysService {
|
|
31
|
+
encryption;
|
|
32
|
+
logger = new common_1.Logger(constants_1.LOG_CONTEXT.signingKeys);
|
|
33
|
+
database;
|
|
34
|
+
cachedKeys;
|
|
35
|
+
constructor(options, encryption) {
|
|
36
|
+
this.encryption = encryption;
|
|
37
|
+
this.database = options.dataSource;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns the keys, re-reading them from the database at most once a minute.
|
|
41
|
+
* A failed load is not cached, so the next call tries again.
|
|
42
|
+
*
|
|
43
|
+
* @returns The current private key and every public key that may still verify tokens.
|
|
44
|
+
*/
|
|
45
|
+
getSigningKeys() {
|
|
46
|
+
const cacheIsFresh = this.cachedKeys && Date.now() - this.cachedKeys.loadedAt < constants_1.SIGNING_KEYS_CACHE_SECONDS * constants_1.SECOND_IN_MILLISECONDS;
|
|
47
|
+
if (!cacheIsFresh) {
|
|
48
|
+
const keys = this.loadSigningKeys();
|
|
49
|
+
keys.catch(() => (this.cachedKeys = undefined));
|
|
50
|
+
this.cachedKeys = { keys, loadedAt: Date.now() };
|
|
51
|
+
}
|
|
52
|
+
return this.cachedKeys.keys;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Retires the current key and starts signing with a new one. Run it every few months, or at
|
|
56
|
+
* once if you think a key leaked. Other servers pick up the new key within a minute, and tokens
|
|
57
|
+
* signed with the old key keep working until they expire.
|
|
58
|
+
*/
|
|
59
|
+
async rotateSigningKey() {
|
|
60
|
+
const newKey = await this.generateSigningKey();
|
|
61
|
+
await this.database.transaction(async (transaction) => {
|
|
62
|
+
await transaction.update(entities_1.SigningKeyEntity, { retiredAt: (0, typeorm_1.IsNull)() }, { retiredAt: new Date() });
|
|
63
|
+
await transaction.insert(entities_1.SigningKeyEntity, newKey);
|
|
64
|
+
});
|
|
65
|
+
this.cachedKeys = undefined;
|
|
66
|
+
this.logger.log(`Signing key rotated. New key id: ${newKey.keyId}`);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Reads the current key and recently retired ones from the database, creating the first key if needed.
|
|
70
|
+
*
|
|
71
|
+
* @returns The loaded keys.
|
|
72
|
+
* @throws If there is no current key or it can't be decrypted.
|
|
73
|
+
*/
|
|
74
|
+
async loadSigningKeys() {
|
|
75
|
+
await this.createFirstKeyIfMissing();
|
|
76
|
+
const oldestRetirementStillPublished = new Date(Date.now() - constants_1.RETIRED_SIGNING_KEY_PUBLISH_SECONDS * constants_1.SECOND_IN_MILLISECONDS);
|
|
77
|
+
const keyRows = await this.database.getRepository(entities_1.SigningKeyEntity).find({
|
|
78
|
+
where: [{ retiredAt: (0, typeorm_1.IsNull)() }, { retiredAt: (0, typeorm_1.MoreThan)(oldestRetirementStillPublished) }],
|
|
79
|
+
});
|
|
80
|
+
const currentKeyRow = keyRows.find((row) => row.retiredAt === null);
|
|
81
|
+
if (!currentKeyRow)
|
|
82
|
+
throw new Error('No current signing key found');
|
|
83
|
+
const privateJwk = JSON.parse(await this.encryption.decrypt(currentKeyRow.encryptedPrivateKey, this.encryptionContext(currentKeyRow.keyId)));
|
|
84
|
+
return {
|
|
85
|
+
currentKey: { keyId: currentKeyRow.keyId, privateKey: (0, jwt_1.privateKeyFromJwk)(privateJwk) },
|
|
86
|
+
publicKeysById: new Map(keyRows.map((row) => [row.keyId, (0, jwt_1.publicKeyFromJwk)(row.publicKey)])),
|
|
87
|
+
publicJwks: keyRows.map((row) => row.publicKey),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Creates the very first signing key. Safe when several servers start at once: the database
|
|
92
|
+
* allows only one current key, so the losers simply use the winner's.
|
|
93
|
+
*/
|
|
94
|
+
async createFirstKeyIfMissing() {
|
|
95
|
+
const keys = this.database.getRepository(entities_1.SigningKeyEntity);
|
|
96
|
+
if (await keys.exists({ where: { retiredAt: (0, typeorm_1.IsNull)() } }))
|
|
97
|
+
return;
|
|
98
|
+
try {
|
|
99
|
+
await keys.insert(await this.generateSigningKey());
|
|
100
|
+
this.logger.log('Created the first signing key');
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
// Two servers started at the same moment. The database allows only one current key, so the other server won.
|
|
104
|
+
if (!(await keys.exists({ where: { retiredAt: (0, typeorm_1.IsNull)() } })))
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Binds the encrypted private key to its key id, so it can't be swapped with another row.
|
|
110
|
+
*
|
|
111
|
+
* @param keyId - The key's id.
|
|
112
|
+
* @returns The context for `EncryptionService`.
|
|
113
|
+
*/
|
|
114
|
+
encryptionContext(keyId) {
|
|
115
|
+
return { purpose: constants_1.ENCRYPTION_PURPOSE.signingKey, keyId };
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Creates a new ES256 key pair. The key id is the key's RFC 7638 thumbprint.
|
|
119
|
+
*
|
|
120
|
+
* @returns The row to insert: key id, public JWK, and the encrypted private key.
|
|
121
|
+
*/
|
|
122
|
+
async generateSigningKey() {
|
|
123
|
+
const { privateKey, publicKey } = (0, node_crypto_1.generateKeyPairSync)(constants_1.SIGNING_KEY_PAIR_TYPE, { namedCurve: constants_1.SIGNING_KEY_CURVE });
|
|
124
|
+
const publicJwk = publicKey.export({ format: constants_1.JWK_EXPORT_FORMAT });
|
|
125
|
+
// The key id is the key's RFC 7638 thumbprint: a hash of its public parts.
|
|
126
|
+
const keyId = (0, node_crypto_1.createHash)(constants_1.HASH_ALGORITHM.sha256)
|
|
127
|
+
.update(JSON.stringify({ crv: publicJwk.crv, kty: publicJwk.kty, x: publicJwk.x, y: publicJwk.y }))
|
|
128
|
+
.digest(constants_1.TEXT_ENCODING.base64Url);
|
|
129
|
+
return {
|
|
130
|
+
keyId,
|
|
131
|
+
publicKey: { kty: publicJwk.kty, crv: publicJwk.crv, x: publicJwk.x, y: publicJwk.y, kid: keyId, alg: constants_1.SIGNING_ALGORITHM, use: constants_1.JWK_USE_SIGNATURE },
|
|
132
|
+
encryptedPrivateKey: await this.encryption.encrypt(JSON.stringify(privateKey.export({ format: constants_1.JWK_EXPORT_FORMAT })), this.encryptionContext(keyId)),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
exports.SigningKeysService = SigningKeysService;
|
|
137
|
+
exports.SigningKeysService = SigningKeysService = __decorate([
|
|
138
|
+
(0, common_1.Injectable)(),
|
|
139
|
+
__param(0, (0, common_1.Inject)(constants_1.AUTH_OPTIONS_TOKEN)),
|
|
140
|
+
__metadata("design:paramtypes", [Object, encryption_service_1.EncryptionService])
|
|
141
|
+
], SigningKeysService);
|
|
142
|
+
//# sourceMappingURL=signing-keys.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signing-keys.service.js","sourceRoot":"","sources":["../../../src/features/signing-keys/signing-keys.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { AuthOptions } from '../../auth-options.js';
|
|
2
|
+
import { RateLimiterService } from '../../common/rate-limiter.service.js';
|
|
3
|
+
import { RequestContext } from '../../common/request-context.js';
|
|
4
|
+
import { SocialProvider } from '../../database/entities/index.js';
|
|
5
|
+
import { AuthEventsService } from '../events/auth-events.service.js';
|
|
6
|
+
import { SettingsService } from '../settings/settings.service.js';
|
|
7
|
+
import { SignInService } from '../sign-in/sign-in.service.js';
|
|
8
|
+
import { SignInResult } from '../sign-in/sign-in.types.js';
|
|
9
|
+
import { SignInMethodsService } from '../users/sign-in-methods.service.js';
|
|
10
|
+
import { UsersService } from '../users/users.service.js';
|
|
11
|
+
import { SocialSignInInput } from './social.dto';
|
|
12
|
+
import { SocialProvidersService } from './social-providers.service';
|
|
13
|
+
/**
|
|
14
|
+
* Sign in with Google, Apple or Facebook, and link / unlink them to an existing account.
|
|
15
|
+
*
|
|
16
|
+
* Who the token belongs to is decided like this:
|
|
17
|
+
* - The provider account is already linked: sign in as that user.
|
|
18
|
+
* - The provider PROVED the email, and a user has it: link to that user, then sign in.
|
|
19
|
+
* - The email is NOT proven, and a user has it: refuse (ACCOUNT_EXISTS_WITH_OTHER_METHOD).
|
|
20
|
+
* Otherwise anyone could create a Facebook account with your email and take over yours.
|
|
21
|
+
* - Nobody has the email: create a new account (if sign-up is open).
|
|
22
|
+
*/
|
|
23
|
+
export declare class SocialAuthService {
|
|
24
|
+
private readonly settingsService;
|
|
25
|
+
private readonly providers;
|
|
26
|
+
private readonly users;
|
|
27
|
+
private readonly signIn;
|
|
28
|
+
private readonly signInMethods;
|
|
29
|
+
private readonly rateLimiter;
|
|
30
|
+
private readonly events;
|
|
31
|
+
private readonly database;
|
|
32
|
+
constructor(options: AuthOptions, settingsService: SettingsService, providers: SocialProvidersService, users: UsersService, signIn: SignInService, signInMethods: SignInMethodsService, rateLimiter: RateLimiterService, events: AuthEventsService);
|
|
33
|
+
/**
|
|
34
|
+
* Signs in (or signs up) with a token from Google, Apple or Facebook.
|
|
35
|
+
* See the class comment for how the token is matched to a user.
|
|
36
|
+
*
|
|
37
|
+
* @param provider - "google", "apple" or "facebook".
|
|
38
|
+
* @param input - The provider's token, the optional nonce, and sign-up details for new accounts.
|
|
39
|
+
* @param request - Who is asking.
|
|
40
|
+
* @returns Tokens, or a 2FA challenge.
|
|
41
|
+
* @throws AuthError INVALID_SOCIAL_TOKEN when the provider's token doesn't check out.
|
|
42
|
+
* @throws AuthError ACCOUNT_EXISTS_WITH_OTHER_METHOD when a user has this email but the provider didn't prove it.
|
|
43
|
+
* @throws AuthError SIGN_UP_CLOSED or VALIDATION_ERROR when a new account can't be created.
|
|
44
|
+
*/
|
|
45
|
+
signInWithProvider(provider: SocialProvider, input: SocialSignInInput, request: RequestContext): Promise<SignInResult>;
|
|
46
|
+
/**
|
|
47
|
+
* Links a provider account to the signed-in user (e.g. "Connect Google" in settings).
|
|
48
|
+
* Linking the same account again is a no-op.
|
|
49
|
+
*
|
|
50
|
+
* @param userId - The signed-in user.
|
|
51
|
+
* @param provider - "google", "apple" or "facebook".
|
|
52
|
+
* @param input - The provider's token and optional nonce.
|
|
53
|
+
* @param request - Who is asking (for the audit log).
|
|
54
|
+
* @returns `{ success: true }`.
|
|
55
|
+
* @throws AuthError SOCIAL_ACCOUNT_IN_USE when that provider account belongs to another user.
|
|
56
|
+
*/
|
|
57
|
+
linkProvider(userId: string, provider: SocialProvider, input: SocialSignInInput, request: RequestContext): Promise<{
|
|
58
|
+
success: true;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Removes a linked provider, unless it is the user's only way to sign in.
|
|
62
|
+
*
|
|
63
|
+
* @param userId - The signed-in user.
|
|
64
|
+
* @param provider - The provider to disconnect.
|
|
65
|
+
* @param request - Who is asking (for the audit log).
|
|
66
|
+
* @returns `{ success: true }`.
|
|
67
|
+
* @throws AuthError NOT_FOUND when the provider isn't linked.
|
|
68
|
+
* @throws AuthError LAST_SIGN_IN_METHOD when it is the user's only way to sign in.
|
|
69
|
+
*/
|
|
70
|
+
unlinkProvider(userId: string, provider: SocialProvider, request: RequestContext): Promise<{
|
|
71
|
+
success: true;
|
|
72
|
+
}>;
|
|
73
|
+
/**
|
|
74
|
+
* @param identity - Who the provider says the user is.
|
|
75
|
+
* @returns The link row for this provider account, or null.
|
|
76
|
+
*/
|
|
77
|
+
private findLinkedAccount;
|
|
78
|
+
/**
|
|
79
|
+
* Links a provider account to a user.
|
|
80
|
+
*
|
|
81
|
+
* @param userId - The user.
|
|
82
|
+
* @param identity - Who the provider says the user is.
|
|
83
|
+
* @param request - Who is asking (for the audit log).
|
|
84
|
+
* @param transaction - Pass one to save the link together with other rows (e.g. a new user).
|
|
85
|
+
* @throws AuthError SOCIAL_ACCOUNT_IN_USE when two requests link the same provider account at once.
|
|
86
|
+
*/
|
|
87
|
+
private saveLink;
|
|
88
|
+
/**
|
|
89
|
+
* Creates a new account from a provider's identity, linked to that provider.
|
|
90
|
+
* Names from the app (Apple sends them only to the app) win over names from the provider.
|
|
91
|
+
*
|
|
92
|
+
* @param identity - Who the provider says the user is.
|
|
93
|
+
* @param input - Sign-up details sent by the app.
|
|
94
|
+
* @param settings - The current settings.
|
|
95
|
+
* @param request - Who is asking.
|
|
96
|
+
* @returns The new user.
|
|
97
|
+
* @throws AuthError SIGN_UP_CLOSED, EMAIL_DOMAIN_NOT_ALLOWED, or VALIDATION_ERROR (e.g. no email from Facebook).
|
|
98
|
+
*/
|
|
99
|
+
private createUserFromIdentity;
|
|
100
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.SocialAuthService = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const typeorm_1 = require("typeorm");
|
|
18
|
+
const auth_error_1 = require("../../common/auth-error.js");
|
|
19
|
+
const identifiers_1 = require("../../common/identifiers.js");
|
|
20
|
+
const rate_limiter_service_1 = require("../../common/rate-limiter.service.js");
|
|
21
|
+
const constants_1 = require("../../constants.js");
|
|
22
|
+
const entities_1 = require("../../database/entities/index.js");
|
|
23
|
+
const auth_events_service_1 = require("../events/auth-events.service.js");
|
|
24
|
+
const settings_service_1 = require("../settings/settings.service.js");
|
|
25
|
+
const sign_in_service_1 = require("../sign-in/sign-in.service.js");
|
|
26
|
+
const sign_in_methods_service_1 = require("../users/sign-in-methods.service.js");
|
|
27
|
+
const sign_up_rules_1 = require("../users/sign-up-rules.js");
|
|
28
|
+
const users_service_1 = require("../users/users.service.js");
|
|
29
|
+
const social_providers_service_1 = require("./social-providers.service");
|
|
30
|
+
/**
|
|
31
|
+
* Sign in with Google, Apple or Facebook, and link / unlink them to an existing account.
|
|
32
|
+
*
|
|
33
|
+
* Who the token belongs to is decided like this:
|
|
34
|
+
* - The provider account is already linked: sign in as that user.
|
|
35
|
+
* - The provider PROVED the email, and a user has it: link to that user, then sign in.
|
|
36
|
+
* - The email is NOT proven, and a user has it: refuse (ACCOUNT_EXISTS_WITH_OTHER_METHOD).
|
|
37
|
+
* Otherwise anyone could create a Facebook account with your email and take over yours.
|
|
38
|
+
* - Nobody has the email: create a new account (if sign-up is open).
|
|
39
|
+
*/
|
|
40
|
+
let SocialAuthService = class SocialAuthService {
|
|
41
|
+
settingsService;
|
|
42
|
+
providers;
|
|
43
|
+
users;
|
|
44
|
+
signIn;
|
|
45
|
+
signInMethods;
|
|
46
|
+
rateLimiter;
|
|
47
|
+
events;
|
|
48
|
+
database;
|
|
49
|
+
constructor(options, settingsService, providers, users, signIn, signInMethods, rateLimiter, events) {
|
|
50
|
+
this.settingsService = settingsService;
|
|
51
|
+
this.providers = providers;
|
|
52
|
+
this.users = users;
|
|
53
|
+
this.signIn = signIn;
|
|
54
|
+
this.signInMethods = signInMethods;
|
|
55
|
+
this.rateLimiter = rateLimiter;
|
|
56
|
+
this.events = events;
|
|
57
|
+
this.database = options.dataSource;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Signs in (or signs up) with a token from Google, Apple or Facebook.
|
|
61
|
+
* See the class comment for how the token is matched to a user.
|
|
62
|
+
*
|
|
63
|
+
* @param provider - "google", "apple" or "facebook".
|
|
64
|
+
* @param input - The provider's token, the optional nonce, and sign-up details for new accounts.
|
|
65
|
+
* @param request - Who is asking.
|
|
66
|
+
* @returns Tokens, or a 2FA challenge.
|
|
67
|
+
* @throws AuthError INVALID_SOCIAL_TOKEN when the provider's token doesn't check out.
|
|
68
|
+
* @throws AuthError ACCOUNT_EXISTS_WITH_OTHER_METHOD when a user has this email but the provider didn't prove it.
|
|
69
|
+
* @throws AuthError SIGN_UP_CLOSED or VALIDATION_ERROR when a new account can't be created.
|
|
70
|
+
*/
|
|
71
|
+
async signInWithProvider(provider, input, request) {
|
|
72
|
+
const settings = await this.settingsService.getSettings();
|
|
73
|
+
await this.rateLimiter.consume(constants_1.RATE_LIMIT_KEY.signInPerIp(request.ipAddress), constants_1.RATE_LIMITS.signInPerIp);
|
|
74
|
+
const identity = await this.providers.verifyCredential(provider, { token: input.token, nonce: input.nonce }, settings);
|
|
75
|
+
// This provider account is already linked to a user.
|
|
76
|
+
const linkedAccount = await this.findLinkedAccount(identity);
|
|
77
|
+
if (linkedAccount) {
|
|
78
|
+
const linkedUser = await this.users.findUserById(linkedAccount.userId);
|
|
79
|
+
if (!linkedUser)
|
|
80
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.invalidSocialToken);
|
|
81
|
+
return this.signIn.completeSignIn(linkedUser, provider, request, settings);
|
|
82
|
+
}
|
|
83
|
+
// Someone already has this email: link only if the provider proved it.
|
|
84
|
+
const userWithSameEmail = identity.email ? await this.users.findUserByEmail(identity.email) : null;
|
|
85
|
+
if (userWithSameEmail) {
|
|
86
|
+
if (!identity.isEmailVerified)
|
|
87
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.accountExistsWithOtherMethod);
|
|
88
|
+
await this.users.removeUnprovenPassword(userWithSameEmail, request);
|
|
89
|
+
await this.saveLink(userWithSameEmail.id, identity, request);
|
|
90
|
+
const verifiedUser = await this.users.markEmailVerified(userWithSameEmail, settings, request);
|
|
91
|
+
return this.signIn.completeSignIn(verifiedUser, provider, request, settings);
|
|
92
|
+
}
|
|
93
|
+
// Nobody has this email yet.
|
|
94
|
+
const newUser = await this.createUserFromIdentity(identity, input, settings, request);
|
|
95
|
+
return this.signIn.completeSignIn(newUser, provider, request, settings);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Links a provider account to the signed-in user (e.g. "Connect Google" in settings).
|
|
99
|
+
* Linking the same account again is a no-op.
|
|
100
|
+
*
|
|
101
|
+
* @param userId - The signed-in user.
|
|
102
|
+
* @param provider - "google", "apple" or "facebook".
|
|
103
|
+
* @param input - The provider's token and optional nonce.
|
|
104
|
+
* @param request - Who is asking (for the audit log).
|
|
105
|
+
* @returns `{ success: true }`.
|
|
106
|
+
* @throws AuthError SOCIAL_ACCOUNT_IN_USE when that provider account belongs to another user.
|
|
107
|
+
*/
|
|
108
|
+
async linkProvider(userId, provider, input, request) {
|
|
109
|
+
const settings = await this.settingsService.getSettings();
|
|
110
|
+
const identity = await this.providers.verifyCredential(provider, { token: input.token, nonce: input.nonce }, settings);
|
|
111
|
+
await this.users.getUserOrThrow(userId);
|
|
112
|
+
const linkedAccount = await this.findLinkedAccount(identity);
|
|
113
|
+
if (linkedAccount) {
|
|
114
|
+
if (linkedAccount.userId === userId)
|
|
115
|
+
return { success: true };
|
|
116
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.socialAccountAlreadyLinked);
|
|
117
|
+
}
|
|
118
|
+
await this.saveLink(userId, identity, request);
|
|
119
|
+
return { success: true };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Removes a linked provider, unless it is the user's only way to sign in.
|
|
123
|
+
*
|
|
124
|
+
* @param userId - The signed-in user.
|
|
125
|
+
* @param provider - The provider to disconnect.
|
|
126
|
+
* @param request - Who is asking (for the audit log).
|
|
127
|
+
* @returns `{ success: true }`.
|
|
128
|
+
* @throws AuthError NOT_FOUND when the provider isn't linked.
|
|
129
|
+
* @throws AuthError LAST_SIGN_IN_METHOD when it is the user's only way to sign in.
|
|
130
|
+
*/
|
|
131
|
+
async unlinkProvider(userId, provider, request) {
|
|
132
|
+
const settings = await this.settingsService.getSettings();
|
|
133
|
+
const user = await this.users.getUserOrThrow(userId);
|
|
134
|
+
const accounts = this.database.getRepository(entities_1.SocialAccountEntity);
|
|
135
|
+
if (!(await accounts.exists({ where: { userId, provider } })))
|
|
136
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.notFound);
|
|
137
|
+
await this.signInMethods.assertCanRemove(user, settings, constants_1.REMOVABLE_SIGN_IN_METHOD.social);
|
|
138
|
+
await accounts.delete({ userId, provider });
|
|
139
|
+
await this.events.record({ name: constants_1.AUTH_EVENTS.socialAccountUnlinked, userId, request, details: { provider } });
|
|
140
|
+
return { success: true };
|
|
141
|
+
}
|
|
142
|
+
// -------------------------------------------------------------------------------------------
|
|
143
|
+
/**
|
|
144
|
+
* @param identity - Who the provider says the user is.
|
|
145
|
+
* @returns The link row for this provider account, or null.
|
|
146
|
+
*/
|
|
147
|
+
findLinkedAccount(identity) {
|
|
148
|
+
return this.database.getRepository(entities_1.SocialAccountEntity).findOne({
|
|
149
|
+
where: { provider: identity.provider, providerUserId: identity.providerUserId },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Links a provider account to a user.
|
|
154
|
+
*
|
|
155
|
+
* @param userId - The user.
|
|
156
|
+
* @param identity - Who the provider says the user is.
|
|
157
|
+
* @param request - Who is asking (for the audit log).
|
|
158
|
+
* @param transaction - Pass one to save the link together with other rows (e.g. a new user).
|
|
159
|
+
* @throws AuthError SOCIAL_ACCOUNT_IN_USE when two requests link the same provider account at once.
|
|
160
|
+
*/
|
|
161
|
+
async saveLink(userId, identity, request, transaction = this.database.manager) {
|
|
162
|
+
try {
|
|
163
|
+
await transaction.insert(entities_1.SocialAccountEntity, {
|
|
164
|
+
userId,
|
|
165
|
+
provider: identity.provider,
|
|
166
|
+
providerUserId: identity.providerUserId,
|
|
167
|
+
email: identity.email,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
// Two requests linked the same provider account at once.
|
|
172
|
+
const code = error.code;
|
|
173
|
+
if (error instanceof typeorm_1.QueryFailedError && code === constants_1.POSTGRES_UNIQUE_VIOLATION_CODE)
|
|
174
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.socialAccountAlreadyLinked);
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
await this.events.record({ name: constants_1.AUTH_EVENTS.socialAccountLinked, userId, request, details: { provider: identity.provider } });
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Creates a new account from a provider's identity, linked to that provider.
|
|
181
|
+
* Names from the app (Apple sends them only to the app) win over names from the provider.
|
|
182
|
+
*
|
|
183
|
+
* @param identity - Who the provider says the user is.
|
|
184
|
+
* @param input - Sign-up details sent by the app.
|
|
185
|
+
* @param settings - The current settings.
|
|
186
|
+
* @param request - Who is asking.
|
|
187
|
+
* @returns The new user.
|
|
188
|
+
* @throws AuthError SIGN_UP_CLOSED, EMAIL_DOMAIN_NOT_ALLOWED, or VALIDATION_ERROR (e.g. no email from Facebook).
|
|
189
|
+
*/
|
|
190
|
+
async createUserFromIdentity(identity, input, settings, request) {
|
|
191
|
+
if (!settings.registration.enabled)
|
|
192
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.signUpClosed);
|
|
193
|
+
// Every account needs an email or phone. Facebook users can refuse to share their email.
|
|
194
|
+
if (!identity.email)
|
|
195
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.validationFailed, { missingSignUpDetails: [constants_1.SIGN_UP_DETAIL.email] });
|
|
196
|
+
if (!(0, identifiers_1.isEmailDomainAllowed)(identity.email, settings.registration.allowedEmailDomains)) {
|
|
197
|
+
throw new auth_error_1.AuthError(constants_1.AUTH_ERRORS.emailDomainNotAllowed);
|
|
198
|
+
}
|
|
199
|
+
// Names from the app (Apple sends them only to the app) win over names from the provider.
|
|
200
|
+
const signUpDetails = {
|
|
201
|
+
...input,
|
|
202
|
+
firstName: input.firstName?.trim() || identity.firstName || undefined,
|
|
203
|
+
lastName: input.lastName?.trim() || identity.lastName || undefined,
|
|
204
|
+
};
|
|
205
|
+
(0, sign_up_rules_1.assertSignUpDetailsComplete)(signUpDetails, settings);
|
|
206
|
+
return this.database.transaction(async (transaction) => {
|
|
207
|
+
const newUser = await this.users.createUser({
|
|
208
|
+
email: identity.email,
|
|
209
|
+
isEmailVerified: identity.isEmailVerified,
|
|
210
|
+
firstName: signUpDetails.firstName ?? null,
|
|
211
|
+
lastName: signUpDetails.lastName ?? null,
|
|
212
|
+
acceptedTerms: input.acceptTerms === true,
|
|
213
|
+
acceptedPrivacyPolicy: input.acceptPrivacyPolicy === true,
|
|
214
|
+
}, settings, request, transaction);
|
|
215
|
+
await this.saveLink(newUser.id, identity, request, transaction);
|
|
216
|
+
return newUser;
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
exports.SocialAuthService = SocialAuthService;
|
|
221
|
+
exports.SocialAuthService = SocialAuthService = __decorate([
|
|
222
|
+
(0, common_1.Injectable)(),
|
|
223
|
+
__param(0, (0, common_1.Inject)(constants_1.AUTH_OPTIONS_TOKEN)),
|
|
224
|
+
__metadata("design:paramtypes", [Object, settings_service_1.SettingsService,
|
|
225
|
+
social_providers_service_1.SocialProvidersService,
|
|
226
|
+
users_service_1.UsersService,
|
|
227
|
+
sign_in_service_1.SignInService,
|
|
228
|
+
sign_in_methods_service_1.SignInMethodsService,
|
|
229
|
+
rate_limiter_service_1.RateLimiterService,
|
|
230
|
+
auth_events_service_1.AuthEventsService])
|
|
231
|
+
], SocialAuthService);
|
|
232
|
+
//# sourceMappingURL=social-auth.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"social-auth.service.js","sourceRoot":"","sources":["../../../src/features/social/social-auth.service.ts"],"names":[],"mappings":"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"}
|