keycloak-authentication-strategy.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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(this.name, userInfo.sub);
  63. if (user) {
  64. return user;
  65. }
  66. const roles = await this.roleService.findAll(ctx);
  67. const merchantRole = roles.items.find(r => r.code === 'merchant');
  68. if (!merchantRole) {
  69. Logger.error(`Could not find "merchant" role`);
  70. return false;
  71. }
  72. return this.externalAuthenticationService.createAdministratorAndUser(ctx, {
  73. strategy: this.name,
  74. externalIdentifier: userInfo.sub,
  75. identifier: userInfo.preferred_username,
  76. emailAddress: userInfo.email,
  77. firstName: userInfo.given_name,
  78. lastName: userInfo.family_name,
  79. roles: [merchantRole],
  80. });
  81. }
  82. }