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:
parent
89ba8a1a0c
commit
ddd93dc977
5 changed files with 56 additions and 48 deletions
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue