external-account.ts 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. import { ErrorV3 } from '@growi/core/dist/models';
  2. import { LoginErrorCode } from '~/interfaces/errors/login-error';
  3. import type { IExternalAuthProviderType } from '~/interfaces/external-auth-provider';
  4. import loggerFactory from '~/utils/logger';
  5. import { NullUsernameToBeRegisteredError } from '../models/errors';
  6. import type { ExternalAccountDocument } from '../models/external-account';
  7. import ExternalAccount from '../models/external-account';
  8. import type PassportService from './passport';
  9. const logger = loggerFactory('growi:service:external-account-service');
  10. class ExternalAccountService {
  11. passportService: PassportService;
  12. constructor(passportService: PassportService) {
  13. this.passportService = passportService;
  14. }
  15. async getOrCreateUser(
  16. userInfo: { id: string; username: string; name?: string; email?: string },
  17. providerId: IExternalAuthProviderType,
  18. ): Promise<ExternalAccountDocument | undefined> {
  19. // get option
  20. const isSameUsernameTreatedAsIdenticalUser =
  21. this.passportService.isSameUsernameTreatedAsIdenticalUser(providerId);
  22. const isSameEmailTreatedAsIdenticalUser =
  23. providerId === 'ldap'
  24. ? false
  25. : this.passportService.isSameEmailTreatedAsIdenticalUser(providerId);
  26. try {
  27. // find or register(create) user
  28. const externalAccount = await ExternalAccount.findOrRegister(
  29. isSameUsernameTreatedAsIdenticalUser,
  30. isSameEmailTreatedAsIdenticalUser,
  31. providerId,
  32. userInfo.id,
  33. userInfo.username,
  34. userInfo.name,
  35. userInfo.email,
  36. );
  37. return externalAccount;
  38. } catch (err) {
  39. if (err instanceof NullUsernameToBeRegisteredError) {
  40. logger.error(err.message);
  41. throw new ErrorV3(err.message);
  42. } else if (err.name === 'DuplicatedUsernameException') {
  43. if (
  44. isSameEmailTreatedAsIdenticalUser ||
  45. isSameUsernameTreatedAsIdenticalUser
  46. ) {
  47. // associate to existing user
  48. logger.debug(
  49. `ExternalAccount '${userInfo.username}' will be created and bound to the exisiting User account`,
  50. );
  51. return ExternalAccount.associate(providerId, userInfo.id, err.user);
  52. }
  53. logger.error('provider-DuplicatedUsernameException', providerId);
  54. throw new ErrorV3(
  55. 'message.provider_duplicated_username_exception',
  56. LoginErrorCode.PROVIDER_DUPLICATED_USERNAME_EXCEPTION,
  57. undefined,
  58. { failedProviderForDuplicatedUsernameException: providerId },
  59. );
  60. } else if (err.name === 'UserUpperLimitException') {
  61. logger.error(err.message);
  62. throw new ErrorV3(err.message);
  63. }
  64. }
  65. }
  66. }
  67. export let externalAccountService: ExternalAccountService | undefined; // singleton instance
  68. export default function instanciate(passportService: PassportService): void {
  69. externalAccountService = new ExternalAccountService(passportService);
  70. }