Skip to content

Commit

Permalink
feat(core): Add admin helpers to ExternalAuthenticationService
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbromley committed Jun 29, 2020
1 parent 5a95476 commit 3456ffb
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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<User | undefined> {
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<User | undefined> {
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<User | undefined> {
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.
*/
Expand Down Expand Up @@ -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<User | undefined> {
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export class GoogleAuthenticationStrategy implements AuthenticationStrategy<Goog
if (!payload || !payload.email) {
return false;
}
const user = await this.externalAuthenticationService.findUser(this.name, payload.sub);
const user = await this.externalAuthenticationService.findCustomerUser(this.name, payload.sub);
if (user) {
return user;
}
Expand Down

0 comments on commit 3456ffb

Please sign in to comment.