UNPKG

@jvhaile/loopback4-helper

Version:
114 lines (100 loc) 4.67 kB
import {injectable, BindingScope, service, inject} from '@loopback/core'; import {JWTService} from "./jwt.service"; import {HttpErrors} from "@loopback/rest"; import {WhereBuilder} from "@loopback/repository"; import {BaseUserRepository} from ".."; import {BaseUser} from ".."; import {BaseSessionRepository} from ".."; import {BaseSession, UserAgent} from ".."; import {BaseClient} from ".."; import {FirebaseService} from "./firebase.service"; import {FirebaseCredential, FirebaseUser, LoginResult, LoginSource} from "../types"; import {RepositoryBindings} from "../keys"; const _ = require('lodash') @injectable({scope: BindingScope.SINGLETON}) export class AuthenticationService { constructor( @service(FirebaseService) readonly firebaseService: FirebaseService, @service(JWTService) readonly jwtService: JWTService, @inject(RepositoryBindings.USER_REPOSITORY) readonly userRepository: BaseUserRepository, @inject(RepositoryBindings.SESSION_REPOSITORY) readonly sessionRepository: BaseSessionRepository, ) { } async signInWithIdToken<T extends BaseUser>( firebaseCredential: FirebaseCredential, userExtra: Partial<T>, source: LoginSource, allowCreateFromFirebase: boolean = false, allowedRoles?: string[], ): Promise<LoginResult<T>> { let newUser = false; const firebaseUser = await this.firebaseService.verifyIdTokenAndGetFirebaseUser( firebaseCredential.firebaseIdToken, ); let user = await this.userRepository.findOne({ where: new WhereBuilder().or([ new WhereBuilder().eq('firebaseUserId', firebaseUser.uid).build(), ...(firebaseUser.email ? [new WhereBuilder().and([ new WhereBuilder().eq('email', firebaseUser.email).build(), new WhereBuilder().eq('emailVerified', true).build(), new WhereBuilder().eq('role', "admin").build(), ]).build()] : []), ]).build() }); if (user == null) { newUser = true; if ((userExtra && userExtra.displayName) || (allowCreateFromFirebase && firebaseUser.displayName)) { const u = this.buildUser(firebaseUser, userExtra); user = await this.userRepository.create(u); } else { throw new HttpErrors.NotFound("User not found, Please register first!"); } } else { const u = this.buildUser(firebaseUser, user); await this.userRepository.updateById(user.id!, u); } if (allowedRoles && allowedRoles.length && !allowedRoles.includes(user.role ?? '')) { throw new HttpErrors.Forbidden("Access denied, account is not authorized for login!"); } const session = await this.sessionRepository.create(new BaseSession({ userId: user.id, clientId: source.clientId, fcmToken: firebaseCredential.fcmToken, userAgent: source.userAgent, loginMethod: firebaseCredential.loginMethod, })) const token = await this.jwtService.generateToken(session.toObject()); return { token, newUser, instance: await this.userRepository.findById(user.id!) as T, } } buildUser<T extends BaseUser>(firebaseUser: FirebaseUser, user?: Partial<T>): Partial<T> { const empty: Partial<T> = {}; return { ...(user ?? empty), firebaseUserId: firebaseUser.uid, displayName: user?.displayName || firebaseUser.displayName, email: user?.email || firebaseUser.email, emailVerified: user?.emailVerified || firebaseUser.emailVerified, phone: user?.phone || firebaseUser.phoneNumber, profilePhotoUrl: user?.profilePhotoUrl || firebaseUser.photoURL, }; } async validateTokenAndGetSession(token: any, client: BaseClient, userAgent: UserAgent): Promise<BaseSession> { const decodedToken = await this.jwtService.verifyToken(token); const session = new BaseSession(decodedToken); if (!session.active) throw new HttpErrors.Unauthorized(`Session is deactivated.`); if (session.clientId != client.id || !this.userAgentMatches(session.userAgent, userAgent)) { throw new HttpErrors.Unauthorized(`Session source mismatch.`); } return session; } userAgentMatches(userAgentA?: UserAgent, userAgentB?: UserAgent): boolean { return userAgentA?.platform == userAgentB?.platform; } }