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
JavaScript
;
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