diff --git a/packages/core/src/service/helpers/external-authentication/external-authentication.service.ts b/packages/core/src/service/helpers/external-authentication/external-authentication.service.ts index 2118702550..b149ca698d 100644 --- a/packages/core/src/service/helpers/external-authentication/external-authentication.service.ts +++ b/packages/core/src/service/helpers/external-authentication/external-authentication.service.ts @@ -4,9 +4,13 @@ import { HistoryEntryType } from '@vendure/common/lib/generated-types'; import { Connection } from 'typeorm'; import { RequestContext } from '../../../api/common/request-context'; +import { Administrator } from '../../../entity/administrator/administrator.entity'; import { ExternalAuthenticationMethod } from '../../../entity/authentication-method/external-authentication-method.entity'; import { Customer } from '../../../entity/customer/customer.entity'; +import { Role } from '../../../entity/role/role.entity'; import { User } from '../../../entity/user/user.entity'; +import { AdministratorService } from '../../services/administrator.service'; +import { CustomerService } from '../../services/customer.service'; import { HistoryService } from '../../services/history.service'; import { RoleService } from '../../services/role.service'; @@ -23,28 +27,47 @@ export class ExternalAuthenticationService { @InjectConnection() private connection: Connection, private roleService: RoleService, private historyService: HistoryService, + private customerService: CustomerService, + private administratorService: AdministratorService, ) {} /** * @description * Looks up a User based on their identifier from an external authentication - * provider. + * provider, ensuring this User is associated with a Customer account. */ - async findUser(strategy: string, externalIdentifier: string): Promise { - return await this.connection - .getRepository(User) - .createQueryBuilder('user') - .leftJoinAndSelect('user.authenticationMethods', 'authMethod') - .where('authMethod.strategy = :strategy', { strategy }) - .andWhere('authMethod.externalIdentifier = :externalIdentifier', { externalIdentifier }) - .andWhere('user.deletedAt IS NULL') - .getOne(); + async findCustomerUser(strategy: string, externalIdentifier: string): Promise { + const user = await this.findUser(strategy, externalIdentifier); + + if (user) { + // Ensure this User is associated with a Customer + const customer = await this.customerService.findOneByUserId(user.id); + if (customer) { + return user; + } + } } /** * @description - * If a user has been successfully authenticated by an external authentication provider, yet cannot - * be found using `findUserByExternalAuthenticationMethod`, then we need to create a new User and + * Looks up a User based on their identifier from an external authentication + * provider, ensuring this User is associated with an Administrator account. + */ + async findAdministratorUser(strategy: string, externalIdentifier: string): Promise { + const user = await this.findUser(strategy, externalIdentifier); + if (user) { + // Ensure this User is associated with an Administrator + const administrator = await this.administratorService.findOneByUserId(user.id); + if (administrator) { + return user; + } + } + } + + /** + * @description + * If a customer has been successfully authenticated by an external authentication provider, yet cannot + * be found using `findCustomerUser`, then we need to create a new User and * Customer record in Vendure for that user. This method encapsulates that logic as well as additional * housekeeping such as adding a record to the Customer's history. */ @@ -107,4 +130,61 @@ export class ExternalAuthenticationService { return savedUser; } + + /** + * @description + * If an administrator has been successfully authenticated by an external authentication provider, yet cannot + * be found using `findAdministratorUser`, then we need to create a new User and + * Administrator record in Vendure for that user. + */ + async createAdministratorAndUser( + ctx: RequestContext, + config: { + strategy: string; + externalIdentifier: string; + identifier: string; + emailAddress?: string; + firstName?: string; + lastName?: string; + roles: Role[]; + }, + ) { + const newUser = new User({ + identifier: config.identifier, + roles: config.roles, + verified: true, + }); + + const authMethod = await this.connection.manager.save( + new ExternalAuthenticationMethod({ + externalIdentifier: config.externalIdentifier, + strategy: config.strategy, + }), + ); + + newUser.authenticationMethods = [authMethod]; + const savedUser = await this.connection.manager.save(newUser); + + const administrator = await this.connection.manager.save( + new Administrator({ + emailAddress: config.emailAddress, + firstName: config.firstName, + lastName: config.lastName, + user: savedUser, + }), + ); + + return newUser; + } + + private findUser(strategy: string, externalIdentifier: string): Promise { + return this.connection + .getRepository(User) + .createQueryBuilder('user') + .leftJoinAndSelect('user.authenticationMethods', 'authMethod') + .where('authMethod.strategy = :strategy', { strategy }) + .andWhere('authMethod.externalIdentifier = :externalIdentifier', { externalIdentifier }) + .andWhere('user.deletedAt IS NULL') + .getOne(); + } } diff --git a/packages/dev-server/test-plugins/google-auth/google-authentication-strategy.ts b/packages/dev-server/test-plugins/google-auth/google-authentication-strategy.ts index 7c17577428..6ab8b523e2 100644 --- a/packages/dev-server/test-plugins/google-auth/google-authentication-strategy.ts +++ b/packages/dev-server/test-plugins/google-auth/google-authentication-strategy.ts @@ -46,7 +46,7 @@ export class GoogleAuthenticationStrategy implements AuthenticationStrategy