mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 21:49:25 +08:00
fix: implement renewed JTI cache duration and update related tests
This commit is contained in:
parent
ddff4e9b7c
commit
0e169da245
@ -11,3 +11,4 @@ export const tokenPolicyRecordKey = 'token-policy-config';
|
|||||||
export const tokenPolicyCacheKey = 'auth:' + tokenPolicyRecordKey;
|
export const tokenPolicyCacheKey = 'auth:' + tokenPolicyRecordKey;
|
||||||
export const tokenPolicyCollectionName = 'tokenControlConfig';
|
export const tokenPolicyCollectionName = 'tokenControlConfig';
|
||||||
export const issuedTokensCollectionName = 'issuedTokens';
|
export const issuedTokensCollectionName = 'issuedTokens';
|
||||||
|
export const RENEWED_JTI_CACHE_MS = 10000;
|
||||||
|
@ -7,10 +7,11 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AuthErrorCode, BaseAuth } from '@nocobase/auth';
|
import { BaseAuth } from '@nocobase/auth';
|
||||||
import { Database, Model } from '@nocobase/database';
|
import { Database, Model } from '@nocobase/database';
|
||||||
import { MockServer, createMockServer } from '@nocobase/test';
|
import { MockServer, createMockServer } from '@nocobase/test';
|
||||||
import { AuthErrorType } from '@nocobase/auth';
|
import { AuthErrorType } from '@nocobase/auth';
|
||||||
|
import { RENEWED_JTI_CACHE_MS } from '../../constants';
|
||||||
function sleep(ms) {
|
function sleep(ms) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(resolve, ms);
|
setTimeout(resolve, ms);
|
||||||
@ -161,19 +162,47 @@ describe('auth', () => {
|
|||||||
expect(checkedUser.id).toEqual(user.id);
|
expect(checkedUser.id).toEqual(user.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('when call renew token with same jti multiple times, only one resolved', async () => {
|
it('when call renew token with same jti multiple times within 10s, the result is same', async () => {
|
||||||
const tokenInfo = await auth.tokenController.add({ userId: 1 });
|
const tokenInfo = await auth.tokenController.add({ userId: 1 });
|
||||||
const renewTasks = Array(15)
|
const renewTasks = Array(15)
|
||||||
.fill(null)
|
.fill(null)
|
||||||
.map(() => auth.tokenController.renew(tokenInfo.jti));
|
.map(() => auth.tokenController.renew(tokenInfo.jti));
|
||||||
const allSettled = await Promise.allSettled(renewTasks);
|
const results = await Promise.all(renewTasks);
|
||||||
const successTasks = allSettled.filter((result) => result.status === 'fulfilled');
|
expect(
|
||||||
expect(successTasks).toHaveLength(1);
|
results.every((result) => result.jti === results[0].jti && result.issuedTime === results[0].issuedTime),
|
||||||
const failedTasks = allSettled.filter(
|
).toBe(true);
|
||||||
(result) => result.status === 'rejected' && result.reason.code === AuthErrorCode.TOKEN_RENEW_FAILED,
|
|
||||||
);
|
|
||||||
expect(failedTasks).toHaveLength(14);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('after JTI is renewed for 10s, any further renewal should fail.', async () => {
|
||||||
|
const tokenInfo = await auth.tokenController.add({ userId: 1 });
|
||||||
|
await auth.tokenController.renew(tokenInfo.jti);
|
||||||
|
const renewedIntervals = [
|
||||||
|
RENEWED_JTI_CACHE_MS - 1000,
|
||||||
|
RENEWED_JTI_CACHE_MS - 2000,
|
||||||
|
RENEWED_JTI_CACHE_MS + 1000,
|
||||||
|
RENEWED_JTI_CACHE_MS + 2000,
|
||||||
|
];
|
||||||
|
const renewTasks = renewedIntervals.map(async (interval) => {
|
||||||
|
try {
|
||||||
|
await sleep(interval);
|
||||||
|
const result = await auth.tokenController.renew(tokenInfo.jti);
|
||||||
|
return [interval, 'resolved', result];
|
||||||
|
} catch (e) {
|
||||||
|
return [interval, 'rejected', e];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const results = await Promise.all(renewTasks);
|
||||||
|
expect(
|
||||||
|
results.every(([interval, status, result]) => {
|
||||||
|
if (interval < RENEWED_JTI_CACHE_MS) {
|
||||||
|
return status === 'resolved';
|
||||||
|
} else {
|
||||||
|
return status === 'rejected';
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('use token policy tokenExpirationTime as token expirein', async () => {
|
it('use token policy tokenExpirationTime as token expirein', async () => {
|
||||||
const config = await auth.tokenController.getConfig();
|
const config = await auth.tokenController.getConfig();
|
||||||
const { token } = await auth.signIn();
|
const { token } = await auth.signIn();
|
||||||
|
@ -21,7 +21,12 @@ import { randomUUID } from 'crypto';
|
|||||||
import ms from 'ms';
|
import ms from 'ms';
|
||||||
import Application from '@nocobase/server';
|
import Application from '@nocobase/server';
|
||||||
import Database, { Repository } from '@nocobase/database';
|
import Database, { Repository } from '@nocobase/database';
|
||||||
import { issuedTokensCollectionName, tokenPolicyCollectionName, tokenPolicyRecordKey } from '../constants';
|
import {
|
||||||
|
issuedTokensCollectionName,
|
||||||
|
tokenPolicyCollectionName,
|
||||||
|
tokenPolicyRecordKey,
|
||||||
|
RENEWED_JTI_CACHE_MS,
|
||||||
|
} from '../constants';
|
||||||
|
|
||||||
type TokenControlService = ITokenControlService;
|
type TokenControlService = ITokenControlService;
|
||||||
|
|
||||||
@ -119,7 +124,7 @@ export class TokenController implements TokenControlService {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (count === 1) {
|
if (count === 1) {
|
||||||
await this.cache.set(`jti-renewed-cahce:${jti}`, { jti: newId, issuedTime }, 20000);
|
await this.cache.set(`jti-renewed-cahce:${jti}`, { jti: newId, issuedTime }, RENEWED_JTI_CACHE_MS);
|
||||||
this.logger.info('jti renewed', { oldJti: jti, newJti: newId, issuedTime });
|
this.logger.info('jti renewed', { oldJti: jti, newJti: newId, issuedTime });
|
||||||
return { jti: newId, issuedTime };
|
return { jti: newId, issuedTime };
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user