2023-01-23 23:13:42 -05:00
|
|
|
import { SystemConfig } from '@app/infra/db/entities';
|
|
|
|
|
import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common';
|
|
|
|
|
import { AuthType, AuthUserDto, ICryptoRepository, LoginResponseDto } from '../auth';
|
|
|
|
|
import { AuthCore } from '../auth/auth.core';
|
|
|
|
|
import { INITIAL_SYSTEM_CONFIG, ISystemConfigRepository } from '../system-config';
|
|
|
|
|
import { IUserRepository, UserCore, UserResponseDto } from '../user';
|
|
|
|
|
import { OAuthCallbackDto, OAuthConfigDto } from './dto';
|
|
|
|
|
import { OAuthCore } from './oauth.core';
|
|
|
|
|
import { OAuthConfigResponseDto } from './response-dto';
|
2023-01-27 20:50:07 +00:00
|
|
|
import { IUserTokenRepository } from '@app/domain/user-token';
|
2023-01-23 23:13:42 -05:00
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class OAuthService {
|
|
|
|
|
private authCore: AuthCore;
|
|
|
|
|
private oauthCore: OAuthCore;
|
|
|
|
|
private userCore: UserCore;
|
|
|
|
|
|
|
|
|
|
private readonly logger = new Logger(OAuthService.name);
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
|
|
|
|
|
@Inject(ISystemConfigRepository) configRepository: ISystemConfigRepository,
|
|
|
|
|
@Inject(IUserRepository) userRepository: IUserRepository,
|
2023-01-27 20:50:07 +00:00
|
|
|
@Inject(IUserTokenRepository) userTokenRepository: IUserTokenRepository,
|
2023-01-23 23:13:42 -05:00
|
|
|
@Inject(INITIAL_SYSTEM_CONFIG) initialConfig: SystemConfig,
|
|
|
|
|
) {
|
2023-01-27 20:50:07 +00:00
|
|
|
this.authCore = new AuthCore(cryptoRepository, configRepository, userTokenRepository, initialConfig);
|
|
|
|
|
this.userCore = new UserCore(userRepository, cryptoRepository);
|
2023-01-23 23:13:42 -05:00
|
|
|
this.oauthCore = new OAuthCore(configRepository, initialConfig);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
generateConfig(dto: OAuthConfigDto): Promise<OAuthConfigResponseDto> {
|
|
|
|
|
return this.oauthCore.generateConfig(dto);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async login(dto: OAuthCallbackDto, isSecure: boolean): Promise<{ response: LoginResponseDto; cookie: string[] }> {
|
|
|
|
|
const profile = await this.oauthCore.callback(dto.url);
|
|
|
|
|
|
|
|
|
|
this.logger.debug(`Logging in with OAuth: ${JSON.stringify(profile)}`);
|
|
|
|
|
let user = await this.userCore.getByOAuthId(profile.sub);
|
|
|
|
|
|
|
|
|
|
// link existing user
|
|
|
|
|
if (!user) {
|
|
|
|
|
const emailUser = await this.userCore.getByEmail(profile.email);
|
|
|
|
|
if (emailUser) {
|
|
|
|
|
user = await this.userCore.updateUser(emailUser, emailUser.id, { oauthId: profile.sub });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// register new user
|
|
|
|
|
if (!user) {
|
|
|
|
|
if (!this.oauthCore.isAutoRegisterEnabled()) {
|
|
|
|
|
this.logger.warn(
|
|
|
|
|
`Unable to register ${profile.email}. To enable set OAuth Auto Register to true in admin settings.`,
|
|
|
|
|
);
|
|
|
|
|
throw new BadRequestException(`User does not exist and auto registering is disabled.`);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.logger.log(`Registering new user: ${profile.email}/${profile.sub}`);
|
|
|
|
|
user = await this.userCore.createUser(this.oauthCore.asUser(profile));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.authCore.createLoginResponse(user, AuthType.OAUTH, isSecure);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async link(user: AuthUserDto, dto: OAuthCallbackDto): Promise<UserResponseDto> {
|
|
|
|
|
const { sub: oauthId } = await this.oauthCore.callback(dto.url);
|
|
|
|
|
const duplicate = await this.userCore.getByOAuthId(oauthId);
|
|
|
|
|
if (duplicate && duplicate.id !== user.id) {
|
|
|
|
|
this.logger.warn(`OAuth link account failed: sub is already linked to another user (${duplicate.email}).`);
|
|
|
|
|
throw new BadRequestException('This OAuth account has already been linked to another user.');
|
|
|
|
|
}
|
|
|
|
|
return this.userCore.updateUser(user, user.id, { oauthId });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async unlink(user: AuthUserDto): Promise<UserResponseDto> {
|
|
|
|
|
return this.userCore.updateUser(user, user.id, { oauthId: '' });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async getLogoutEndpoint(): Promise<string | null> {
|
|
|
|
|
return this.oauthCore.getLogoutEndpoint();
|
|
|
|
|
}
|
|
|
|
|
}
|