very-express
Version:
based on json schema and openapi, generate Express RestApi server with mongoDb
109 lines (91 loc) • 4.26 kB
text/typescript
// {{headerComment}}
import { UserEntity, User, UserWithRelations } from "./../../_models/UserModel.gen";
import { UserAuthProfilesEntity, UserAuthProfiles } from "./../../_models/UserAuthProfilesModel.gen";
import { VexRepository } from "./../../_types/vex";
import JWTService from "./../auth/JWTService.gen";
import OAuthProfileMap, { IProfile } from "./OAuthProfileMap.gen";
import VexDb from "./../VexDb.gen";
import utils from "../../_utils";
export default class OAuthStrategyService {
private get userRepo(): VexRepository<UserWithRelations> {
return VexDb.getRepository<UserWithRelations>(UserEntity);
}
private get uapRepo(): VexRepository<UserAuthProfiles> {
return VexDb.getRepository<UserAuthProfiles>(UserAuthProfilesEntity);
}
constructor() {}
public async verify(accessToken: string, refreshToken: string, profile: IProfile, done: (error: any, user?: any) => void): Promise<void> {
try {
let user: UserWithRelations;
const authUser = new OAuthProfileMap().map(profile);
const authProfile = authUser.userAuthProfiles?.[0];
// find user by oauthId + provider, or fall back to email
let existingUser: UserWithRelations | null = null;
if (authProfile?.oauthId && authProfile?.provider) {
const matchedProfile = await this.uapRepo.findOneWhere(
{ oauthId: authProfile.oauthId, provider: authProfile.provider },
);
if (matchedProfile?.userId) {
existingUser = await this.userRepo.findOne({ _id: matchedProfile.userId });
}
}
if (!existingUser && authUser.email) {
existingUser = await this.userRepo.findOneWhere({ email: authUser.email });
}
// 1. create new user
if (!existingUser) {
user = await this.createNewUser(authUser);
}
// 2. update existing user
else {
console.log("OAuthVerify UpdateUser");
user = await this.processExistingUser(authProfile, existingUser);
}
// set req.user
done(null, {
provider: profile.provider,
profile: new JWTService().sanitizeUser(user),
});
}
catch (err) {
return done(err);
}
}
private async createNewUser(authProfile: UserWithRelations): Promise<UserWithRelations> {
utils.log.info("OAuthVerify > createNewUser");
const user = await this.userRepo.create(authProfile);
// create auth profile row linked to the new user
if (authProfile.userAuthProfiles?.[0]) {
await this.uapRepo.create({
...authProfile.userAuthProfiles[0],
userId: user._id,
} as Partial<UserAuthProfiles>);
}
return user;
}
private async processExistingUser(incomingProfile: UserAuthProfiles | undefined, existingUser: User): Promise<User> {
utils.log.info("OAuthVerify > processExistingUser");
if (!incomingProfile) {
utils.log.error("OAuthVerify > Invalid OAuth Callback \"authProfile\"");
throw new Error("Invalid OAuth Callback \"authProfile\"");
}
// check if this provider/oauthId already tracked
const existing = await this.uapRepo.findOneWhere(
{ userId: existingUser._id, provider: incomingProfile.provider, oauthId: incomingProfile.oauthId },
);
if (!existing) {
// B.2. add new provider
utils.log.info("OAuthVerify > add new provider", incomingProfile.provider);
await this.uapRepo.create({
...incomingProfile,
userId: existingUser._id,
} as Partial<UserAuthProfiles>);
}
else if (existing.username !== incomingProfile.username) {
// B.3. update username if changed
utils.log.info("OAuthVerify > update username", incomingProfile.provider);
await this.uapRepo.update(existing._id, { username: incomingProfile.username });
}
return existingUser;
}
}