keycloak-authentication-strategy.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import { HttpService } from '@nestjs/common';
  2. import {
  3. AuthenticationStrategy,
  4. ExternalAuthenticationService,
  5. Injector,
  6. Logger,
  7. RequestContext,
  8. RoleService,
  9. User,
  10. } from '@vendure/core';
  11. import { DocumentNode } from 'graphql';
  12. import gql from 'graphql-tag';
  13. export type KeycloakAuthData = {
  14. token: string;
  15. };
  16. export type OpenIdUserInfo = {
  17. name: string;
  18. sub: string;
  19. email?: string;
  20. email_verified: boolean;
  21. preferred_username: string;
  22. given_name?: string;
  23. family_name?: string;
  24. };
  25. export class KeycloakAuthenticationStrategy implements AuthenticationStrategy<KeycloakAuthData> {
  26. readonly name = 'keycloak';
  27. private externalAuthenticationService: ExternalAuthenticationService;
  28. private httpService: HttpService;
  29. private roleService: RoleService;
  30. private bearerToken: string;
  31. init(injector: Injector) {
  32. this.externalAuthenticationService = injector.get(ExternalAuthenticationService);
  33. this.httpService = injector.get(HttpService);
  34. this.roleService = injector.get(RoleService);
  35. }
  36. defineInputType(): DocumentNode {
  37. return gql`
  38. input KeycloakAuthInput {
  39. token: String!
  40. }
  41. `;
  42. }
  43. async authenticate(ctx: RequestContext, data: KeycloakAuthData): Promise<User | false> {
  44. let userInfo: OpenIdUserInfo;
  45. this.bearerToken = data.token;
  46. try {
  47. const response = await this.httpService
  48. .get('http://localhost:9000/auth/realms/myrealm/protocol/openid-connect/userinfo', {
  49. headers: {
  50. Authorization: `Bearer ${this.bearerToken}`,
  51. },
  52. })
  53. .toPromise();
  54. userInfo = response.data;
  55. } catch (e) {
  56. Logger.error(e);
  57. return false;
  58. }
  59. if (!userInfo) {
  60. return false;
  61. }
  62. const user = await this.externalAuthenticationService.findAdministratorUser(
  63. ctx,
  64. this.name,
  65. userInfo.sub,
  66. );
  67. if (user) {
  68. return user;
  69. }
  70. const roles = await this.roleService.findAll(ctx);
  71. const merchantRole = roles.items.find(r => r.code === 'merchant');
  72. if (!merchantRole) {
  73. Logger.error(`Could not find "merchant" role`);
  74. return false;
  75. }
  76. return this.externalAuthenticationService.createAdministratorAndUser(ctx, {
  77. strategy: this.name,
  78. externalIdentifier: userInfo.sub,
  79. identifier: userInfo.preferred_username,
  80. emailAddress: userInfo.email,
  81. firstName: userInfo.given_name,
  82. lastName: userInfo.family_name,
  83. roles: [merchantRole],
  84. });
  85. }
  86. }