auth.resolver.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import { Args, Context, Mutation, Query, Resolver } from '@nestjs/graphql';
  2. import { Request, Response } from 'express';
  3. import {
  4. LoginMutationArgs,
  5. LoginResult,
  6. Permission,
  7. RefreshCustomerVerificationMutationArgs,
  8. RegisterCustomerAccountMutationArgs,
  9. VerifyCustomerAccountMutationArgs,
  10. } from '../../../../../shared/generated-types';
  11. import { VerificationTokenError } from '../../../common/error/errors';
  12. import { ConfigService } from '../../../config/config.service';
  13. import { User } from '../../../entity/user/user.entity';
  14. import { AuthService } from '../../../service/services/auth.service';
  15. import { ChannelService } from '../../../service/services/channel.service';
  16. import { CustomerService } from '../../../service/services/customer.service';
  17. import { UserService } from '../../../service/services/user.service';
  18. import { extractAuthToken } from '../../common/extract-auth-token';
  19. import { RequestContext } from '../../common/request-context';
  20. import { setAuthToken } from '../../common/set-auth-token';
  21. import { Allow } from '../../decorators/allow.decorator';
  22. import { Ctx } from '../../decorators/request-context.decorator';
  23. @Resolver('Auth')
  24. export class AuthResolver {
  25. constructor(
  26. private authService: AuthService,
  27. private userService: UserService,
  28. private channelService: ChannelService,
  29. private customerService: CustomerService,
  30. private configService: ConfigService,
  31. ) {}
  32. /**
  33. * Attempts a login given the username and password of a user. If successful, returns
  34. * the user data and returns the token either in a cookie or in the response body.
  35. */
  36. @Mutation()
  37. @Allow(Permission.Public)
  38. async login(
  39. @Args() args: LoginMutationArgs,
  40. @Ctx() ctx: RequestContext,
  41. @Context('req') req: Request,
  42. @Context('res') res: Response,
  43. ): Promise<LoginResult> {
  44. return await this.createAuthenticatedSession(ctx, args, req, res);
  45. }
  46. @Mutation()
  47. @Allow(Permission.Public)
  48. async logout(@Context('req') req: Request, @Context('res') res: Response): Promise<boolean> {
  49. const token = extractAuthToken(req, this.configService.authOptions.tokenMethod);
  50. if (!token) {
  51. return false;
  52. }
  53. await this.authService.deleteSessionByToken(token);
  54. setAuthToken({
  55. req,
  56. res,
  57. authOptions: this.configService.authOptions,
  58. rememberMe: false,
  59. authToken: '',
  60. });
  61. return true;
  62. }
  63. @Mutation()
  64. @Allow(Permission.Public)
  65. async registerCustomerAccount(
  66. @Ctx() ctx: RequestContext,
  67. @Args() args: RegisterCustomerAccountMutationArgs,
  68. ) {
  69. return this.customerService.registerCustomerAccount(ctx, args.input).then(() => true);
  70. }
  71. @Mutation()
  72. @Allow(Permission.Public)
  73. async verifyCustomerAccount(
  74. @Ctx() ctx: RequestContext,
  75. @Args() args: VerifyCustomerAccountMutationArgs,
  76. @Context('req') req: Request,
  77. @Context('res') res: Response,
  78. ) {
  79. const customer = await this.customerService.verifyCustomerEmailAddress(
  80. ctx,
  81. args.token,
  82. args.password,
  83. );
  84. if (customer && customer.user) {
  85. return this.createAuthenticatedSession(
  86. ctx,
  87. {
  88. username: customer.user.identifier,
  89. password: args.password,
  90. rememberMe: true,
  91. },
  92. req,
  93. res,
  94. );
  95. } else {
  96. throw new VerificationTokenError();
  97. }
  98. }
  99. @Mutation()
  100. @Allow(Permission.Public)
  101. async refreshCustomerVerification(
  102. @Ctx() ctx: RequestContext,
  103. @Args() args: RefreshCustomerVerificationMutationArgs,
  104. ) {
  105. return this.customerService.refreshVerificationToken(ctx, args.emailAddress).then(() => true);
  106. }
  107. /**
  108. * Returns information about the current authenticated user.
  109. */
  110. @Query()
  111. @Allow(Permission.Authenticated)
  112. async me(@Ctx() ctx: RequestContext) {
  113. const userId = ctx.activeUserId;
  114. const user = userId && (await this.userService.getUserById(userId));
  115. return user ? this.publiclyAccessibleUser(user) : null;
  116. }
  117. /**
  118. * Creates an authenticated session and sets the session token.
  119. */
  120. private async createAuthenticatedSession(
  121. ctx: RequestContext,
  122. args: LoginMutationArgs,
  123. req: Request,
  124. res: Response,
  125. ) {
  126. const session = await this.authService.authenticate(ctx, args.username, args.password);
  127. setAuthToken({
  128. req,
  129. res,
  130. authOptions: this.configService.authOptions,
  131. rememberMe: args.rememberMe || false,
  132. authToken: session.token,
  133. });
  134. return {
  135. user: this.publiclyAccessibleUser(session.user),
  136. };
  137. }
  138. /**
  139. * Exposes a subset of the User properties which we want to expose to the public API.
  140. */
  141. private publiclyAccessibleUser(user: User): any {
  142. return {
  143. id: user.id,
  144. identifier: user.identifier,
  145. channelTokens: this.getAvailableChannelTokens(user),
  146. };
  147. }
  148. private getAvailableChannelTokens(user: User): string[] {
  149. return user.roles.reduce((tokens, role) => role.channels.map(c => c.token), [] as string[]);
  150. }
  151. }