@3sixty/api-auth-entity-storage-service 0.0.0-stage → 0.20.0-next.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.
Files changed (133) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +24 -2
  3. package/dist/es/entities/authenticationAuditEntry.js +121 -0
  4. package/dist/es/entities/authenticationAuditEntry.js.map +1 -0
  5. package/dist/es/entities/authenticationRateEntry.js +37 -0
  6. package/dist/es/entities/authenticationRateEntry.js.map +1 -0
  7. package/dist/es/entities/authenticationUser.js +69 -0
  8. package/dist/es/entities/authenticationUser.js.map +1 -0
  9. package/dist/es/index.js +29 -0
  10. package/dist/es/index.js.map +1 -0
  11. package/dist/es/models/IAuthHeaderProcessorConfig.js +4 -0
  12. package/dist/es/models/IAuthHeaderProcessorConfig.js.map +1 -0
  13. package/dist/es/models/IAuthHeaderProcessorConstructorOptions.js +2 -0
  14. package/dist/es/models/IAuthHeaderProcessorConstructorOptions.js.map +1 -0
  15. package/dist/es/models/IAuthTokenContext.js +4 -0
  16. package/dist/es/models/IAuthTokenContext.js.map +1 -0
  17. package/dist/es/models/IEntityStorageAuthenticationAdminServiceConfig.js +4 -0
  18. package/dist/es/models/IEntityStorageAuthenticationAdminServiceConfig.js.map +1 -0
  19. package/dist/es/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js +2 -0
  20. package/dist/es/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js.map +1 -0
  21. package/dist/es/models/IEntityStorageAuthenticationAuditServiceConfig.js +4 -0
  22. package/dist/es/models/IEntityStorageAuthenticationAuditServiceConfig.js.map +1 -0
  23. package/dist/es/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js +2 -0
  24. package/dist/es/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js.map +1 -0
  25. package/dist/es/models/IEntityStorageAuthenticationRateServiceConfig.js +2 -0
  26. package/dist/es/models/IEntityStorageAuthenticationRateServiceConfig.js.map +1 -0
  27. package/dist/es/models/IEntityStorageAuthenticationRateServiceConstructorOptions.js +2 -0
  28. package/dist/es/models/IEntityStorageAuthenticationRateServiceConstructorOptions.js.map +1 -0
  29. package/dist/es/models/IEntityStorageAuthenticationServiceConfig.js +2 -0
  30. package/dist/es/models/IEntityStorageAuthenticationServiceConfig.js.map +1 -0
  31. package/dist/es/models/IEntityStorageAuthenticationServiceConstructorOptions.js +2 -0
  32. package/dist/es/models/IEntityStorageAuthenticationServiceConstructorOptions.js.map +1 -0
  33. package/dist/es/processors/authHeaderProcessor.js +277 -0
  34. package/dist/es/processors/authHeaderProcessor.js.map +1 -0
  35. package/dist/es/restEntryPoints.js +27 -0
  36. package/dist/es/restEntryPoints.js.map +1 -0
  37. package/dist/es/routes/entityStorageAuthenticationAdminRoutes.js +367 -0
  38. package/dist/es/routes/entityStorageAuthenticationAdminRoutes.js.map +1 -0
  39. package/dist/es/routes/entityStorageAuthenticationAuditRoutes.js +343 -0
  40. package/dist/es/routes/entityStorageAuthenticationAuditRoutes.js.map +1 -0
  41. package/dist/es/routes/entityStorageAuthenticationRoutes.js +247 -0
  42. package/dist/es/routes/entityStorageAuthenticationRoutes.js.map +1 -0
  43. package/dist/es/schema.js +15 -0
  44. package/dist/es/schema.js.map +1 -0
  45. package/dist/es/services/entityStorageAuthenticationAdminService.js +323 -0
  46. package/dist/es/services/entityStorageAuthenticationAdminService.js.map +1 -0
  47. package/dist/es/services/entityStorageAuthenticationAuditService.js +240 -0
  48. package/dist/es/services/entityStorageAuthenticationAuditService.js.map +1 -0
  49. package/dist/es/services/entityStorageAuthenticationRateService.js +213 -0
  50. package/dist/es/services/entityStorageAuthenticationRateService.js.map +1 -0
  51. package/dist/es/services/entityStorageAuthenticationService.js +326 -0
  52. package/dist/es/services/entityStorageAuthenticationService.js.map +1 -0
  53. package/dist/es/utils/passwordHelper.js +58 -0
  54. package/dist/es/utils/passwordHelper.js.map +1 -0
  55. package/dist/es/utils/tokenHelper.js +142 -0
  56. package/dist/es/utils/tokenHelper.js.map +1 -0
  57. package/dist/types/entities/authenticationAuditEntry.d.ts +49 -0
  58. package/dist/types/entities/authenticationRateEntry.d.ts +17 -0
  59. package/dist/types/entities/authenticationUser.d.ts +33 -0
  60. package/dist/types/index.d.ts +26 -0
  61. package/dist/types/models/IAuthHeaderProcessorConfig.d.ts +39 -0
  62. package/dist/types/models/IAuthHeaderProcessorConstructorOptions.d.ts +25 -0
  63. package/dist/types/models/IAuthTokenContext.d.ts +34 -0
  64. package/dist/types/models/IEntityStorageAuthenticationAdminServiceConfig.d.ts +14 -0
  65. package/dist/types/models/IEntityStorageAuthenticationAdminServiceConstructorOptions.d.ts +20 -0
  66. package/dist/types/models/IEntityStorageAuthenticationAuditServiceConfig.d.ts +9 -0
  67. package/dist/types/models/IEntityStorageAuthenticationAuditServiceConstructorOptions.d.ts +15 -0
  68. package/dist/types/models/IEntityStorageAuthenticationRateServiceConfig.d.ts +10 -0
  69. package/dist/types/models/IEntityStorageAuthenticationRateServiceConstructorOptions.d.ts +25 -0
  70. package/dist/types/models/IEntityStorageAuthenticationServiceConfig.d.ts +36 -0
  71. package/dist/types/models/IEntityStorageAuthenticationServiceConstructorOptions.d.ts +35 -0
  72. package/dist/types/processors/authHeaderProcessor.d.ts +57 -0
  73. package/dist/types/restEntryPoints.d.ts +5 -0
  74. package/dist/types/routes/entityStorageAuthenticationAdminRoutes.d.ts +62 -0
  75. package/dist/types/routes/entityStorageAuthenticationAuditRoutes.d.ts +54 -0
  76. package/dist/types/routes/entityStorageAuthenticationRoutes.d.ts +45 -0
  77. package/dist/types/schema.d.ts +4 -0
  78. package/dist/types/services/entityStorageAuthenticationAdminService.d.ts +65 -0
  79. package/dist/types/services/entityStorageAuthenticationAuditService.d.ts +72 -0
  80. package/dist/types/services/entityStorageAuthenticationRateService.d.ts +60 -0
  81. package/dist/types/services/entityStorageAuthenticationService.d.ts +65 -0
  82. package/dist/types/utils/passwordHelper.d.ts +24 -0
  83. package/dist/types/utils/tokenHelper.d.ts +60 -0
  84. package/docs/changelog.md +0 -0
  85. package/docs/examples.md +178 -0
  86. package/docs/open-api/spec.json +2122 -0
  87. package/docs/reference/classes/AuthHeaderProcessor.md +193 -0
  88. package/docs/reference/classes/AuthenticationAuditEntry.md +101 -0
  89. package/docs/reference/classes/AuthenticationRateEntry.md +37 -0
  90. package/docs/reference/classes/AuthenticationUser.md +69 -0
  91. package/docs/reference/classes/EntityStorageAuthenticationAdminService.md +229 -0
  92. package/docs/reference/classes/EntityStorageAuthenticationAuditService.md +241 -0
  93. package/docs/reference/classes/EntityStorageAuthenticationRateService.md +227 -0
  94. package/docs/reference/classes/EntityStorageAuthenticationService.md +221 -0
  95. package/docs/reference/classes/PasswordHelper.md +74 -0
  96. package/docs/reference/classes/TokenHelper.md +205 -0
  97. package/docs/reference/functions/authenticationAdminCreateUser.md +37 -0
  98. package/docs/reference/functions/authenticationAdminGetUser.md +31 -0
  99. package/docs/reference/functions/authenticationAdminGetUserByIdentity.md +31 -0
  100. package/docs/reference/functions/authenticationAdminRemoveUser.md +31 -0
  101. package/docs/reference/functions/authenticationAdminUpdateUser.md +31 -0
  102. package/docs/reference/functions/authenticationAdminUpdateUserPassword.md +31 -0
  103. package/docs/reference/functions/authenticationAuditCreate.md +37 -0
  104. package/docs/reference/functions/authenticationAuditGet.md +31 -0
  105. package/docs/reference/functions/authenticationAuditQuery.md +31 -0
  106. package/docs/reference/functions/authenticationAuditRemove.md +31 -0
  107. package/docs/reference/functions/authenticationAuditUpdate.md +31 -0
  108. package/docs/reference/functions/authenticationLogin.md +31 -0
  109. package/docs/reference/functions/authenticationLogout.md +31 -0
  110. package/docs/reference/functions/authenticationRefreshToken.md +31 -0
  111. package/docs/reference/functions/authenticationUpdatePassword.md +31 -0
  112. package/docs/reference/functions/generateRestRoutesAuthentication.md +25 -0
  113. package/docs/reference/functions/generateRestRoutesAuthenticationAdmin.md +25 -0
  114. package/docs/reference/functions/generateRestRoutesAuthenticationAudit.md +25 -0
  115. package/docs/reference/functions/initSchema.md +9 -0
  116. package/docs/reference/index.md +57 -0
  117. package/docs/reference/interfaces/IAuthHeaderProcessorConfig.md +86 -0
  118. package/docs/reference/interfaces/IAuthHeaderProcessorConstructorOptions.md +53 -0
  119. package/docs/reference/interfaces/IAuthTokenContext.md +60 -0
  120. package/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConfig.md +25 -0
  121. package/docs/reference/interfaces/IEntityStorageAuthenticationAdminServiceConstructorOptions.md +39 -0
  122. package/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConfig.md +11 -0
  123. package/docs/reference/interfaces/IEntityStorageAuthenticationAuditServiceConstructorOptions.md +25 -0
  124. package/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConfig.md +17 -0
  125. package/docs/reference/interfaces/IEntityStorageAuthenticationRateServiceConstructorOptions.md +53 -0
  126. package/docs/reference/interfaces/IEntityStorageAuthenticationServiceConfig.md +87 -0
  127. package/docs/reference/interfaces/IEntityStorageAuthenticationServiceConstructorOptions.md +81 -0
  128. package/docs/reference/variables/restEntryPoints.md +5 -0
  129. package/docs/reference/variables/tagsAuthentication.md +5 -0
  130. package/docs/reference/variables/tagsAuthenticationAdmin.md +5 -0
  131. package/docs/reference/variables/tagsAuthenticationAudit.md +5 -0
  132. package/locales/en.json +52 -0
  133. package/package.json +64 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entityStorageAuthenticationService.js","sourceRoot":"","sources":["../../../src/services/entityStorageAuthenticationService.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type {\n\tIAuthenticationRateActionConfig,\n\tIAuthenticationRateComponent,\n\tIAuthenticationAuditComponent,\n\tIAuthenticationComponent\n} from \"@3sixty/api-auth-entity-storage-models\";\nimport { AuthAuditEvent } from \"@3sixty/api-auth-entity-storage-models\";\nimport { type ITenantAdminComponent, ScopeHelper } from \"@3sixty/api-models\";\nimport { ContextIdHelper, ContextIdKeys, ContextIdStore } from \"@3sixty/context\";\nimport {\n\tCoerce,\n\tComponentFactory,\n\tConverter,\n\tGeneralError,\n\tGuards,\n\tIs,\n\tNotFoundError,\n\tUnauthorizedError\n} from \"@3sixty/core\";\nimport { PasswordGenerator, PasswordValidator } from \"@3sixty/crypto\";\nimport {\n\tEntityStorageConnectorFactory,\n\ttype IEntityStorageConnector\n} from \"@3sixty/entity-storage-models\";\nimport { nameof } from \"@3sixty/nameof\";\nimport { VaultConnectorFactory, type IVaultConnector } from \"@3sixty/vault-models\";\nimport type { AuthenticationUser } from \"../entities/authenticationUser.js\";\nimport type { IEntityStorageAuthenticationServiceConstructorOptions } from \"../models/IEntityStorageAuthenticationServiceConstructorOptions.js\";\nimport { PasswordHelper } from \"../utils/passwordHelper.js\";\nimport { TokenHelper } from \"../utils/tokenHelper.js\";\n\n/**\n * Implementation of the authentication component using entity storage.\n */\nexport class EntityStorageAuthenticationService implements IAuthenticationComponent {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntityStorageAuthenticationService>();\n\n\t/**\n\t * Default TTL in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TTL_MINUTES: number = 60;\n\n\t/**\n\t * Default maximum login attempts in a rate window.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LOGIN_RATE_MAX_ATTEMPTS: number = 5;\n\n\t/**\n\t * Default login rate window in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_LOGIN_RATE_WINDOW_MINUTES: number = 15;\n\n\t/**\n\t * Default maximum password change attempts in a rate window.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_PASSWORD_CHANGE_RATE_MAX_ATTEMPTS: number = 5;\n\n\t/**\n\t * Default password change rate window in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_PASSWORD_CHANGE_RATE_WINDOW_MINUTES: number = 15;\n\n\t/**\n\t * Default maximum token refresh attempts in a rate window.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TOKEN_REFRESH_RATE_MAX_ATTEMPTS: number = 30;\n\n\t/**\n\t * Default token refresh rate window in minutes.\n\t * @internal\n\t */\n\tprivate static readonly _DEFAULT_TOKEN_REFRESH_RATE_WINDOW_MINUTES: number = 60;\n\n\t/**\n\t * The audit service.\n\t * @internal\n\t */\n\tprivate readonly _authenticationAuditService?: IAuthenticationAuditComponent;\n\n\t/**\n\t * The rate service.\n\t * @internal\n\t */\n\tprivate readonly _authenticationRateService: IAuthenticationRateComponent;\n\n\t/**\n\t * The entity storage for users.\n\t * @internal\n\t */\n\tprivate readonly _userEntityStorage: IEntityStorageConnector<AuthenticationUser>;\n\n\t/**\n\t * The vault for the keys.\n\t * @internal\n\t */\n\tprivate readonly _vaultConnector: IVaultConnector;\n\n\t/**\n\t * The name of the key to retrieve from the vault for signing JWT.\n\t * @internal\n\t */\n\tprivate readonly _signingKeyName: string;\n\n\t/**\n\t * The default TTL for the token.\n\t * @internal\n\t */\n\tprivate readonly _defaultTtlMinutes: number;\n\n\t/**\n\t * The minimum password length for validation.\n\t * @internal\n\t */\n\tprivate readonly _minPasswordLength?: number;\n\n\t/**\n\t * Rate limit configuration for login failures.\n\t * @internal\n\t */\n\tprivate readonly _loginRateLimit: IAuthenticationRateActionConfig;\n\n\t/**\n\t * Rate limit configuration for password changes.\n\t * @internal\n\t */\n\tprivate readonly _passwordChangeRateLimit: IAuthenticationRateActionConfig;\n\n\t/**\n\t * Rate limit configuration for token refresh.\n\t * @internal\n\t */\n\tprivate readonly _tokenRefreshRateLimit: IAuthenticationRateActionConfig;\n\n\t/**\n\t * The component to retrieve tenant information.\n\t * @internal\n\t */\n\tprivate readonly _tenantAdminComponent?: ITenantAdminComponent;\n\n\t/**\n\t * The node identity.\n\t * @internal\n\t */\n\tprivate _nodeId?: string;\n\n\t/**\n\t * Create a new instance of EntityStorageAuthentication.\n\t * @param options The dependencies for the identity connector.\n\t */\n\tconstructor(options?: IEntityStorageAuthenticationServiceConstructorOptions) {\n\t\tthis._userEntityStorage = EntityStorageConnectorFactory.get(\n\t\t\toptions?.userEntityStorageType ?? \"authentication-user\"\n\t\t);\n\n\t\tthis._vaultConnector = VaultConnectorFactory.get(options?.vaultConnectorType ?? \"vault\");\n\n\t\tthis._authenticationAuditService = ComponentFactory.getIfExists<IAuthenticationAuditComponent>(\n\t\t\toptions?.authenticationAuditServiceType ?? \"authentication-audit\"\n\t\t);\n\n\t\tthis._authenticationRateService = ComponentFactory.get<IAuthenticationRateComponent>(\n\t\t\toptions?.authenticationRateServiceType ?? \"authentication-rate\"\n\t\t);\n\n\t\tthis._tenantAdminComponent = ComponentFactory.getIfExists<ITenantAdminComponent>(\n\t\t\toptions?.tenantAdminComponentType ?? \"tenant-admin\"\n\t\t);\n\n\t\tthis._signingKeyName = options?.config?.signingKeyName ?? \"auth-signing\";\n\t\tthis._defaultTtlMinutes =\n\t\t\toptions?.config?.defaultTtlMinutes ?? EntityStorageAuthenticationService._DEFAULT_TTL_MINUTES;\n\t\tthis._minPasswordLength = options?.config?.minPasswordLength;\n\t\tthis._loginRateLimit = {\n\t\t\tmaxAttempts:\n\t\t\t\toptions?.config?.loginRateLimit?.maxAttempts ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_LOGIN_RATE_MAX_ATTEMPTS,\n\t\t\twindowMinutes:\n\t\t\t\toptions?.config?.loginRateLimit?.windowMinutes ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_LOGIN_RATE_WINDOW_MINUTES\n\t\t};\n\t\tthis._passwordChangeRateLimit = {\n\t\t\tmaxAttempts:\n\t\t\t\toptions?.config?.passwordChangeRateLimit?.maxAttempts ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_PASSWORD_CHANGE_RATE_MAX_ATTEMPTS,\n\t\t\twindowMinutes:\n\t\t\t\toptions?.config?.passwordChangeRateLimit?.windowMinutes ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_PASSWORD_CHANGE_RATE_WINDOW_MINUTES\n\t\t};\n\t\tthis._tokenRefreshRateLimit = {\n\t\t\tmaxAttempts:\n\t\t\t\toptions?.config?.tokenRefreshRateLimit?.maxAttempts ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_TOKEN_REFRESH_RATE_MAX_ATTEMPTS,\n\t\t\twindowMinutes:\n\t\t\t\toptions?.config?.tokenRefreshRateLimit?.windowMinutes ??\n\t\t\t\tEntityStorageAuthenticationService._DEFAULT_TOKEN_REFRESH_RATE_WINDOW_MINUTES\n\t\t};\n\t}\n\n\t/**\n\t * Returns the class name of the component.\n\t * @returns The class name of the component.\n\t */\n\tpublic className(): string {\n\t\treturn EntityStorageAuthenticationService.CLASS_NAME;\n\t}\n\n\t/**\n\t * The service needs to be started when the application is initialized.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns A promise that resolves when rate-limit actions have been registered and the node identity cached.\n\t */\n\tpublic async start(nodeLoggingComponentType?: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.Node);\n\t\tthis._nodeId = contextIds[ContextIdKeys.Node];\n\n\t\tawait this._authenticationRateService.registerAction(\"login\", this._loginRateLimit);\n\t\tawait this._authenticationRateService.registerAction(\n\t\t\t\"password-change\",\n\t\t\tthis._passwordChangeRateLimit\n\t\t);\n\t\tawait this._authenticationRateService.registerAction(\n\t\t\t\"token-refresh\",\n\t\t\tthis._tokenRefreshRateLimit\n\t\t);\n\t}\n\n\t/**\n\t * The component needs to be stopped when the node is closed.\n\t * @param nodeLoggingComponentType The node logging component type.\n\t * @returns A promise that resolves when all rate-limit actions have been unregistered.\n\t */\n\tpublic async stop(nodeLoggingComponentType?: string): Promise<void> {\n\t\tawait this._authenticationRateService.unregisterAction(\"login\");\n\t\tawait this._authenticationRateService.unregisterAction(\"password-change\");\n\t\tawait this._authenticationRateService.unregisterAction(\"token-refresh\");\n\t}\n\n\t/**\n\t * Perform a login for the user.\n\t * @param email The email address for the user.\n\t * @param password The password for the user.\n\t * @returns The authentication token for the user, if it uses a mechanism with public access.\n\t */\n\tpublic async login(\n\t\temail: string,\n\t\tpassword: string\n\t): Promise<{\n\t\ttoken?: string;\n\t\texpiry: number;\n\t}> {\n\t\tGuards.stringValue(EntityStorageAuthenticationService.CLASS_NAME, nameof(email), email);\n\t\tGuards.stringValue(EntityStorageAuthenticationService.CLASS_NAME, nameof(password), password);\n\n\t\tif (!Is.stringValue(this._nodeId)) {\n\t\t\tthrow new GeneralError(EntityStorageAuthenticationService.CLASS_NAME, \"nodeIdNotSet\");\n\t\t}\n\n\t\tlet loginUser: AuthenticationUser | undefined;\n\t\tlet loginTenantId: string | undefined;\n\t\tlet tokenAndExpiry: { token?: string; expiry: number } | undefined;\n\n\t\ttry {\n\t\t\tawait this._authenticationRateService.check(\"login\", email);\n\n\t\t\tconst user = await this._userEntityStorage.get(email);\n\t\t\tif (!user) {\n\t\t\t\tthrow new GeneralError(EntityStorageAuthenticationService.CLASS_NAME, \"userNotFound\");\n\t\t\t}\n\n\t\t\tconst saltBytes = Converter.base64ToBytes(user.salt);\n\t\t\tconst passwordBytes = Converter.utf8ToBytes(password);\n\n\t\t\tconst hashedPassword = await PasswordGenerator.hashPassword(passwordBytes, saltBytes);\n\n\t\t\tif (!PasswordValidator.comparePasswordHashes(hashedPassword, user.password)) {\n\t\t\t\tthrow new GeneralError(EntityStorageAuthenticationService.CLASS_NAME, \"passwordMismatch\");\n\t\t\t}\n\n\t\t\t// This might be undefined if the login is performed in a single tenant context\n\t\t\t// it is verified during the token processing, tenant id will be matched against\n\t\t\t// the context\n\t\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\t\tloginTenantId = contextIds?.[ContextIdKeys.Tenant];\n\n\t\t\ttokenAndExpiry = await TokenHelper.createToken(\n\t\t\t\tthis._vaultConnector,\n\t\t\t\tthis._nodeId,\n\t\t\t\tthis._signingKeyName,\n\t\t\t\tuser.identity,\n\t\t\t\tuser.organization,\n\t\t\t\tloginTenantId,\n\t\t\t\tthis._defaultTtlMinutes,\n\t\t\t\tuser.scope,\n\t\t\t\tuser.passwordVersion ?? 0\n\t\t\t);\n\t\t\tloginUser = user;\n\t\t} catch (error) {\n\t\t\tawait this._authenticationAuditService?.create({\n\t\t\t\tactorId: email,\n\t\t\t\tevent: AuthAuditEvent.LoginFailure\n\t\t\t});\n\n\t\t\tthrow new UnauthorizedError(\n\t\t\t\tEntityStorageAuthenticationService.CLASS_NAME,\n\t\t\t\t\"loginFailed\",\n\t\t\t\tundefined,\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\n\t\tawait this._authenticationRateService.clear(\"login\", email);\n\n\t\tawait this._authenticationAuditService?.create({\n\t\t\tactorId: email,\n\t\t\tevent: AuthAuditEvent.LoginSuccess,\n\t\t\tdata: {\n\t\t\t\tuserIdentity: loginUser.identity,\n\t\t\t\torganizationIdentity: loginUser.organization,\n\t\t\t\ttenantId: loginTenantId,\n\t\t\t\tscope: ScopeHelper.toArray(loginUser.scope)\n\t\t\t}\n\t\t});\n\n\t\treturn tokenAndExpiry;\n\t}\n\n\t/**\n\t * Logout the current user.\n\t * @param token The token to logout, if it uses a mechanism with public access.\n\t * @returns A promise that resolves when the logout audit entry has been recorded.\n\t */\n\tpublic async logout(token?: string): Promise<void> {\n\t\t// Nothing to do here, as we are stateless.\n\t\t// The cookie will be revoked by the REST route handling\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tconst identifier = contextIds?.[ContextIdKeys.User];\n\t\tif (Is.stringValue(identifier)) {\n\t\t\tawait this._authenticationAuditService?.create({\n\t\t\t\tactorId: identifier,\n\t\t\t\tevent: AuthAuditEvent.Logout\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Refresh the token.\n\t * @param token The token to refresh, if it uses a mechanism with public access.\n\t * @returns The refreshed token, if it uses a mechanism with public access.\n\t */\n\tpublic async refresh(token?: string): Promise<{\n\t\ttoken?: string;\n\t\texpiry: number;\n\t}> {\n\t\tif (!Is.stringValue(this._nodeId)) {\n\t\t\tthrow new GeneralError(EntityStorageAuthenticationService.CLASS_NAME, \"nodeIdNotSet\");\n\t\t}\n\n\t\tlet refreshPasswordVersion: number | undefined;\n\t\tlet refreshScope: string | undefined;\n\t\tlet tenantId: string | undefined;\n\n\t\t// If the verify fails on the current token then it will throw an exception.\n\t\tconst headerAndPayload = await TokenHelper.verify(\n\t\t\tthis._vaultConnector,\n\t\t\tthis._nodeId,\n\t\t\tthis._signingKeyName,\n\t\t\ttoken,\n\t\t\tundefined,\n\t\t\tasync (sub, org, tid, passwordVersion) => {\n\t\t\t\tconst validParts = [];\n\n\t\t\t\ttenantId = tid;\n\n\t\t\t\tif (Is.stringValue(tenantId)) {\n\t\t\t\t\tconst tenant = await this._tenantAdminComponent?.get(tenantId);\n\t\t\t\t\tif (tenant?.id === tenantId) {\n\t\t\t\t\t\tvalidParts.push(\"tenant\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// We use the tenant id from the token, if the user is not in that\n\t\t\t\t// partition then the get will fail. Only override the tenant key when\n\t\t\t\t// tid is present - setting it to undefined would drop the active partition.\n\t\t\t\tconst baseContext = (await ContextIdStore.getContextIds()) ?? {};\n\t\t\t\tconst contextIdsForUserLookup = Is.stringValue(tid)\n\t\t\t\t\t? { ...baseContext, [ContextIdKeys.Tenant]: tid }\n\t\t\t\t\t: baseContext;\n\n\t\t\t\t// Wrap the user lookup in the request context so partitioned storage uses the correct tenant.\n\t\t\t\tconst user = await ContextIdStore.run(contextIdsForUserLookup, async () =>\n\t\t\t\t\tthis._userEntityStorage.get(sub, \"identity\")\n\t\t\t\t);\n\n\t\t\t\trefreshPasswordVersion = user?.passwordVersion;\n\t\t\t\t// The scope is read from storage rather than copied from the token being refreshed,\n\t\t\t\t// so a scope change reaches a client that keeps refreshing.\n\t\t\t\trefreshScope = user?.scope;\n\t\t\t\tif (user?.identity === sub && (passwordVersion ?? 0) === (refreshPasswordVersion ?? 0)) {\n\t\t\t\t\tvalidParts.push(\"user\");\n\t\t\t\t}\n\t\t\t\tif (user?.organization === org) {\n\t\t\t\t\tvalidParts.push(\"organization\");\n\t\t\t\t}\n\t\t\t\treturn validParts;\n\t\t\t}\n\t\t);\n\n\t\tconst refreshSub = headerAndPayload.payload.sub ?? \"\";\n\t\tawait this._authenticationRateService.check(\"token-refresh\", refreshSub);\n\n\t\tconst payloadOrg = Coerce.string(headerAndPayload.payload.org);\n\n\t\tconst refreshTokenAndExpiry = await TokenHelper.createToken(\n\t\t\tthis._vaultConnector,\n\t\t\tthis._nodeId,\n\t\t\tthis._signingKeyName,\n\t\t\trefreshSub,\n\t\t\tpayloadOrg,\n\t\t\ttenantId,\n\t\t\tthis._defaultTtlMinutes,\n\t\t\trefreshScope,\n\t\t\trefreshPasswordVersion ?? 0\n\t\t);\n\n\t\tawait this._authenticationAuditService?.create({\n\t\t\tactorId: refreshSub,\n\t\t\tevent: AuthAuditEvent.TokenRefreshed,\n\t\t\tdata: {\n\t\t\t\torganizationIdentity: payloadOrg,\n\t\t\t\ttenantId,\n\t\t\t\tscope: ScopeHelper.toArray(refreshScope),\n\t\t\t\tversion: refreshPasswordVersion ?? 0\n\t\t\t}\n\t\t});\n\n\t\treturn refreshTokenAndExpiry;\n\t}\n\n\t/**\n\t * Update the user's password.\n\t * @param currentPassword The current password for the user.\n\t * @param newPassword The new password for the user.\n\t * @returns A promise that resolves when the password has been updated and the rate limit cleared.\n\t */\n\tpublic async updatePassword(currentPassword: string, newPassword: string): Promise<void> {\n\t\tconst contextIds = await ContextIdStore.getContextIds();\n\t\tContextIdHelper.guard(contextIds, ContextIdKeys.User);\n\n\t\tconst userIdentity = contextIds[ContextIdKeys.User];\n\t\tawait this._authenticationRateService.check(\"password-change\", userIdentity);\n\n\t\tconst user = await this._userEntityStorage.get(userIdentity, \"identity\");\n\t\tif (!Is.object<AuthenticationUser>(user)) {\n\t\t\tthrow new NotFoundError(\n\t\t\t\tEntityStorageAuthenticationService.CLASS_NAME,\n\t\t\t\t\"userNotFound\",\n\t\t\t\tuserIdentity\n\t\t\t);\n\t\t}\n\n\t\tawait PasswordHelper.updatePassword(\n\t\t\tthis._userEntityStorage,\n\t\t\tthis._authenticationAuditService,\n\t\t\tuser,\n\t\t\tnewPassword,\n\t\t\tcurrentPassword,\n\t\t\tthis._minPasswordLength\n\t\t);\n\n\t\tawait this._authenticationRateService.clear(\"password-change\", userIdentity);\n\t}\n}\n"]}
@@ -0,0 +1,58 @@
1
+ import { AuthAuditEvent } from "@3sixty/api-auth-entity-storage-models";
2
+ import { Converter, GeneralError, Is, RandomHelper } from "@3sixty/core";
3
+ import { PasswordGenerator, PasswordValidator } from "@3sixty/crypto";
4
+ /**
5
+ * Helper class for password operations.
6
+ */
7
+ export class PasswordHelper {
8
+ /**
9
+ * Runtime name for the class.
10
+ */
11
+ static CLASS_NAME = "PasswordHelper";
12
+ /**
13
+ * Update the password for a user.
14
+ * Validates password strength, verifies the current password if provided, then hashes and stores the new password and raises an audit event.
15
+ * @param userEntityStorage The entity storage for users.
16
+ * @param authenticationAuditService The optional audit service.
17
+ * @param user The user whose password is being updated.
18
+ * @param newPassword The new password to set.
19
+ * @param currentPassword The current password to verify against, if supplied.
20
+ * @param minPasswordLength Optional minimum password length for validation.
21
+ * @returns A promise that resolves when the new password has been stored and the audit entry recorded.
22
+ */
23
+ static async updatePassword(userEntityStorage, authenticationAuditService, user, newPassword, currentPassword, minPasswordLength) {
24
+ PasswordValidator.validatePassword(newPassword, {
25
+ minLength: minPasswordLength
26
+ });
27
+ if (Is.stringValue(currentPassword)) {
28
+ const saltBytes = Converter.base64ToBytes(user.salt);
29
+ const passwordBytes = Converter.utf8ToBytes(currentPassword);
30
+ const hashedCurrentPassword = await PasswordGenerator.hashPassword(passwordBytes, saltBytes);
31
+ if (!PasswordValidator.comparePasswordHashes(hashedCurrentPassword, user.password)) {
32
+ throw new GeneralError(PasswordHelper.CLASS_NAME, "currentPasswordMismatch");
33
+ }
34
+ }
35
+ const saltBytes = RandomHelper.generate(16);
36
+ const passwordBytes = Converter.utf8ToBytes(newPassword);
37
+ const hashedPassword = await PasswordGenerator.hashPassword(passwordBytes, saltBytes);
38
+ const updatedUser = {
39
+ email: user.email,
40
+ salt: Converter.bytesToBase64(saltBytes),
41
+ password: hashedPassword,
42
+ identity: user.identity,
43
+ organization: user.organization,
44
+ scope: user.scope,
45
+ passwordVersion: (user.passwordVersion ?? 0) + 1
46
+ };
47
+ await userEntityStorage.set(updatedUser);
48
+ await authenticationAuditService?.create({
49
+ actorId: user.email,
50
+ event: AuthAuditEvent.PasswordChanged,
51
+ data: {
52
+ userIdentity: updatedUser.identity,
53
+ organizationIdentity: updatedUser.organization
54
+ }
55
+ });
56
+ }
57
+ }
58
+ //# sourceMappingURL=passwordHelper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"passwordHelper.js","sourceRoot":"","sources":["../../../src/utils/passwordHelper.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAKtE;;GAEG;AACH,MAAM,OAAO,cAAc;IAC1B;;OAEG;IACI,MAAM,CAAU,UAAU,oBAAoC;IAErE;;;;;;;;;;OAUG;IACI,MAAM,CAAC,KAAK,CAAC,cAAc,CACjC,iBAA8D,EAC9D,0BAAqE,EACrE,IAAwB,EACxB,WAAmB,EACnB,eAAwB,EACxB,iBAA0B;QAE1B,iBAAiB,CAAC,gBAAgB,CAAC,WAAW,EAAE;YAC/C,SAAS,EAAE,iBAAiB;SAC5B,CAAC,CAAC;QAEH,IAAI,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC;YACrC,MAAM,SAAS,GAAG,SAAS,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrD,MAAM,aAAa,GAAG,SAAS,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;YAC7D,MAAM,qBAAqB,GAAG,MAAM,iBAAiB,CAAC,YAAY,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;YAC7F,IAAI,CAAC,iBAAiB,CAAC,qBAAqB,CAAC,qBAAqB,EAAE,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;gBACpF,MAAM,IAAI,YAAY,CAAC,cAAc,CAAC,UAAU,EAAE,yBAAyB,CAAC,CAAC;YAC9E,CAAC;QACF,CAAC;QAED,MAAM,SAAS,GAAG,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC5C,MAAM,aAAa,GAAG,SAAS,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;QACzD,MAAM,cAAc,GAAG,MAAM,iBAAiB,CAAC,YAAY,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QAEtF,MAAM,WAAW,GAAuB;YACvC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,SAAS,CAAC,aAAa,CAAC,SAAS,CAAC;YACxC,QAAQ,EAAE,cAAc;YACxB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,YAAY,EAAE,IAAI,CAAC,YAAY;YAC/B,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,eAAe,EAAE,CAAC,IAAI,CAAC,eAAe,IAAI,CAAC,CAAC,GAAG,CAAC;SAChD,CAAC;QAEF,MAAM,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACzC,MAAM,0BAA0B,EAAE,MAAM,CAAC;YACxC,OAAO,EAAE,IAAI,CAAC,KAAK;YACnB,KAAK,EAAE,cAAc,CAAC,eAAe;YACrC,IAAI,EAAE;gBACL,YAAY,EAAE,WAAW,CAAC,QAAQ;gBAClC,oBAAoB,EAAE,WAAW,CAAC,YAAY;aAC9C;SACD,CAAC,CAAC;IACJ,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IAuthenticationAuditComponent } from \"@3sixty/api-auth-entity-storage-models\";\nimport { AuthAuditEvent } from \"@3sixty/api-auth-entity-storage-models\";\nimport { Converter, GeneralError, Is, RandomHelper } from \"@3sixty/core\";\nimport { PasswordGenerator, PasswordValidator } from \"@3sixty/crypto\";\nimport type { IEntityStorageConnector } from \"@3sixty/entity-storage-models\";\nimport { nameof } from \"@3sixty/nameof\";\nimport type { AuthenticationUser } from \"../entities/authenticationUser.js\";\n\n/**\n * Helper class for password operations.\n */\nexport class PasswordHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<PasswordHelper>();\n\n\t/**\n\t * Update the password for a user.\n\t * Validates password strength, verifies the current password if provided, then hashes and stores the new password and raises an audit event.\n\t * @param userEntityStorage The entity storage for users.\n\t * @param authenticationAuditService The optional audit service.\n\t * @param user The user whose password is being updated.\n\t * @param newPassword The new password to set.\n\t * @param currentPassword The current password to verify against, if supplied.\n\t * @param minPasswordLength Optional minimum password length for validation.\n\t * @returns A promise that resolves when the new password has been stored and the audit entry recorded.\n\t */\n\tpublic static async updatePassword(\n\t\tuserEntityStorage: IEntityStorageConnector<AuthenticationUser>,\n\t\tauthenticationAuditService: IAuthenticationAuditComponent | undefined,\n\t\tuser: AuthenticationUser,\n\t\tnewPassword: string,\n\t\tcurrentPassword?: string,\n\t\tminPasswordLength?: number\n\t): Promise<void> {\n\t\tPasswordValidator.validatePassword(newPassword, {\n\t\t\tminLength: minPasswordLength\n\t\t});\n\n\t\tif (Is.stringValue(currentPassword)) {\n\t\t\tconst saltBytes = Converter.base64ToBytes(user.salt);\n\t\t\tconst passwordBytes = Converter.utf8ToBytes(currentPassword);\n\t\t\tconst hashedCurrentPassword = await PasswordGenerator.hashPassword(passwordBytes, saltBytes);\n\t\t\tif (!PasswordValidator.comparePasswordHashes(hashedCurrentPassword, user.password)) {\n\t\t\t\tthrow new GeneralError(PasswordHelper.CLASS_NAME, \"currentPasswordMismatch\");\n\t\t\t}\n\t\t}\n\n\t\tconst saltBytes = RandomHelper.generate(16);\n\t\tconst passwordBytes = Converter.utf8ToBytes(newPassword);\n\t\tconst hashedPassword = await PasswordGenerator.hashPassword(passwordBytes, saltBytes);\n\n\t\tconst updatedUser: AuthenticationUser = {\n\t\t\temail: user.email,\n\t\t\tsalt: Converter.bytesToBase64(saltBytes),\n\t\t\tpassword: hashedPassword,\n\t\t\tidentity: user.identity,\n\t\t\torganization: user.organization,\n\t\t\tscope: user.scope,\n\t\t\tpasswordVersion: (user.passwordVersion ?? 0) + 1\n\t\t};\n\n\t\tawait userEntityStorage.set(updatedUser);\n\t\tawait authenticationAuditService?.create({\n\t\t\tactorId: user.email,\n\t\t\tevent: AuthAuditEvent.PasswordChanged,\n\t\t\tdata: {\n\t\t\t\tuserIdentity: updatedUser.identity,\n\t\t\t\torganizationIdentity: updatedUser.organization\n\t\t\t}\n\t\t});\n\t}\n}\n"]}
@@ -0,0 +1,142 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { ScopeHelper } from "@3sixty/api-models";
4
+ import { Coerce, Is, UnauthorizedError } from "@3sixty/core";
5
+ import { VaultConnectorHelper } from "@3sixty/vault-models";
6
+ import { CookieHelper, HeaderHelper, HeaderTypes, Jwt } from "@3sixty/web";
7
+ /**
8
+ * Helper class for token operations.
9
+ */
10
+ export class TokenHelper {
11
+ /**
12
+ * Runtime name for the class.
13
+ */
14
+ static CLASS_NAME = "TokenHelper";
15
+ /**
16
+ * Create a new token.
17
+ * @param vaultConnector The vault connector.
18
+ * @param nodeId The node identifier, embedded as the JWT issuer claim.
19
+ * @param signingKeyName The signing key name, embedded as the JWT key identifier.
20
+ * @param userIdentity The subject for the token.
21
+ * @param organizationIdentity The organization for the token.
22
+ * @param tenantId The tenant id for the token.
23
+ * @param ttlMinutes The time to live for the token in minutes.
24
+ * @param scope The scopes for the token.
25
+ * @param passwordVersion The user's current password version counter, embedded in the token so that a password change invalidates existing tokens.
26
+ * @returns The new token and its expiry date.
27
+ */
28
+ static async createToken(vaultConnector, nodeId, signingKeyName, userIdentity, organizationIdentity, tenantId, ttlMinutes, scope, passwordVersion) {
29
+ const nowSeconds = Math.trunc(Date.now() / 1000);
30
+ const ttlSeconds = ttlMinutes * 60;
31
+ const vaultKeyName = `${nodeId}/${signingKeyName}`;
32
+ const jwt = await Jwt.encodeWithSigner({ alg: "EdDSA", kid: signingKeyName }, {
33
+ iss: nodeId,
34
+ sub: userIdentity,
35
+ org: organizationIdentity,
36
+ tid: tenantId,
37
+ exp: nowSeconds + ttlSeconds,
38
+ scope,
39
+ pver: passwordVersion
40
+ }, async (header, payload) => VaultConnectorHelper.jwtSigner(vaultConnector, vaultKeyName, header, payload));
41
+ return {
42
+ token: jwt,
43
+ expiry: (nowSeconds + ttlSeconds) * 1000
44
+ };
45
+ }
46
+ /**
47
+ * Verify the token.
48
+ * @param vaultConnector The vault connector.
49
+ * @param nodeId The node identifier, expected to match the JWT issuer claim.
50
+ * @param signingKeyName The signing key name, expected to match the JWT key identifier.
51
+ * @param token The token to verify.
52
+ * @param requiredScopes The required scopes.
53
+ * @param verifyUser A function to verify the user identity and organization. The password version counter embedded in the token (pver claim) is passed so callers can detect if the password has changed since the token was issued.
54
+ * @returns The verified details.
55
+ * @throws UnauthorizedError if the token is missing, invalid or expired.
56
+ */
57
+ static async verify(vaultConnector, nodeId, signingKeyName, token, requiredScopes, verifyUser) {
58
+ if (!Is.stringValue(token)) {
59
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "missing");
60
+ }
61
+ const vaultKeyName = `${nodeId}/${signingKeyName}`;
62
+ const decoded = await Jwt.verifyWithVerifier(token, async (t) => VaultConnectorHelper.jwtVerifier(vaultConnector, vaultKeyName, t));
63
+ // If some of the header/payload data is not properly populated then it is unauthorized.
64
+ if (decoded.header.kid !== signingKeyName) {
65
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "headerKeyIdMismatch");
66
+ }
67
+ else if (decoded.payload.iss !== nodeId) {
68
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "payloadIssuerMismatch");
69
+ }
70
+ else if (!Is.stringValue(decoded.payload.sub)) {
71
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "payloadMissingSubject");
72
+ }
73
+ else if (!Is.stringValue(decoded.payload.org)) {
74
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "payloadMissingOrganization");
75
+ }
76
+ else if (!Is.empty(decoded.payload?.exp) &&
77
+ decoded.payload.exp < Math.trunc(Date.now() / 1000)) {
78
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "expired");
79
+ }
80
+ if (Is.function(verifyUser)) {
81
+ const tid = Coerce.string(decoded.payload.tid);
82
+ const userVerified = await verifyUser(decoded.payload.sub, decoded.payload.org, tid, Coerce.integer(decoded.payload.pver));
83
+ if (!userVerified.includes("user")) {
84
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "userNotVerified");
85
+ }
86
+ else if (!userVerified.includes("organization")) {
87
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "organizationNotVerified");
88
+ }
89
+ else if (Is.stringValue(tid) && !userVerified.includes("tenant")) {
90
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "tenantNotVerified");
91
+ }
92
+ }
93
+ TokenHelper.verifyScopes(Coerce.string(decoded.payload.scope), requiredScopes);
94
+ return {
95
+ header: decoded.header,
96
+ payload: decoded.payload
97
+ };
98
+ }
99
+ /**
100
+ * Verify that a token carries all of the required scopes.
101
+ * @param scope The comma separated scopes from the token.
102
+ * @param requiredScopes The scopes the caller must hold.
103
+ * @throws UnauthorizedError if any of the required scopes is missing.
104
+ */
105
+ static verifyScopes(scope, requiredScopes) {
106
+ if (Is.arrayValue(requiredScopes)) {
107
+ const tokenScopes = ScopeHelper.toArray(scope);
108
+ for (const requiredScope of ScopeHelper.toArray(requiredScopes)) {
109
+ if (!tokenScopes.includes(requiredScope)) {
110
+ throw new UnauthorizedError(TokenHelper.CLASS_NAME, "insufficientScopes");
111
+ }
112
+ }
113
+ }
114
+ }
115
+ /**
116
+ * Extract the auth token from the headers, either from the authorization header or the cookie header.
117
+ * @param headers The headers to extract the token from.
118
+ * @param cookieName The name of the cookie to extract the token from.
119
+ * @returns The token if found.
120
+ */
121
+ static extractTokenFromHeaders(headers, cookieName) {
122
+ const authHeader = headers?.[HeaderTypes.Authorization];
123
+ const cookiesHeader = headers?.[HeaderTypes.Cookie];
124
+ const bearerToken = HeaderHelper.extractBearer(authHeader);
125
+ if (Is.stringValue(bearerToken)) {
126
+ return {
127
+ token: bearerToken,
128
+ location: "authorization"
129
+ };
130
+ }
131
+ else if (Is.notEmpty(cookiesHeader) && Is.stringValue(cookieName)) {
132
+ const value = CookieHelper.getCookieFromHeaders(cookiesHeader, cookieName);
133
+ if (Is.stringValue(value)) {
134
+ return {
135
+ token: value,
136
+ location: "cookie"
137
+ };
138
+ }
139
+ }
140
+ }
141
+ }
142
+ //# sourceMappingURL=tokenHelper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenHelper.js","sourceRoot":"","sources":["../../../src/utils/tokenHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ScopeHelper } from \"@3sixty/api-models\";\nimport { Coerce, Is, UnauthorizedError } from \"@3sixty/core\";\nimport { nameof } from \"@3sixty/nameof\";\nimport { type IVaultConnector, VaultConnectorHelper } from \"@3sixty/vault-models\";\nimport {\n\tCookieHelper,\n\tHeaderHelper,\n\tHeaderTypes,\n\ttype IHttpHeaders,\n\ttype IJwtHeader,\n\ttype IJwtPayload,\n\tJwt\n} from \"@3sixty/web\";\n\n/**\n * Helper class for token operations.\n */\nexport class TokenHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<TokenHelper>();\n\n\t/**\n\t * Create a new token.\n\t * @param vaultConnector The vault connector.\n\t * @param nodeId The node identifier, embedded as the JWT issuer claim.\n\t * @param signingKeyName The signing key name, embedded as the JWT key identifier.\n\t * @param userIdentity The subject for the token.\n\t * @param organizationIdentity The organization for the token.\n\t * @param tenantId The tenant id for the token.\n\t * @param ttlMinutes The time to live for the token in minutes.\n\t * @param scope The scopes for the token.\n\t * @param passwordVersion The user's current password version counter, embedded in the token so that a password change invalidates existing tokens.\n\t * @returns The new token and its expiry date.\n\t */\n\tpublic static async createToken(\n\t\tvaultConnector: IVaultConnector,\n\t\tnodeId: string,\n\t\tsigningKeyName: string,\n\t\tuserIdentity: string,\n\t\torganizationIdentity: string | undefined,\n\t\ttenantId: string | undefined,\n\t\tttlMinutes: number,\n\t\tscope?: string,\n\t\tpasswordVersion?: number\n\t): Promise<{\n\t\ttoken: string;\n\t\texpiry: number;\n\t}> {\n\t\tconst nowSeconds = Math.trunc(Date.now() / 1000);\n\t\tconst ttlSeconds = ttlMinutes * 60;\n\t\tconst vaultKeyName = `${nodeId}/${signingKeyName}`;\n\n\t\tconst jwt = await Jwt.encodeWithSigner(\n\t\t\t{ alg: \"EdDSA\", kid: signingKeyName },\n\t\t\t{\n\t\t\t\tiss: nodeId,\n\t\t\t\tsub: userIdentity,\n\t\t\t\torg: organizationIdentity,\n\t\t\t\ttid: tenantId,\n\t\t\t\texp: nowSeconds + ttlSeconds,\n\t\t\t\tscope,\n\t\t\t\tpver: passwordVersion\n\t\t\t},\n\t\t\tasync (header, payload) =>\n\t\t\t\tVaultConnectorHelper.jwtSigner(vaultConnector, vaultKeyName, header, payload)\n\t\t);\n\n\t\treturn {\n\t\t\ttoken: jwt,\n\t\t\texpiry: (nowSeconds + ttlSeconds) * 1000\n\t\t};\n\t}\n\n\t/**\n\t * Verify the token.\n\t * @param vaultConnector The vault connector.\n\t * @param nodeId The node identifier, expected to match the JWT issuer claim.\n\t * @param signingKeyName The signing key name, expected to match the JWT key identifier.\n\t * @param token The token to verify.\n\t * @param requiredScopes The required scopes.\n\t * @param verifyUser A function to verify the user identity and organization. The password version counter embedded in the token (pver claim) is passed so callers can detect if the password has changed since the token was issued.\n\t * @returns The verified details.\n\t * @throws UnauthorizedError if the token is missing, invalid or expired.\n\t */\n\tpublic static async verify(\n\t\tvaultConnector: IVaultConnector,\n\t\tnodeId: string,\n\t\tsigningKeyName: string,\n\t\ttoken: string | undefined,\n\t\trequiredScopes?: string[],\n\t\tverifyUser?: (\n\t\t\tsub: string,\n\t\t\torg: string,\n\t\t\ttid: string | undefined,\n\t\t\tpasswordVersion: number | undefined\n\t\t) => Promise<string[]>\n\t): Promise<{\n\t\theader: IJwtHeader;\n\t\tpayload: IJwtPayload;\n\t}> {\n\t\tif (!Is.stringValue(token)) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"missing\");\n\t\t}\n\n\t\tconst vaultKeyName = `${nodeId}/${signingKeyName}`;\n\t\tconst decoded = await Jwt.verifyWithVerifier(token, async t =>\n\t\t\tVaultConnectorHelper.jwtVerifier(vaultConnector, vaultKeyName, t)\n\t\t);\n\n\t\t// If some of the header/payload data is not properly populated then it is unauthorized.\n\t\tif (decoded.header.kid !== signingKeyName) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"headerKeyIdMismatch\");\n\t\t} else if (decoded.payload.iss !== nodeId) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"payloadIssuerMismatch\");\n\t\t} else if (!Is.stringValue(decoded.payload.sub)) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"payloadMissingSubject\");\n\t\t} else if (!Is.stringValue(decoded.payload.org)) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"payloadMissingOrganization\");\n\t\t} else if (\n\t\t\t!Is.empty(decoded.payload?.exp) &&\n\t\t\tdecoded.payload.exp < Math.trunc(Date.now() / 1000)\n\t\t) {\n\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"expired\");\n\t\t}\n\n\t\tif (Is.function(verifyUser)) {\n\t\t\tconst tid = Coerce.string(decoded.payload.tid);\n\t\t\tconst userVerified = await verifyUser(\n\t\t\t\tdecoded.payload.sub,\n\t\t\t\tdecoded.payload.org,\n\t\t\t\ttid,\n\t\t\t\tCoerce.integer(decoded.payload.pver)\n\t\t\t);\n\t\t\tif (!userVerified.includes(\"user\")) {\n\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"userNotVerified\");\n\t\t\t} else if (!userVerified.includes(\"organization\")) {\n\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"organizationNotVerified\");\n\t\t\t} else if (Is.stringValue(tid) && !userVerified.includes(\"tenant\")) {\n\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"tenantNotVerified\");\n\t\t\t}\n\t\t}\n\n\t\tTokenHelper.verifyScopes(Coerce.string(decoded.payload.scope), requiredScopes);\n\n\t\treturn {\n\t\t\theader: decoded.header,\n\t\t\tpayload: decoded.payload\n\t\t};\n\t}\n\n\t/**\n\t * Verify that a token carries all of the required scopes.\n\t * @param scope The comma separated scopes from the token.\n\t * @param requiredScopes The scopes the caller must hold.\n\t * @throws UnauthorizedError if any of the required scopes is missing.\n\t */\n\tpublic static verifyScopes(scope: string | undefined, requiredScopes?: string[]): void {\n\t\tif (Is.arrayValue(requiredScopes)) {\n\t\t\tconst tokenScopes = ScopeHelper.toArray(scope);\n\n\t\t\tfor (const requiredScope of ScopeHelper.toArray(requiredScopes)) {\n\t\t\t\tif (!tokenScopes.includes(requiredScope)) {\n\t\t\t\t\tthrow new UnauthorizedError(TokenHelper.CLASS_NAME, \"insufficientScopes\");\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Extract the auth token from the headers, either from the authorization header or the cookie header.\n\t * @param headers The headers to extract the token from.\n\t * @param cookieName The name of the cookie to extract the token from.\n\t * @returns The token if found.\n\t */\n\tpublic static extractTokenFromHeaders(\n\t\theaders?: IHttpHeaders,\n\t\tcookieName?: string\n\t):\n\t\t| {\n\t\t\t\ttoken: string;\n\t\t\t\tlocation: \"authorization\" | \"cookie\";\n\t\t }\n\t\t| undefined {\n\t\tconst authHeader = headers?.[HeaderTypes.Authorization];\n\t\tconst cookiesHeader = headers?.[HeaderTypes.Cookie];\n\n\t\tconst bearerToken = HeaderHelper.extractBearer(authHeader);\n\t\tif (Is.stringValue(bearerToken)) {\n\t\t\treturn {\n\t\t\t\ttoken: bearerToken,\n\t\t\t\tlocation: \"authorization\"\n\t\t\t};\n\t\t} else if (Is.notEmpty(cookiesHeader) && Is.stringValue(cookieName)) {\n\t\t\tconst value = CookieHelper.getCookieFromHeaders(cookiesHeader, cookieName);\n\t\t\tif (Is.stringValue(value)) {\n\t\t\t\treturn {\n\t\t\t\t\ttoken: value,\n\t\t\t\t\tlocation: \"cookie\"\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Class defining the storage for authentication audit entries.
3
+ */
4
+ export declare class AuthenticationAuditEntry {
5
+ /**
6
+ * The unique identifier for the audit entry.
7
+ */
8
+ id: string;
9
+ /**
10
+ * The timestamp of the audit entry in ISO 8601 format.
11
+ */
12
+ dateCreated: string;
13
+ /**
14
+ * The audit event that occurred.
15
+ */
16
+ event: string;
17
+ /**
18
+ * The actor identifier, could be e-mail, username, or other unique identifier.
19
+ */
20
+ actorId?: string;
21
+ /**
22
+ * The node identifier associated with the audit entry, if applicable.
23
+ */
24
+ nodeId?: string;
25
+ /**
26
+ * The organization identifier associated with the audit entry, if applicable.
27
+ */
28
+ organizationId?: string;
29
+ /**
30
+ * The tenant identifier associated with the audit entry, if applicable.
31
+ */
32
+ tenantId?: string;
33
+ /**
34
+ * The hashed IP addresses of the client.
35
+ */
36
+ ipAddressHashes?: string[];
37
+ /**
38
+ * The user agent string of the client.
39
+ */
40
+ userAgent?: string;
41
+ /**
42
+ * The correlation ID for request tracing.
43
+ */
44
+ correlationId?: string;
45
+ /**
46
+ * Additional data related to the audit entry, such as IP address, user agent, etc.
47
+ */
48
+ data?: unknown;
49
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Class defining the storage for authentication rate entries.
3
+ */
4
+ export declare class AuthenticationRateEntry {
5
+ /**
6
+ * The id for the rate entry.
7
+ */
8
+ id: string;
9
+ /**
10
+ * Array of ISO date strings representing timestamps of failed attempts.
11
+ */
12
+ timestamps: string[];
13
+ /**
14
+ * Last modification time in ISO date format.
15
+ */
16
+ dateModified: string;
17
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Class defining the storage for user login credentials.
3
+ */
4
+ export declare class AuthenticationUser {
5
+ /**
6
+ * The user e-mail address.
7
+ */
8
+ email: string;
9
+ /**
10
+ * The encrypted password for the user.
11
+ */
12
+ password: string;
13
+ /**
14
+ * The salt for the password.
15
+ */
16
+ salt: string;
17
+ /**
18
+ * The user identity.
19
+ */
20
+ identity: string;
21
+ /**
22
+ * The users organization.
23
+ */
24
+ organization: string;
25
+ /**
26
+ * The scope assigned to the user, comma separated.
27
+ */
28
+ scope: string;
29
+ /**
30
+ * The password version counter, incremented on every password change to invalidate existing tokens.
31
+ */
32
+ passwordVersion?: number;
33
+ }
@@ -0,0 +1,26 @@
1
+ export * from "./entities/authenticationAuditEntry.js";
2
+ export * from "./entities/authenticationRateEntry.js";
3
+ export * from "./entities/authenticationUser.js";
4
+ export * from "./models/IAuthHeaderProcessorConfig.js";
5
+ export * from "./models/IAuthHeaderProcessorConstructorOptions.js";
6
+ export * from "./models/IAuthTokenContext.js";
7
+ export * from "./models/IEntityStorageAuthenticationAdminServiceConfig.js";
8
+ export * from "./models/IEntityStorageAuthenticationAdminServiceConstructorOptions.js";
9
+ export * from "./models/IEntityStorageAuthenticationAuditServiceConfig.js";
10
+ export * from "./models/IEntityStorageAuthenticationAuditServiceConstructorOptions.js";
11
+ export * from "./models/IEntityStorageAuthenticationRateServiceConfig.js";
12
+ export * from "./models/IEntityStorageAuthenticationRateServiceConstructorOptions.js";
13
+ export * from "./models/IEntityStorageAuthenticationServiceConfig.js";
14
+ export * from "./models/IEntityStorageAuthenticationServiceConstructorOptions.js";
15
+ export * from "./processors/authHeaderProcessor.js";
16
+ export * from "./restEntryPoints.js";
17
+ export * from "./routes/entityStorageAuthenticationAdminRoutes.js";
18
+ export * from "./routes/entityStorageAuthenticationAuditRoutes.js";
19
+ export * from "./routes/entityStorageAuthenticationRoutes.js";
20
+ export * from "./schema.js";
21
+ export * from "./services/entityStorageAuthenticationAdminService.js";
22
+ export * from "./services/entityStorageAuthenticationAuditService.js";
23
+ export * from "./services/entityStorageAuthenticationRateService.js";
24
+ export * from "./services/entityStorageAuthenticationService.js";
25
+ export * from "./utils/tokenHelper.js";
26
+ export * from "./utils/passwordHelper.js";
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Configuration for the authentication header processor
3
+ */
4
+ export interface IAuthHeaderProcessorConfig {
5
+ /**
6
+ * The name of the key to retrieve from the vault for signing JWT.
7
+ * @default auth-signing
8
+ */
9
+ signingKeyName?: string;
10
+ /**
11
+ * The name of the cookie to use for the token.
12
+ * @default access_token
13
+ */
14
+ cookieName?: string;
15
+ /**
16
+ * Include the stack with errors.
17
+ */
18
+ includeErrorStack?: boolean;
19
+ /**
20
+ * The time in milliseconds a verified token is kept in the in-memory cache, counted from when it
21
+ * was verified and not extended by use, set to 0 to disable caching. A hit skips the vault
22
+ * signature check, the tenant lookup and the user lookup, so this is also the longest a password
23
+ * change, a user removal or a signing key rotation can go unnoticed. An entry never outlives the
24
+ * expiry claim of its own token either. The scopes a route requires are still checked for every
25
+ * request.
26
+ * @default 30000
27
+ */
28
+ tokenCacheTtlMs?: number;
29
+ /**
30
+ * The maximum number of verified tokens to hold in the in-memory cache.
31
+ * @default undefined (LfuCache default)
32
+ */
33
+ tokenCacheCapacity?: number;
34
+ /**
35
+ * Maximum time in milliseconds to wait for the token cache mutex during a cache population.
36
+ * @default undefined (LfuCache default)
37
+ */
38
+ tokenCacheMutexTimeoutMs?: number;
39
+ }
@@ -0,0 +1,25 @@
1
+ import type { IAuthHeaderProcessorConfig } from "./IAuthHeaderProcessorConfig.js";
2
+ /**
3
+ * Options for the AuthHeaderProcessor constructor.
4
+ */
5
+ export interface IAuthHeaderProcessorConstructorOptions {
6
+ /**
7
+ * The entity storage for users.
8
+ * @default authentication-user
9
+ */
10
+ userEntityStorageType?: string;
11
+ /**
12
+ * The vault for the private keys.
13
+ * @default vault
14
+ */
15
+ vaultConnectorType?: string;
16
+ /**
17
+ * The component to retrieve tenant information.
18
+ * @default tenant-admin
19
+ */
20
+ tenantAdminComponentType?: string;
21
+ /**
22
+ * The configuration for the processor.
23
+ */
24
+ config?: IAuthHeaderProcessorConfig;
25
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * The context resolved from an access token, everything a request needs that does not depend on
3
+ * the route being called.
4
+ */
5
+ export interface IAuthTokenContext {
6
+ /**
7
+ * The tenant id from the token, if it carried one.
8
+ */
9
+ tenantId?: string;
10
+ /**
11
+ * The organization id of the tenant the token belongs to.
12
+ */
13
+ tenantOrganizationId?: string;
14
+ /**
15
+ * The public origin of the tenant the token belongs to.
16
+ */
17
+ tenantPublicOrigin?: string;
18
+ /**
19
+ * The identity of the verified user.
20
+ */
21
+ userIdentity?: string;
22
+ /**
23
+ * The organization of the verified user.
24
+ */
25
+ userOrganization?: string;
26
+ /**
27
+ * The comma separated scopes carried by the token.
28
+ */
29
+ scope?: string;
30
+ /**
31
+ * The expiry of the token in milliseconds since the epoch, if it carried one.
32
+ */
33
+ expires?: number;
34
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Configuration for the entity storage authentication admin service.
3
+ */
4
+ export interface IEntityStorageAuthenticationAdminServiceConfig {
5
+ /**
6
+ * The minimum password length.
7
+ * @default 8
8
+ */
9
+ minPasswordLength?: number;
10
+ /**
11
+ * The scope value that grants escalated privilege to assign privileged roles. Defaults to "global-admin".
12
+ */
13
+ escalatedPrivilegeScope?: string;
14
+ }
@@ -0,0 +1,20 @@
1
+ import type { IEntityStorageAuthenticationAdminServiceConfig } from "./IEntityStorageAuthenticationAdminServiceConfig.js";
2
+ /**
3
+ * Options for the EntityStorageAuthenticationAdminService constructor.
4
+ */
5
+ export interface IEntityStorageAuthenticationAdminServiceConstructorOptions {
6
+ /**
7
+ * The entity storage for the users.
8
+ * @default authentication-user
9
+ */
10
+ userEntityStorageType?: string;
11
+ /**
12
+ * The audit service.
13
+ * @default authentication-audit
14
+ */
15
+ authenticationAuditServiceType?: string;
16
+ /**
17
+ * The configuration for the authentication.
18
+ */
19
+ config?: IEntityStorageAuthenticationAdminServiceConfig;
20
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Config for the EntityStorageAuthenticationAuditService constructor.
3
+ */
4
+ export interface IEntityStorageAuthenticationAuditServiceConfig {
5
+ /**
6
+ * The server-side salt for hashing IP addresses in audit logs, if configured.
7
+ */
8
+ ipHashSalt?: string;
9
+ }
@@ -0,0 +1,15 @@
1
+ import type { IEntityStorageAuthenticationAuditServiceConfig } from "./IEntityStorageAuthenticationAuditServiceConfig.js";
2
+ /**
3
+ * Options for the EntityStorageAuthenticationAuditService constructor.
4
+ */
5
+ export interface IEntityStorageAuthenticationAuditServiceConstructorOptions {
6
+ /**
7
+ * The entity storage for the audit entries.
8
+ * @default authentication-audit-entry
9
+ */
10
+ authenticationAuditEntryStorageType?: string;
11
+ /**
12
+ * The configuration for the authentication audit service.
13
+ */
14
+ config?: IEntityStorageAuthenticationAuditServiceConfig;
15
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Configuration for the entity storage authentication rate service.
3
+ */
4
+ export interface IEntityStorageAuthenticationRateServiceConfig {
5
+ /**
6
+ * Interval between cleanup runs in minutes.
7
+ * @default 5
8
+ */
9
+ cleanupIntervalMinutes?: number;
10
+ }
@@ -0,0 +1,25 @@
1
+ import type { IEntityStorageAuthenticationRateServiceConfig } from "./IEntityStorageAuthenticationRateServiceConfig.js";
2
+ /**
3
+ * Options for the EntityStorageAuthenticationRateService constructor.
4
+ */
5
+ export interface IEntityStorageAuthenticationRateServiceConstructorOptions {
6
+ /**
7
+ * The entity storage for authentication rate entries.
8
+ * @default authentication-rate-entry
9
+ */
10
+ authenticationRateEntryStorageType?: string;
11
+ /**
12
+ * The task scheduler component type.
13
+ * @default task-scheduler
14
+ */
15
+ taskSchedulerComponentType?: string;
16
+ /**
17
+ * The platform component type, used to run the periodic cleanup per tenant.
18
+ * @default platform
19
+ */
20
+ platformComponentType?: string;
21
+ /**
22
+ * The configuration for the authentication rate service.
23
+ */
24
+ config?: IEntityStorageAuthenticationRateServiceConfig;
25
+ }