mirror of
https://github.com/logto-io/logto.git
synced 2024-12-16 20:26:19 -05:00
chore: update cloud version and the way to call cloud custom JWT API
This commit is contained in:
parent
961fd8ea99
commit
48cfdf51a3
7 changed files with 100 additions and 44 deletions
|
@ -48,6 +48,6 @@
|
|||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@logto/cloud": "0.2.5-81f06ea"
|
||||
"@logto/cloud": "0.2.5-2a777a1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,12 +257,18 @@ 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 {
|
||||
// TODO: Log the error
|
||||
|
|
|
@ -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) {
|
||||
/**
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
@ -18004,6 +18004,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
|
||||
|
|
Loading…
Reference in a new issue