0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

Merge pull request #5585 from logto-io/yemq-log-8483-update-cloud-client-API-call

chore: update cloud version and the way to call cloud custom JWT API
This commit is contained in:
Darcy Ye 2024-03-29 14:32:55 +08:00 committed by GitHub
commit a77fd3f97f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 100 additions and 44 deletions

View file

@ -48,6 +48,6 @@
"access": "public"
},
"devDependencies": {
"@logto/cloud": "0.2.5-81f06ea"
"@logto/cloud": "0.2.5-2a777a1"
}
}

View file

@ -28,7 +28,7 @@
"@fontsource/roboto-mono": "^5.0.0",
"@jest/types": "^29.5.0",
"@logto/app-insights": "workspace:^1.4.0",
"@logto/cloud": "0.2.5-81f06ea",
"@logto/cloud": "0.2.5-2a777a1",
"@logto/connector-kit": "workspace:^2.1.0",
"@logto/core-kit": "workspace:^2.3.0",
"@logto/language-kit": "workspace:^1.1.0",

View file

@ -91,7 +91,7 @@
"zod": "^3.22.4"
},
"devDependencies": {
"@logto/cloud": "0.2.5-81f06ea",
"@logto/cloud": "0.2.5-2a777a1",
"@silverhand/eslint-config": "5.0.0",
"@silverhand/ts-config": "5.0.0",
"@types/debug": "^4.1.7",

View file

@ -14,7 +14,9 @@ import {
logtoCookieKey,
type LogtoUiCookie,
LogtoJwtTokenKey,
LogtoJwtTokenPath,
ExtraParamsKey,
type Json,
} from '@logto/schemas';
import { conditional, trySafe, tryThat } from '@silverhand/essentials';
import i18next from 'i18next';
@ -204,6 +206,7 @@ export default function initOidc(
},
},
extraParams: Object.values(ExtraParamsKey),
extraTokenClaims: async (ctx, token) => {
const { isDevFeaturesEnabled, isCloud } = EnvSet.values;
@ -239,6 +242,12 @@ export default function initOidc(
const client = await cloudConnection.getClient();
const commonPayload = {
script,
envVars,
token: readOnlyToken,
};
// We pass context to the cloud API only when it is a user's access token.
const logtoUserInfo = conditional(
!isTokenClientCredentials &&
@ -248,11 +257,17 @@ export default function initOidc(
// `context` parameter is only eligible for user's access token for now.
return await client.post(`/api/services/custom-jwt`, {
body: {
script,
envVars,
token: readOnlyToken,
...conditional(logtoUserInfo && { context: { user: logtoUserInfo } }),
body: isTokenClientCredentials
? {
...commonPayload,
tokenType: LogtoJwtTokenPath.ClientCredentials,
}
: {
...commonPayload,
tokenType: LogtoJwtTokenPath.AccessToken,
// TODO (LOG-8555): the newly added `UserProfile` type includes undefined fields and can not be directly assigned to `Json` type. And the `undefined` fields should be removed by zod guard.
// eslint-disable-next-line no-restricted-syntax
context: { user: logtoUserInfo as Record<string, Json> },
},
});
} catch {

View file

@ -17,6 +17,9 @@ import {
LogtoJwtTokenKey,
LogtoJwtTokenPath,
jsonObjectGuard,
type CustomJwtFetcher,
jwtCustomizerTestRequestBodyGuard,
type JwtCustomizerTestRequestBody,
} from '@logto/schemas';
import { adminTenantId } from '@logto/schemas';
import { ResponseError } from '@withtyped/client';
@ -50,6 +53,37 @@ const getJwtTokenKeyAndBody = (tokenPath: LogtoJwtTokenPath, body: unknown) => {
};
};
/**
* Transpile the request body of the JWT customizer test API to the request body of the Cloud JWT customizer test API.
*
* @param body Core JWT customizer test API request body.
* @returns Request body of the Cloud JWT customizer test API.
*/
const transpileJwtCustomizerTestRequestBody = (
body: JwtCustomizerTestRequestBody
): CustomJwtFetcher => {
const { tokenType, payload } = body;
/**
* We have to deal with the `tokenType` and `payload` at the same time since they are put together as one of the discriminated union type.
* Otherwise the type inference will not work as expected.
*/
if (tokenType === LogtoJwtTokenPath.AccessToken) {
const { tokenSample: token, contextSample: context, ...rest } = payload;
return {
tokenType,
token,
context,
...rest,
};
}
const { tokenSample: token, contextSample, ...rest } = payload;
return {
tokenType,
token,
...rest,
};
};
/**
* Remove actual values of the private keys from response.
* @param type Logto config key DB column name. Values are either `oidc.privateKeys` or `oidc.cookieKeys`.
@ -314,41 +348,18 @@ export default function logtoConfigRoutes<T extends AuthedRouter>(
* 1. no `script` provided.
* 2. no `tokenSample` provided.
*/
body: z.discriminatedUnion('tokenType', [
z.object({
tokenType: z.literal(LogtoJwtTokenPath.AccessToken),
payload: accessTokenJwtCustomizerGuard.required({
script: true,
tokenSample: true,
}),
}),
z.object({
tokenType: z.literal(LogtoJwtTokenPath.ClientCredentials),
payload: clientCredentialsJwtCustomizerGuard.required({
script: true,
tokenSample: true,
}),
}),
]),
body: jwtCustomizerTestRequestBodyGuard,
response: jsonObjectGuard,
status: [200, 400, 403, 422],
}),
async (ctx, next) => {
const {
body: {
payload: { tokenSample, contextSample, ...rest },
},
} = ctx.guard;
const { body } = ctx.guard;
const client = await cloudConnection.getClient();
try {
ctx.body = await client.post(`/api/services/custom-jwt`, {
body: {
...rest,
token: tokenSample,
context: contextSample,
},
body: transpileJwtCustomizerTestRequestBody(body),
});
} catch (error: unknown) {
/**

View file

@ -3,7 +3,11 @@ import { z } from 'zod';
import { Roles, UserSsoIdentities, Organizations } from '../db-entries/index.js';
import { jsonObjectGuard, mfaFactorsGuard } from '../foundations/index.js';
import { jwtCustomizerGuard } from './logto-config/index.js';
import {
jwtCustomizerGuard,
accessTokenJwtCustomizerGuard,
clientCredentialsJwtCustomizerGuard,
} from './logto-config/index.js';
import { scopeResponseGuard } from './scope.js';
import { userInfoGuard } from './user.js';
@ -37,6 +41,29 @@ export enum LogtoJwtTokenPath {
ClientCredentials = 'client-credentials',
}
/**
* This guard is for the core JWT customizer testing API request body guard.
*/
export const jwtCustomizerTestRequestBodyGuard = z.discriminatedUnion('tokenType', [
z.object({
tokenType: z.literal(LogtoJwtTokenPath.AccessToken),
payload: accessTokenJwtCustomizerGuard.required({
script: true,
tokenSample: true,
contextSample: true,
}),
}),
z.object({
tokenType: z.literal(LogtoJwtTokenPath.ClientCredentials),
payload: clientCredentialsJwtCustomizerGuard.required({
script: true,
tokenSample: true,
}),
}),
]);
export type JwtCustomizerTestRequestBody = z.infer<typeof jwtCustomizerTestRequestBodyGuard>;
/**
* This guard is for cloud API use (request body guard).
* Since the cloud API will be use by both testing and production, should keep the fields as general as possible.

View file

@ -1235,8 +1235,8 @@ importers:
version: 3.22.4
devDependencies:
'@logto/cloud':
specifier: 0.2.5-81f06ea
version: 0.2.5-81f06ea(zod@3.22.4)
specifier: 0.2.5-2a777a1
version: 0.2.5-2a777a1(zod@3.22.4)
'@rollup/plugin-commonjs':
specifier: ^25.0.0
version: 25.0.7(rollup@4.12.0)
@ -2715,8 +2715,8 @@ importers:
specifier: workspace:^1.4.0
version: link:../app-insights
'@logto/cloud':
specifier: 0.2.5-81f06ea
version: 0.2.5-81f06ea(zod@3.22.4)
specifier: 0.2.5-2a777a1
version: 0.2.5-2a777a1(zod@3.22.4)
'@logto/connector-kit':
specifier: workspace:^2.1.0
version: link:../toolkit/connector-kit
@ -3202,8 +3202,8 @@ importers:
version: 3.22.4
devDependencies:
'@logto/cloud':
specifier: 0.2.5-81f06ea
version: 0.2.5-81f06ea(zod@3.22.4)
specifier: 0.2.5-2a777a1
version: 0.2.5-2a777a1(zod@3.22.4)
'@silverhand/eslint-config':
specifier: 5.0.0
version: 5.0.0(eslint@8.44.0)(prettier@3.0.0)(typescript@5.3.3)
@ -7647,8 +7647,8 @@ packages:
jose: 5.2.2
dev: true
/@logto/cloud@0.2.5-81f06ea(zod@3.22.4):
resolution: {integrity: sha512-7u2VY8qlRoaheWDEbHdoFmQP9MbloKuuCwbz1jk+Wrn2EE1v+tgixVK/MiyFaAN5mLAVLAlCVQ00JIabw+g6YA==}
/@logto/cloud@0.2.5-2a777a1(zod@3.22.4):
resolution: {integrity: sha512-RnU13Hrv5phYtIjVHDo0Ik1ZFvEOT5XBdQ0fDOHBFuGH+1Xd4X4HK79Mm5iC5JMM7KxxuH7bb6lStCvsOkUUYw==}
engines: {node: ^20.9.0}
dependencies:
'@silverhand/essentials': 2.9.0
@ -18009,6 +18009,9 @@ packages:
resolution: {integrity: sha512-2GTVocFkwblV/TIg9AmT7TI2fO4xdWkyN8aFUEVtiVNWt96GTR3FgQyHFValfCbcj1k9Xf962Ws2hYXYUr9k1Q==}
engines: {node: '>= 12.0.0'}
hasBin: true
peerDependenciesMeta:
'@parcel/core':
optional: true
dependencies:
'@parcel/config-default': 2.9.3(@parcel/core@2.9.3)(postcss@8.4.31)
'@parcel/core': 2.9.3