0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-03-10 22:22:45 -05:00

refactor(core): add cache for cloud connection data

This commit is contained in:
Darcy Ye 2024-03-19 13:32:41 +08:00
parent 89ba8a1a0c
commit ddd93dc977
No known key found for this signature in database
GPG key ID: B46F4C07EDEFC610
5 changed files with 56 additions and 48 deletions

View file

@ -36,18 +36,24 @@ const accessTokenExpirationMargin = 60;
export class CloudConnectionLibrary {
private client?: Client<typeof router>;
private accessTokenCache?: { expiresAt: number; accessToken: string };
private credentialsCache?: CloudConnection;
constructor(private readonly logtoConfigs: LogtoConfigLibrary) {}
public getCloudConnectionData = async (): Promise<CloudConnection> => {
if (this.credentialsCache) {
return this.credentialsCache;
}
const { getCloudConnectionData: getCloudServiceM2mCredentials } = this.logtoConfigs;
const credentials = await getCloudServiceM2mCredentials();
const { cloudUrlSet, adminUrlSet } = EnvSet.values;
return {
this.credentialsCache = {
...credentials,
tokenEndpoint: appendPath(adminUrlSet.endpoint, 'oidc/token').toString(),
endpoint: appendPath(cloudUrlSet.endpoint, 'api').toString(),
};
return this.credentialsCache;
};
/**

View file

@ -6,7 +6,7 @@ import {
LogtoOidcConfigKey,
jwtCustomizerConfigGuard,
} from '@logto/schemas';
import type { LogtoOidcConfigType, LogtoJwtTokenKey } from '@logto/schemas';
import type { LogtoOidcConfigType, LogtoJwtTokenKey, CloudConnectionData } from '@logto/schemas';
import chalk from 'chalk';
import { z, ZodError } from 'zod';
@ -53,7 +53,7 @@ export const createLogtoConfigLibrary = ({
}
};
const getCloudConnectionData = async () => {
const getCloudConnectionData = async (): Promise<CloudConnectionData> => {
const { value } = await queryCloudConnectionData();
const result = cloudConnectionDataGuard.safeParse(value);
@ -94,7 +94,7 @@ export const createLogtoConfigLibrary = ({
});
}
return z.object({ value: jwtCustomizerConfigGuard[key] }).parse(rows[0]);
return z.object({ value: jwtCustomizerConfigGuard[key] }).parse(rows[0]).value;
};
return { getOidcConfigs, getCloudConnectionData, upsertJwtCustomizer, getJwtCustomizer };

View file

@ -210,7 +210,9 @@ export default function initOidc(
},
extraParams: [OIDCExtraParametersKey.InteractionMode],
extraTokenClaims: async (ctx, token) => {
if (!EnvSet.values.isDevFeaturesEnabled) {
const { isDevFeaturesEnabled, isCloud } = EnvSet.values;
// No cloud connection for OSS version, skip.
if (!isDevFeaturesEnabled || !isCloud) {
return;
}
@ -226,48 +228,49 @@ export default function initOidc(
}
}
const {
value: { script, envVars },
} = (await trySafe(
logtoConfigs.getJwtCustomizer(
isTokenClientCredentials
? LogtoJwtTokenKey.ClientCredentials
: LogtoJwtTokenKey.AccessToken
)
)) ?? { value: {} };
const { script, envVars } =
(await trySafe(
logtoConfigs.getJwtCustomizer(
isTokenClientCredentials
? LogtoJwtTokenKey.ClientCredentials
: LogtoJwtTokenKey.AccessToken
)
)) ?? {};
if (script) {
// Wait for cloud API to be ready and we can use cloud connection client to request the API.
const client = await cloudConnection.getClient();
// We pass context to the cloud API only when it is a user's access token.
const logtoUserInfo = conditional(
!isTokenClientCredentials &&
token.accountId &&
(await libraries.jwtCustomizers.getUserContext(token.accountId))
);
/**
* `token` and `context` can not be assigned to Record<string, Json> according to the type inference,
* use request body guard to ensure the type.
*
* Use direct type casting to avoid the type inference issue since if the type is not correct the client
* will throw an Zod type error, there is no need to implement the zod guard and error handling here.
*/
// eslint-disable-next-line no-restricted-syntax
const payload = {
script,
envVars,
token,
context: conditional(logtoUserInfo && { user: logtoUserInfo }),
} as unknown as CustomJwtFetcher;
return (
(await trySafe(
client.post(`/api/services/custom-jwt`, {
body: payload,
})
)) ?? {}
);
if (!script) {
return;
}
// Wait for cloud API to be ready and we can use cloud connection client to request the API.
const client = await cloudConnection.getClient();
// We pass context to the cloud API only when it is a user's access token.
const logtoUserInfo = conditional(
!isTokenClientCredentials &&
token.accountId &&
(await libraries.jwtCustomizers.getUserContext(token.accountId))
);
/**
* `token` and `context` can not be assigned to Record<string, Json> according to the type inference,
* use request body guard to ensure the type.
*
* Use direct type casting to avoid the type inference issue since if the type is not correct the client
* will throw an Zod type error, there is no need to implement the zod guard and error handling here.
*/
// eslint-disable-next-line no-restricted-syntax
const payload = {
script,
envVars,
token,
context: conditional(logtoUserInfo && { user: logtoUserInfo }),
} as unknown as CustomJwtFetcher;
return (
(await trySafe(
client.post(`/api/services/custom-jwt`, {
body: payload,
})
)) ?? {}
);
},
extraClientMetadata: {
properties: Object.values(CustomClientMetadataKey),

View file

@ -270,7 +270,7 @@ describe('configs routes', () => {
it('GET /configs/jwt-customizer/:tokenType should return the record', async () => {
logtoConfigLibraries.getJwtCustomizer.mockResolvedValueOnce(
mockJwtCustomizerConfigForAccessToken
mockJwtCustomizerConfigForAccessToken.value
);
const response = await routeRequester.get('/configs/jwt-customizer/access-token');
expect(response.status).toEqual(200);

View file

@ -257,12 +257,11 @@ export default function logtoConfigRoutes<T extends AuthedRouter>(
const {
params: { tokenTypePath },
} = ctx.guard;
const { value } = await getJwtCustomizer(
ctx.body = await getJwtCustomizer(
tokenTypePath === LogtoJwtTokenPath.AccessToken
? LogtoJwtTokenKey.AccessToken
: LogtoJwtTokenKey.ClientCredentials
);
ctx.body = value;
return next();
}
);