UNPKG

lbx-jwt

Version:

Provides JWT authentication for loopback applications. Includes storing roles inside tokens and handling refreshing. Built-in reuse detection.

96 lines 5.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.JwtAuthenticationStrategy = void 0; const tslib_1 = require("tslib"); const authentication_1 = require("@loopback/authentication"); const core_1 = require("@loopback/core"); const rest_1 = require("@loopback/rest"); const two_factor_service_1 = require("./two-factor.service"); const keys_1 = require("../keys"); const repositories_1 = require("../repositories"); /** * The jwt authentication strategy. */ let JwtAuthenticationStrategy = class JwtAuthenticationStrategy { constructor(accessTokenService, metadataArray, baseUserRepository, forceTwoFactor, forceTwoFactorAllowedRoutes, twoFactorService) { this.accessTokenService = accessTokenService; this.metadataArray = metadataArray; this.baseUserRepository = baseUserRepository; this.forceTwoFactor = forceTwoFactor; this.forceTwoFactorAllowedRoutes = forceTwoFactorAllowedRoutes; this.twoFactorService = twoFactorService; // eslint-disable-next-line jsdoc/require-jsdoc this.name = 'jwt'; } // eslint-disable-next-line jsdoc/require-jsdoc async authenticate(request) { const token = this.extractTokenFromRequest(request); const userProfile = await this.accessTokenService.verifyToken(token); const user = await this.baseUserRepository.findById(userProfile.id); // eslint-disable-next-line typescript/strict-boolean-expressions if (user.requiresPasswordChange) { throw new rest_1.HttpErrors.BadRequest('This account needs to change his password before it can access this route.'); } await this.validate2FA(user, request); return userProfile; } /** * Checks if the request requires 2fa and validates accordingly. * @param user - The currently logged in user. * @param request - The request, is used to extract the two factor code from the custom header. */ async validate2FA(user, request) { var _a; if (this.forceTwoFactor && user.twoFactorEnabled != true && !this.forceTwoFactorAllowedRoutes.find(r => request.url === r || new URL(request.url).pathname === r)) { throw new rest_1.HttpErrors.BadRequest('This account needs to setup two factor authentication before it can access this route.'); } const metadata = this.metadataArray.find(m => m.strategy === this.name); // eslint-disable-next-line typescript/strict-boolean-expressions if (!((_a = metadata === null || metadata === void 0 ? void 0 : metadata.options) === null || _a === void 0 ? void 0 : _a['require2fa'])) { return; } // eslint-disable-next-line typescript/strict-boolean-expressions if (!this.forceTwoFactor && !user.twoFactorEnabled) { return; } const code = this.twoFactorService.extractCodeFromRequest(request); await this.twoFactorService.validateCode(user.id, code); } /** * Extracts the token from the given request. * @param request - The request to get the token from. * @returns The found token. An error otherwise. * @throws An Http-Unauthorized-Error when no token could be found. */ extractTokenFromRequest(request) { if (!request.headers.authorization) { throw new rest_1.HttpErrors.Unauthorized('Authorization header not found.'); } // for example : Bearer xxx.yyy.zzz const authHeaderValue = request.headers.authorization; if (!authHeaderValue.startsWith('Bearer')) { throw new rest_1.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 rest_1.HttpErrors.Unauthorized( // eslint-disable-next-line stylistic/max-len '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.'); } const token = parts[1]; return token; } }; exports.JwtAuthenticationStrategy = JwtAuthenticationStrategy; exports.JwtAuthenticationStrategy = JwtAuthenticationStrategy = tslib_1.__decorate([ tslib_1.__param(0, (0, core_1.inject)(keys_1.LbxJwtBindings.ACCESS_TOKEN_SERVICE)), tslib_1.__param(1, (0, core_1.inject)(authentication_1.AuthenticationBindings.METADATA)), tslib_1.__param(2, (0, core_1.inject)(keys_1.LbxJwtBindings.BASE_USER_REPOSITORY)), tslib_1.__param(3, (0, core_1.inject)(keys_1.LbxJwtBindings.FORCE_TWO_FACTOR)), tslib_1.__param(4, (0, core_1.inject)(keys_1.LbxJwtBindings.FORCE_TWO_FACTOR_ALLOWED_ROUTES)), tslib_1.__param(5, (0, core_1.inject)(keys_1.LbxJwtBindings.TWO_FACTOR_SERVICE)), tslib_1.__metadata("design:paramtypes", [Object, Array, repositories_1.BaseUserRepository, Boolean, Array, two_factor_service_1.TwoFactorService]) ], JwtAuthenticationStrategy); //# sourceMappingURL=jwt.auth.strategy.js.map