Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,27 @@ import {
} from 'loopback4-authentication';
import moment from 'moment-timezone';
import * as jose from 'node-jose';
import {JwtKeysRepository} from '../../../repositories';
import {JwtKeysRepository, RevokedTokenRepository} from '../../../repositories';
import {ILogger, LOGGER} from '../../logger-extension';
import {IAuthUserWithPermissions} from '../keys';
import {checkIfTokenRevoked} from './utils/revoked-token-checker.util';

export class ServicesBearerAsymmetricTokenVerifyProvider implements Provider<VerifyFunction.BearerFn> {
constructor(
@inject(LOGGER.LOGGER_INJECT) public logger: ILogger,
@repository(JwtKeysRepository)
public jwtKeysRepo: JwtKeysRepository,
@repository(RevokedTokenRepository)
public revokedTokenRepo: RevokedTokenRepository,
@inject(AuthenticationBindings.USER_MODEL, {optional: true})
public authUserModel?: Constructor<EntityWithIdentifier & IAuthUser>,
) {}

value(): VerifyFunction.BearerFn {
return async (token: string) => {
// Check if token has been revoked
await checkIfTokenRevoked(token, this.revokedTokenRepo, this.logger);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

where are we revoking this token actually ?


let user: IAuthUserWithPermissions;

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
// Copyright (c) 2023 Sourcefuse Technologies
// Copyright (c) 2023 Sourcefuse Technologies
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
import {Constructor, inject, Provider} from '@loopback/context';
import {repository} from '@loopback/repository';
import {HttpErrors} from '@loopback/rest';
import {verify} from 'jsonwebtoken';
import {
Expand All @@ -12,18 +13,25 @@ import {
VerifyFunction,
} from 'loopback4-authentication';
import moment from 'moment-timezone';
import {RevokedTokenRepository} from '../../../repositories';
import {ILogger, LOGGER} from '../../logger-extension';
import {IAuthUserWithPermissions} from '../keys';
import {checkIfTokenRevoked} from './utils/revoked-token-checker.util';

export class ServicesBearerTokenVerifyProvider implements Provider<VerifyFunction.BearerFn> {
constructor(
@inject(LOGGER.LOGGER_INJECT) public logger: ILogger,
@repository(RevokedTokenRepository)
public revokedTokenRepo: RevokedTokenRepository,
@inject(AuthenticationBindings.USER_MODEL, {optional: true})
public authUserModel?: Constructor<EntityWithIdentifier & IAuthUser>,
) {}

value(): VerifyFunction.BearerFn {
return async (token: string) => {
// Check if token has been revoked
await checkIfTokenRevoked(token, this.revokedTokenRepo, this.logger);

let user: IAuthUserWithPermissions;

try {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2023 Sourcefuse Technologies
//
// This software is released under the MIT License.
// https://opensource.org/licenses/MIT
import {HttpErrors} from '@loopback/rest';
import {RevokedTokenRepository} from '../../../../repositories';
import {AuthenticateErrorKeys} from '../../../../enums/auth-error-keys.enum';
import {ILogger} from '../../../../components/logger-extension';

/**
* Checks if a token has been revoked and throws an error if it has.
*
* This function queries the RevokedTokenRepository to determine if the given token
* has been revoked. If the token is found in the revoked list, an Unauthorized
* error is thrown, preventing the use of previously logged-out tokens.
*
* @param token - The JWT token to check for revocation
* @param revokedTokenRepo - The repository to check for revoked tokens
* @param logger - Logger instance for security and error logging
* @throws {HttpErrors.Unauthorized} When the token has been revoked
*/
export async function checkIfTokenRevoked(
token: string,
revokedTokenRepo: RevokedTokenRepository,
logger: ILogger,
): Promise<void> {
try {
const isRevoked = await revokedTokenRepo.get(token);
if (isRevoked?.token) {
logger.warn(`[SECURITY] Attempt to use revoked token detected`);
throw new HttpErrors.Unauthorized(AuthenticateErrorKeys.TokenRevoked);
}
} catch (error) {
// Re-throw HTTP errors (like our TokenRevoked error)
if (HttpErrors.HttpError.prototype.isPrototypeOf(error)) {
throw error;
}
// Log but don't fail on repository errors to allow graceful degradation
logger.error(
`[AUTH] Revoked token repository error during token verification.`,
error,
);
}
}
Loading