UNPKG

@jvhaile/loopback4-helper

Version:
116 lines (96 loc) 4.61 kB
import {AuthenticationBindings, AuthenticationMetadata, AuthenticationStrategy} from '@loopback/authentication'; import {Getter, service} from '@loopback/core'; import {HttpErrors, Request} from '@loopback/rest'; import {securityId, UserProfile} from '@loopback/security'; import {inject} from "@loopback/core"; import {AuthenticationService} from "./services/authentication.service"; import {RepositoryBindings} from "./keys"; import {BaseUserRepository} from "./repositories/base.user.repository"; import {BaseSessionRepository} from "./repositories/base.session.repository"; import {BaseClientRepository} from "./repositories/base.client.repository"; import {BaseClient} from "./models/base.client.model"; import {BaseSession} from "./models/base.session.model"; import {BaseUser} from "./models/base.user.model"; export type CurrentUser<U extends BaseUser = BaseUser, C extends BaseClient = BaseClient, S extends BaseSession = BaseSession> = { [securityId]: string, client: C, user: U, session: S, } export class AuthStrategy implements AuthenticationStrategy { readonly name = 'remit'; constructor( @service(AuthenticationService) readonly authenticationService: AuthenticationService, @inject.getter(AuthenticationBindings.METADATA) readonly getMetaData: Getter<AuthenticationMetadata>, @inject(RepositoryBindings.USER_REPOSITORY) readonly userRepository: BaseUserRepository, @inject(RepositoryBindings.CLIENT_REPOSITORY) readonly clientRepository: BaseClientRepository, @inject(RepositoryBindings.SESSION_REPOSITORY) readonly sessionRepository: BaseSessionRepository, ) { } async getMeta(): Promise<AuthenticationMetadata | null> { const meta: any = await this.getMetaData(); if (meta) { if (meta.forEach) { return meta.length ? meta[0] : null; } return meta; } return null; } async authenticate(request: Request): Promise<UserProfile | undefined> { const apiKey = request.header('apiKey'); // @ts-ignore const userAgent: UserAgent = request.headers['parsed-user-agent']; const client = await this.validateClient(apiKey); const meta = await this.getMeta(); if (meta?.options?.passUserAuth) return { [securityId]: '', client: client }; const token = this.extractTokenFromHeader(request); const session = await this.authenticationService.validateTokenAndGetSession(token, client, userAgent); const user = await this.userRepository.findById(session.userId) const allowedRoles = meta?.options?.allowedRoles ?? []; if (allowedRoles && allowedRoles.length && !allowedRoles.includes(user.role ?? '')) { throw new HttpErrors.Forbidden("Access denied, account is not authorized for this action!"); } return { [securityId]: session.id!, client, session, user }; } async validateClient(apiKey: any): Promise<BaseClient> { //todo client platform verification if (!apiKey) throw new HttpErrors.Unauthorized(`API Key required.`); if (typeof apiKey != "string") throw new HttpErrors.Unauthorized(`Invalid API Key format.`); const client = await this.clientRepository.findOne({where: {apiKey}}); if (!client) throw new HttpErrors.Unauthorized(`Invalid API Key.`); if (!client.active) throw new HttpErrors.Unauthorized(`API Key is disabled.`); return client; } extractTokenFromHeader(request: Request): string { if (!request.headers.authorization) { throw new HttpErrors.Unauthorized(`Authorization header not found.`); } const authHeaderValue = request.headers.authorization; if (!authHeaderValue.startsWith('Bearer')) { throw new HttpErrors.Unauthorized( `Authorization header is not of type 'Bearer'.`, ); } //split the string into 2 parts : 'Bearer ' and the `xxx.yyy.zzz` const parts = authHeaderValue.split(' '); if (parts.length !== 2) throw new HttpErrors.Unauthorized( `Authorization header value has too many parts. It must follow the pattern: 'Bearer xx.yy.zz' where xx.yy.zz is a valid JWT token.`, ); return parts[1]; } }