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
43,240 changes: 19,808 additions & 23,432 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"got": "^11.8.6"
}
},
"tar": "^7.5.11",
"postman-request": {
"form-data": "4.0.4",
"qs": "^6.14.1"
Expand Down
18 changes: 9 additions & 9 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ $ npm install -g @sourceloop/cli
$ sl COMMAND
running command...
$ sl (-v|--version|version)
@sourceloop/cli/12.2.6 darwin-arm64 node-v20.20.2
@sourceloop/cli/13.0.0 darwin-arm64 node-v24.16.0
$ sl --help [COMMAND]
USAGE
$ sl COMMAND
Expand Down Expand Up @@ -105,7 +105,7 @@ OPTIONS
--templateVersion=templateVersion Template branch, tag, or version
```

_See code: [src/commands/angular/scaffold.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.2.6/src/commands/angular/scaffold.ts)_
_See code: [src/commands/angular/scaffold.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v13.0.0/src/commands/angular/scaffold.ts)_

## `sl autocomplete [SHELL]`

Expand Down Expand Up @@ -153,7 +153,7 @@ OPTIONS
--help show manual pages
```

_See code: [src/commands/cdk.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.2.6/src/commands/cdk.ts)_
_See code: [src/commands/cdk.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v13.0.0/src/commands/cdk.ts)_

## `sl extension [NAME]`

Expand All @@ -170,7 +170,7 @@ OPTIONS
--help show manual pages
```

_See code: [src/commands/extension.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.2.6/src/commands/extension.ts)_
_See code: [src/commands/extension.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v13.0.0/src/commands/extension.ts)_

## `sl help [COMMAND]`

Expand Down Expand Up @@ -211,7 +211,7 @@ DESCRIPTION
}
```

_See code: [src/commands/mcp.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.2.6/src/commands/mcp.ts)_
_See code: [src/commands/mcp.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v13.0.0/src/commands/mcp.ts)_

## `sl microservice [NAME]`

Expand Down Expand Up @@ -259,7 +259,7 @@ OPTIONS
Include sequelize as ORM in service
```

_See code: [src/commands/microservice.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.2.6/src/commands/microservice.ts)_
_See code: [src/commands/microservice.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v13.0.0/src/commands/microservice.ts)_

## `sl react:scaffold [NAME]`

Expand All @@ -280,7 +280,7 @@ OPTIONS
--templateVersion=templateVersion Template branch or version
```

_See code: [src/commands/react/scaffold.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.2.6/src/commands/react/scaffold.ts)_
_See code: [src/commands/react/scaffold.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v13.0.0/src/commands/react/scaffold.ts)_

## `sl scaffold [NAME]`

Expand All @@ -304,7 +304,7 @@ OPTIONS
--owner=owner owner of the repo
```

_See code: [src/commands/scaffold.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.2.6/src/commands/scaffold.ts)_
_See code: [src/commands/scaffold.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v13.0.0/src/commands/scaffold.ts)_

## `sl update`

Expand All @@ -318,7 +318,7 @@ OPTIONS
--help show manual pages
```

_See code: [src/commands/update.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v12.2.6/src/commands/update.ts)_
_See code: [src/commands/update.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/v13.0.0/src/commands/update.ts)_
<!-- commandsstop -->

---
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@
"@types/js-yaml": "^4.0.5",
"js-yaml": "^4.1.0",
"lodash": "^4.17.21",
"@sourceloop/core": "^20.0.0",
"@sourceloop/core": "^21.0.0",
"@sourceloop/cache": "^6.0.0",
"@sourceloop/feature-toggle": "^6.0.0",
"@sourceloop/audit-service": "^19.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../../../repositories';
import {ILogger, LOGGER} from '../../logger-extension';
import {IAuthUserWithPermissions} from '../keys';
import {checkIfTokenRevoked} from './utils/revoked-token-checker.util';

export class FacadesBearerAsymmetricTokenVerifyProvider implements Provider<VerifyFunction.BearerFn> {
constructor(
Expand All @@ -43,7 +44,12 @@ export class FacadesBearerAsymmetricTokenVerifyProvider implements Provider<Veri
*/
value(): VerifyFunction.BearerFn {
return async (token: string, req?: Request) => {
await this._checkIfTokenRevoked(token);
// Check if token has been revoked (fail-closed: errors propagate and deny request)
await checkIfTokenRevoked(
token,
this.revokedTokenRepository,
this.logger,
);
let user = await this._verifyTokenAndGetUser(token);
this._checkPasswordExpiry(user);
try {
Expand Down Expand Up @@ -90,27 +96,6 @@ export class FacadesBearerAsymmetricTokenVerifyProvider implements Provider<Veri
};
}

/**
* The function `_checkIfTokenRevoked` checks if a token is revoked and throws an error if it is.
* @param {string} token - The `token` parameter in the `_checkIfTokenRevoked` function is a string
* that represents the token being checked for revocation. This token is used to query the
* `revokedTokenRepository` to determine if it has been revoked. If the token is found to be revoked,
* an `
*/
private async _checkIfTokenRevoked(token: string): Promise<void> {
try {
const isRevoked = await this.revokedTokenRepository.get(token);
if (isRevoked?.token) {
throw new HttpErrors.Unauthorized('TokenRevoked');
}
} catch (error) {
if (HttpErrors.HttpError.prototype.isPrototypeOf(error)) {
throw error;
}
this.logger.error('Revoked token repository not available !');
}
}

/**
* The function `_verifyTokenAndGetUser` verifies a token, decodes it, retrieves the corresponding
* key, and then verifies the token's authenticity using the key.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import moment from 'moment';
import {RevokedTokenRepository} from '../../../repositories';
import {ILogger, LOGGER} from '../../logger-extension';
import {IAuthUserWithPermissions} from '../keys';
import {checkIfTokenRevoked} from './utils/revoked-token-checker.util';

export class FacadesBearerTokenVerifyProvider implements Provider<VerifyFunction.BearerFn> {
constructor(
Expand All @@ -31,23 +32,19 @@ export class FacadesBearerTokenVerifyProvider implements Provider<VerifyFunction
* The function verifies a bearer token, checks for token revocation, expiration, and password
* expiry, and returns the authenticated user.
* @returns The `value()` function returns a BearerFn function that verifies a token. Inside the
* function, it first checks if the token is revoked, then verifies the token using a JWT secret key.
* If the token is valid, it checks for password expiry and returns either an instance of
* `authUserModel` or the user object based on the availability of `authUserModel`.
* function, it first checks if the token is revoked using the shared utility (fail-closed: errors
* propagate and deny request), then verifies the token using a JWT secret key. If the token is
* valid, it checks for password expiry and returns either an instance of `authUserModel` or the
* user object based on the availability of `authUserModel`.
*/
value(): VerifyFunction.BearerFn {
return async (token: string, req?: Request) => {
try {
const isRevoked = await this.revokedTokenRepository.get(token);
if (isRevoked?.token) {
throw new HttpErrors.Unauthorized('TokenRevoked');
}
} catch (error) {
if (HttpErrors.HttpError.prototype.isPrototypeOf(error)) {
throw error;
}
this.logger.error('Revoked token repository not available !');
}
// Check if token has been revoked (fail-closed: errors propagate and deny request)
await checkIfTokenRevoked(
token,
this.revokedTokenRepository,
this.logger,
);

let user: IAuthUserWithPermissions;
try {
Expand Down
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);

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,39 @@
// 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.
*
* **Security posture (fail-closed)**: If the revoked token repository is unavailable
* (Redis down, timeout, connection errors), the error propagates and the request is
* denied. This ensures that logout always takes effect - if we cannot verify a token
* is not revoked, we reject it. This matches the established pattern from
* authentication-service's bearer-token-verify.provider.ts.
*
* @param token - The JWT token to check for revocation
* @param revokedTokenRepo - The repository to check for revoked tokens
* @param logger - Logger instance for security logging
* @throws {HttpErrors.Unauthorized} When the token has been revoked
* @throws When the revoked token repository is unavailable
*/
export async function checkIfTokenRevoked(
token: string,
revokedTokenRepo: RevokedTokenRepository,
logger: ILogger,
): Promise<void> {
const isRevoked = await revokedTokenRepo.get(token);
if (isRevoked?.token) {
logger.warn(`[SECURITY] Attempt to use revoked token detected`);
throw new HttpErrors.Unauthorized(AuthenticateErrorKeys.TokenRevoked);
}
}
Loading
Loading