mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
feat(core): refresh token rotation reuse interval (#1617)
* feat(core): refresh token rotation reuse interval * refactor: apply suggestions from code review Co-authored-by: Gao Sun <gao@silverhand.io>
This commit is contained in:
parent
708523ed52
commit
bb245adbb9
2 changed files with 33 additions and 8 deletions
|
@ -112,12 +112,19 @@ const loadOidcValues = async (issuer: string) => {
|
|||
const cookieKeys = await readCookieKeys();
|
||||
const privateKey = crypto.createPrivateKey(await readPrivateKey());
|
||||
const publicKey = crypto.createPublicKey(privateKey);
|
||||
/**
|
||||
* This interval helps to avoid concurrency issues when exchanging the rotating refresh token multiple times within a given timeframe.
|
||||
* During the leeway window (in seconds), the consumed refresh token will be considered as valid.
|
||||
* This is useful for distributed apps and serverless apps like Next.js, in which there is no shared memory.
|
||||
*/
|
||||
const refreshTokenReuseInterval = getEnv('OIDC_REFRESH_TOKEN_REUSE_INTERVAL', '3');
|
||||
|
||||
return Object.freeze({
|
||||
cookieKeys,
|
||||
privateKey,
|
||||
publicKey,
|
||||
issuer,
|
||||
refreshTokenReuseInterval: Number(refreshTokenReuseInterval),
|
||||
defaultIdTokenTtl: 60 * 60,
|
||||
defaultRefreshTokenTtl: 14 * 24 * 60 * 60,
|
||||
});
|
||||
|
|
|
@ -4,7 +4,8 @@ import {
|
|||
OidcModelInstancePayload,
|
||||
OidcModelInstances,
|
||||
} from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { conditional, Nullable } from '@silverhand/essentials';
|
||||
import dayjs from 'dayjs';
|
||||
import { sql, ValueExpression } from 'slonik';
|
||||
|
||||
import { buildInsertInto } from '@/database/insert-into';
|
||||
|
@ -16,15 +17,32 @@ export type QueryResult = Pick<OidcModelInstance, 'payload' | 'consumedAt'>;
|
|||
|
||||
const { table, fields } = convertToIdentifiers(OidcModelInstances);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
const withConsumed = <T>(data: T, consumedAt?: number | null): WithConsumed<T> => ({
|
||||
const isConsumed = (modelName: string, consumedAt: Nullable<number>): boolean => {
|
||||
if (!consumedAt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { refreshTokenReuseInterval } = envSet.values.oidc;
|
||||
|
||||
if (modelName !== 'RefreshToken' || !refreshTokenReuseInterval) {
|
||||
return Boolean(consumedAt);
|
||||
}
|
||||
|
||||
return dayjs(consumedAt).add(refreshTokenReuseInterval, 'seconds').isBefore(dayjs());
|
||||
};
|
||||
|
||||
const withConsumed = <T>(
|
||||
data: T,
|
||||
modelName: string,
|
||||
consumedAt: Nullable<number>
|
||||
): WithConsumed<T> => ({
|
||||
...data,
|
||||
...(consumedAt ? { consumed: true } : undefined),
|
||||
...(isConsumed(modelName, consumedAt) ? { consumed: true } : undefined),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
const convertResult = (result: QueryResult | null) =>
|
||||
conditional(result && withConsumed(result.payload, result.consumedAt));
|
||||
const convertResult = (result: QueryResult | null, modelName: string) =>
|
||||
conditional(result && withConsumed(result.payload, modelName, result.consumedAt));
|
||||
|
||||
export const upsertInstance = buildInsertInto<CreateOidcModelInstance>(OidcModelInstances, {
|
||||
onConflict: {
|
||||
|
@ -45,7 +63,7 @@ export const findPayloadById = async (modelName: string, id: string) => {
|
|||
and ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
return convertResult(result);
|
||||
return convertResult(result, modelName);
|
||||
};
|
||||
|
||||
export const findPayloadByPayloadField = async <
|
||||
|
@ -61,7 +79,7 @@ export const findPayloadByPayloadField = async <
|
|||
and ${fields.payload}->>${field}=${value}
|
||||
`);
|
||||
|
||||
return convertResult(result);
|
||||
return convertResult(result, modelName);
|
||||
};
|
||||
|
||||
export const consumeInstanceById = async (modelName: string, id: string) => {
|
||||
|
|
Loading…
Reference in a new issue