Skip to content

Commit b90e01d

Browse files
prernagpprernagp90prernagp
authored
fix(authentication-service): added cache layer to the jwks implementation (#2241)
* fix(authentication-service): added cache layer to the jwks implementation * feat(authentication-service): added ttl to public key repo BREAKING CHANGE: issue-2034 --------- Co-authored-by: prernagp <prernagp90@gmail.com> Co-authored-by: prernagp <prerna.gupta@Prerna-SFIN372.local>
1 parent ba66f23 commit b90e01d

File tree

59 files changed

+1341
-388
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1341
-388
lines changed

packages/cli/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,4 +230,4 @@ OPTIONS
230230
```
231231

232232
_See code: [src/commands/update.ts](https://github.com/sourcefuse/loopback4-microservice-catalog/blob/master/packages/cli/src/commands/update.ts)_
233-
<!-- commandsstop -->
233+
<!-- commandsstop -->

packages/core/locales/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,5 +44,6 @@
4444
"Task completion cannot be done through the PATCH API.": "Task completion cannot be done through the PATCH API.",
4545
"Unauthorized": "Unauthorized",
4646
"No keys found": "No keys found",
47-
"[{\"keyword\"": "\"type\",\"dataPath\":\".valueB\",\"schemaPath\":\"#/properties/valueB/type\",\"params\":{\"type\":\"string\"},\"message\":\"should be string\"}]"
47+
"[{\"keyword\"": "\"type\",\"dataPath\":\".valueB\",\"schemaPath\":\"#/properties/valueB/type\",\"params\":{\"type\":\"string\"},\"message\":\"should be string\"}]",
48+
"JSON.stringify(validate.errors) invalid": "JSON.stringify(validate.errors) invalid"
4849
}

packages/core/src/components/bearer-verifier/component.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,18 @@
55
import {Binding, Component, inject, ProviderMap} from '@loopback/core';
66
import {Class, Model, Repository} from '@loopback/repository';
77
import {Strategies} from 'loopback4-authentication';
8-
import {JwtKeysRepository} from '../../repositories';
8+
import {JwtKeys, RevokedToken} from '../../models';
9+
import {PublicKeysRepository, RevokedTokenRepository} from '../../repositories';
910
import {ILogger, LOGGER} from '../logger-extension';
1011
import {
1112
BearerVerifierBindings,
1213
BearerVerifierConfig,
1314
BearerVerifierType,
1415
} from './keys';
15-
import {JwtKeys, RevokedToken} from './models';
1616
import {FacadesBearerAsymmetricTokenVerifyProvider} from './providers/facades-bearer-asym-token-verify.provider';
1717
import {FacadesBearerTokenVerifyProvider} from './providers/facades-bearer-token-verify.provider';
1818
import {ServicesBearerAsymmetricTokenVerifyProvider} from './providers/services-bearer-asym-token-verifier';
1919
import {ServicesBearerTokenVerifyProvider} from './providers/services-bearer-token-verify.provider';
20-
import {RevokedTokenRepository} from './repositories';
2120

2221
export class BearerVerifierComponent implements Component {
2322
constructor(
@@ -27,8 +26,8 @@ export class BearerVerifierComponent implements Component {
2726
) {
2827
this.providers = {};
2928

30-
this.repositories = [RevokedTokenRepository, JwtKeysRepository];
3129
this.models = [RevokedToken, JwtKeys];
30+
this.repositories = [RevokedTokenRepository, PublicKeysRepository];
3231

3332
if (this.config && this.config.type === BearerVerifierType.service) {
3433
this.providers[Strategies.Passport.BEARER_TOKEN_VERIFIER.key] =

packages/core/src/components/bearer-verifier/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,3 @@
44
// https://opensource.org/licenses/MIT
55
export * from './component';
66
export * from './keys';
7-
export * from './models';
8-
export * from './repositories';

packages/core/src/components/bearer-verifier/models/index.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

packages/core/src/components/bearer-verifier/providers/facades-bearer-asym-token-verify.provider.ts

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,22 @@ import {
1414
} from 'loopback4-authentication';
1515
import moment from 'moment';
1616
import * as jose from 'node-jose';
17-
import {JwtKeysRepository} from '../../../repositories';
17+
import {
18+
PublicKeysRepository,
19+
RevokedTokenRepository,
20+
} from '../../../repositories';
1821
import {ILogger, LOGGER} from '../../logger-extension';
1922
import {IAuthUserWithPermissions} from '../keys';
20-
import {RevokedTokenRepository} from '../repositories';
2123

2224
export class FacadesBearerAsymmetricTokenVerifyProvider
2325
implements Provider<VerifyFunction.BearerFn>
2426
{
2527
constructor(
2628
@repository(RevokedTokenRepository)
2729
public revokedTokenRepository: RevokedTokenRepository,
30+
@repository(PublicKeysRepository)
31+
public publicKeysRepository: PublicKeysRepository,
2832
@inject(LOGGER.LOGGER_INJECT) private readonly logger: ILogger,
29-
@repository(JwtKeysRepository)
30-
public jwtKeysRepo: JwtKeysRepository,
3133
@inject(AuthenticationBindings.USER_MODEL, {optional: true})
3234
public authUserModel?: Constructor<EntityWithIdentifier & IAuthUser>,
3335
) {}
@@ -44,10 +46,49 @@ export class FacadesBearerAsymmetricTokenVerifyProvider
4446
value(): VerifyFunction.BearerFn {
4547
return async (token: string, req?: Request) => {
4648
await this._checkIfTokenRevoked(token);
47-
const user = await this._verifyTokenAndGetUser(token);
49+
let user = await this._verifyTokenAndGetUser(token);
4850
this._checkPasswordExpiry(user);
51+
try {
52+
// Get the key that matches the token's kid
53+
const decoded = jwt.decode(token.trim(), {complete: true});
54+
if (!decoded) {
55+
throw new Error('Token is not valid');
56+
}
57+
const kid = decoded?.header.kid ?? '';
58+
59+
// Get the public key from the cache
60+
const key = await this.publicKeysRepository.get(kid);
61+
62+
if (!key) {
63+
throw new Error('Key not found for verification');
64+
}
4965

50-
return this.authUserModel ? new this.authUserModel(user) : user;
66+
// Convert the JWK to PEM format for verification
67+
const jwkKey = await jose.JWK.asKey(key.publicKey, 'pem');
68+
const pem = jwkKey.toPEM(false);
69+
70+
// Verify the token with the retrieved PEM-formatted public key
71+
user = jwt.verify(token, pem, {
72+
issuer: process.env.JWT_ISSUER,
73+
algorithms: ['RS256'],
74+
}) as IAuthUserWithPermissions;
75+
} catch (error) {
76+
this.logger.error(JSON.stringify(error));
77+
throw new HttpErrors.Unauthorized('TokenExpired');
78+
}
79+
80+
if (
81+
user.passwordExpiryTime &&
82+
moment().isSameOrAfter(moment(user.passwordExpiryTime))
83+
) {
84+
throw new HttpErrors.Unauthorized('PasswordExpiryError');
85+
}
86+
87+
if (this.authUserModel) {
88+
return new this.authUserModel(user);
89+
} else {
90+
return user;
91+
}
5192
};
5293
}
5394

@@ -94,7 +135,7 @@ export class FacadesBearerAsymmetricTokenVerifyProvider
94135
}
95136

96137
const kid = decoded?.header.kid;
97-
const key = await this.jwtKeysRepo.findOne({where: {keyId: kid}});
138+
const key = await this.publicKeysRepository.get(kid ?? '');
98139
if (!key) {
99140
throw new Error('Key not found for verification');
100141
}

packages/core/src/components/bearer-verifier/providers/facades-bearer-token-verify.provider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,16 @@ import {repository} from '@loopback/repository';
77
import {HttpErrors, Request} from '@loopback/rest';
88
import {verify} from 'jsonwebtoken';
99
import {
10-
VerifyFunction,
1110
AuthenticationBindings,
1211
EntityWithIdentifier,
1312
IAuthUser,
13+
VerifyFunction,
1414
} from 'loopback4-authentication';
1515
import moment from 'moment';
1616

17+
import {RevokedTokenRepository} from '../../../repositories';
1718
import {ILogger, LOGGER} from '../../logger-extension';
1819
import {IAuthUserWithPermissions} from '../keys';
19-
import {RevokedTokenRepository} from '../repositories';
2020

2121
export class FacadesBearerTokenVerifyProvider
2222
implements Provider<VerifyFunction.BearerFn>

packages/core/src/components/bearer-verifier/providers/services-bearer-token-verify.provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import {Constructor, inject, Provider} from '@loopback/context';
66
import {HttpErrors} from '@loopback/rest';
77
import {verify} from 'jsonwebtoken';
88
import {
9-
VerifyFunction,
109
AuthenticationBindings,
1110
EntityWithIdentifier,
1211
IAuthUser,
12+
VerifyFunction,
1313
} from 'loopback4-authentication';
1414
import moment from 'moment-timezone';
1515
import {ILogger, LOGGER} from '../../logger-extension';

packages/core/src/components/bearer-verifier/repositories/index.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

packages/core/src/components/bearer-verifier/repositories/jwt-keys.repository.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

0 commit comments

Comments
 (0)