SubscriptionTokensController.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import { CrossServiceTokenData, TokenEncoderInterface } from '@standardnotes/security'
  2. import { ErrorTag } from '@standardnotes/responses'
  3. import { SettingName } from '@standardnotes/settings'
  4. import { Request, Response } from 'express'
  5. import { inject } from 'inversify'
  6. import {
  7. BaseHttpController,
  8. controller,
  9. httpPost,
  10. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  11. results,
  12. } from 'inversify-express-utils'
  13. import TYPES from '../Bootstrap/Types'
  14. import { Role } from '../Domain/Role/Role'
  15. import { SettingServiceInterface } from '../Domain/Setting/SettingServiceInterface'
  16. import { AuthenticateSubscriptionToken } from '../Domain/UseCase/AuthenticateSubscriptionToken/AuthenticateSubscriptionToken'
  17. import { CreateSubscriptionToken } from '../Domain/UseCase/CreateSubscriptionToken/CreateSubscriptionToken'
  18. import { User } from '../Domain/User/User'
  19. import { ProjectorInterface } from '../Projection/ProjectorInterface'
  20. import { ControllerContainerInterface } from '@standardnotes/domain-core'
  21. @controller('/subscription-tokens')
  22. export class SubscriptionTokensController extends BaseHttpController {
  23. constructor(
  24. @inject(TYPES.Auth_CreateSubscriptionToken) private createSubscriptionToken: CreateSubscriptionToken,
  25. @inject(TYPES.Auth_AuthenticateSubscriptionToken) private authenticateToken: AuthenticateSubscriptionToken,
  26. @inject(TYPES.Auth_SettingService) private settingService: SettingServiceInterface,
  27. @inject(TYPES.Auth_UserProjector) private userProjector: ProjectorInterface<User>,
  28. @inject(TYPES.Auth_RoleProjector) private roleProjector: ProjectorInterface<Role>,
  29. @inject(TYPES.Auth_CrossServiceTokenEncoder) private tokenEncoder: TokenEncoderInterface<CrossServiceTokenData>,
  30. @inject(TYPES.Auth_AUTH_JWT_TTL) private jwtTTL: number,
  31. @inject(TYPES.Auth_ControllerContainer) private controllerContainer: ControllerContainerInterface,
  32. ) {
  33. super()
  34. this.controllerContainer.register('auth.subscription-tokens.create', this.createToken.bind(this))
  35. }
  36. @httpPost('/', TYPES.Auth_ApiGatewayAuthMiddleware)
  37. async createToken(_request: Request, response: Response): Promise<results.JsonResult> {
  38. if (response.locals.readOnlyAccess) {
  39. return this.json(
  40. {
  41. error: {
  42. tag: ErrorTag.ReadOnlyAccess,
  43. message: 'Session has read-only access.',
  44. },
  45. },
  46. 401,
  47. )
  48. }
  49. const result = await this.createSubscriptionToken.execute({
  50. userUuid: response.locals.user.uuid,
  51. })
  52. return this.json({
  53. token: result.subscriptionToken.token,
  54. })
  55. }
  56. @httpPost('/:token/validate')
  57. async validate(request: Request): Promise<results.JsonResult> {
  58. const authenticateTokenResponse = await this.authenticateToken.execute({
  59. token: request.params.token,
  60. })
  61. if (!authenticateTokenResponse.success) {
  62. return this.json(
  63. {
  64. error: {
  65. tag: 'invalid-auth',
  66. message: 'Invalid login credentials.',
  67. },
  68. },
  69. 401,
  70. )
  71. }
  72. const user = authenticateTokenResponse.user as User
  73. let extensionKey = undefined
  74. const extensionKeySetting = await this.settingService.findSettingWithDecryptedValue({
  75. settingName: SettingName.create(SettingName.NAMES.ExtensionKey).getValue(),
  76. userUuid: user.uuid,
  77. })
  78. if (extensionKeySetting !== null) {
  79. extensionKey = extensionKeySetting.value as string
  80. }
  81. const roles = await user.roles
  82. const authTokenData: CrossServiceTokenData = {
  83. user: await this.projectUser(user),
  84. roles: await this.projectRoles(roles),
  85. extensionKey,
  86. }
  87. const authToken = this.tokenEncoder.encodeExpirableToken(authTokenData, this.jwtTTL)
  88. return this.json({ authToken })
  89. }
  90. private async projectUser(user: User): Promise<{ uuid: string; email: string }> {
  91. return <{ uuid: string; email: string }>await this.userProjector.projectSimple(user)
  92. }
  93. private async projectRoles(roles: Array<Role>): Promise<Array<{ uuid: string; name: string }>> {
  94. const roleProjections = []
  95. for (const role of roles) {
  96. roleProjections.push(<{ uuid: string; name: string }>await this.roleProjector.projectSimple(role))
  97. }
  98. return roleProjections
  99. }
  100. }