UNPKG

very-express

Version:

based on json schema and openapi, generate Express RestApi server with mongoDb

109 lines (91 loc) 4.26 kB
// {{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; } }