base-auth.resolver.ts 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. import { AuthenticationResult as ShopAuthenticationResult } from '@vendure/common/lib/generated-shop-types';
  2. import {
  3. AuthenticationResult as AdminAuthenticationResult,
  4. CurrentUser,
  5. CurrentUserChannel,
  6. MutationAuthenticateArgs,
  7. MutationLoginArgs,
  8. Success,
  9. } from '@vendure/common/lib/generated-types';
  10. import { Request, Response } from 'express';
  11. import { isGraphQlErrorResult } from '../../../common/error/error-result';
  12. import { ForbiddenError } from '../../../common/error/errors';
  13. import { NativeAuthStrategyError as AdminNativeAuthStrategyError } from '../../../common/error/generated-graphql-admin-errors';
  14. import {
  15. InvalidCredentialsError,
  16. NativeAuthStrategyError as ShopNativeAuthStrategyError,
  17. NotVerifiedError,
  18. } from '../../../common/error/generated-graphql-shop-errors';
  19. import { NATIVE_AUTH_STRATEGY_NAME } from '../../../config/auth/native-authentication-strategy';
  20. import { ConfigService } from '../../../config/config.service';
  21. import { Logger } from '../../../config/logger/vendure-logger';
  22. import { User } from '../../../entity/user/user.entity';
  23. import { getUserChannelsPermissions } from '../../../service/helpers/utils/get-user-channels-permissions';
  24. import { AdministratorService } from '../../../service/services/administrator.service';
  25. import { AuthService } from '../../../service/services/auth.service';
  26. import { UserService } from '../../../service/services/user.service';
  27. import { extractSessionToken } from '../../common/extract-session-token';
  28. import { ApiType } from '../../common/get-api-type';
  29. import { RequestContext } from '../../common/request-context';
  30. import { setSessionToken } from '../../common/set-session-token';
  31. export class BaseAuthResolver {
  32. protected readonly nativeAuthStrategyIsConfigured: boolean;
  33. constructor(
  34. protected authService: AuthService,
  35. protected userService: UserService,
  36. protected administratorService: AdministratorService,
  37. protected configService: ConfigService,
  38. ) {
  39. this.nativeAuthStrategyIsConfigured = !!this.configService.authOptions.shopAuthenticationStrategy.find(
  40. strategy => strategy.name === NATIVE_AUTH_STRATEGY_NAME,
  41. );
  42. }
  43. /**
  44. * Attempts a login given the username and password of a user. If successful, returns
  45. * the user data and returns the token either in a cookie or in the response body.
  46. */
  47. async baseLogin(
  48. args: MutationLoginArgs,
  49. ctx: RequestContext,
  50. req: Request,
  51. res: Response,
  52. ): Promise<AdminAuthenticationResult | ShopAuthenticationResult | NotVerifiedError> {
  53. return await this.authenticateAndCreateSession(
  54. ctx,
  55. {
  56. input: { [NATIVE_AUTH_STRATEGY_NAME]: args },
  57. },
  58. req,
  59. res,
  60. );
  61. }
  62. async logout(ctx: RequestContext, req: Request, res: Response): Promise<Success> {
  63. const token = extractSessionToken(req, this.configService.authOptions.tokenMethod);
  64. if (!token) {
  65. return { success: false };
  66. }
  67. await this.authService.destroyAuthenticatedSession(ctx, token);
  68. setSessionToken({
  69. req,
  70. res,
  71. authOptions: this.configService.authOptions,
  72. rememberMe: false,
  73. sessionToken: '',
  74. });
  75. return { success: true };
  76. }
  77. /**
  78. * Returns information about the current authenticated user.
  79. */
  80. async me(ctx: RequestContext, apiType: ApiType) {
  81. const userId = ctx.activeUserId;
  82. if (!userId) {
  83. throw new ForbiddenError();
  84. }
  85. if (apiType === 'admin') {
  86. const administrator = await this.administratorService.findOneByUserId(ctx, userId);
  87. if (!administrator) {
  88. throw new ForbiddenError();
  89. }
  90. }
  91. const user = userId && (await this.userService.getUserById(ctx, userId));
  92. return user ? this.publiclyAccessibleUser(user) : null;
  93. }
  94. /**
  95. * Creates an authenticated session and sets the session token.
  96. */
  97. protected async authenticateAndCreateSession(
  98. ctx: RequestContext,
  99. args: MutationAuthenticateArgs,
  100. req: Request,
  101. res: Response,
  102. ): Promise<AdminAuthenticationResult | ShopAuthenticationResult | NotVerifiedError> {
  103. const [method, data] = Object.entries(args.input)[0];
  104. const { apiType } = ctx;
  105. const session = await this.authService.authenticate(ctx, apiType, method, data);
  106. if (isGraphQlErrorResult(session)) {
  107. return session;
  108. }
  109. if (apiType && apiType === 'admin') {
  110. const administrator = await this.administratorService.findOneByUserId(ctx, session.user.id);
  111. if (!administrator) {
  112. return new InvalidCredentialsError('');
  113. }
  114. }
  115. setSessionToken({
  116. req,
  117. res,
  118. authOptions: this.configService.authOptions,
  119. rememberMe: args.rememberMe || false,
  120. sessionToken: session.token,
  121. });
  122. return this.publiclyAccessibleUser(session.user);
  123. }
  124. /**
  125. * Updates the password of an existing User.
  126. */
  127. protected async updatePassword(
  128. ctx: RequestContext,
  129. currentPassword: string,
  130. newPassword: string,
  131. ): Promise<boolean | InvalidCredentialsError> {
  132. const { activeUserId } = ctx;
  133. if (!activeUserId) {
  134. throw new ForbiddenError();
  135. }
  136. return this.userService.updatePassword(ctx, activeUserId, currentPassword, newPassword);
  137. }
  138. /**
  139. * Exposes a subset of the User properties which we want to expose to the public API.
  140. */
  141. protected publiclyAccessibleUser(user: User): CurrentUser {
  142. return {
  143. id: user.id,
  144. identifier: user.identifier,
  145. channels: getUserChannelsPermissions(user) as CurrentUserChannel[],
  146. };
  147. }
  148. protected requireNativeAuthStrategy():
  149. | AdminNativeAuthStrategyError
  150. | ShopNativeAuthStrategyError
  151. | undefined {
  152. if (!this.nativeAuthStrategyIsConfigured) {
  153. const authStrategyNames = this.configService.authOptions.shopAuthenticationStrategy
  154. .map(s => s.name)
  155. .join(', ');
  156. const errorMessage =
  157. 'This GraphQL operation requires that the NativeAuthenticationStrategy be configured for the Shop API.\n' +
  158. `Currently the following AuthenticationStrategies are enabled: ${authStrategyNames}`;
  159. Logger.error(errorMessage);
  160. return new AdminNativeAuthStrategyError();
  161. }
  162. }
  163. }