Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update cloud version and the way to call cloud custom JWT API #5585

Merged
merged 1 commit into from
Mar 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/connectors/connector-logto-email/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@
"access": "public"
},
"devDependencies": {
"@logto/cloud": "0.2.5-81f06ea"
"@logto/cloud": "0.2.5-2a777a1"
}
}
2 changes: 1 addition & 1 deletion packages/console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
27 changes: 21 additions & 6 deletions packages/core/src/oidc/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
logtoCookieKey,
type LogtoUiCookie,
LogtoJwtTokenKey,
LogtoJwtTokenPath,
ExtraParamsKey,
type Json,
} from '@logto/schemas';
import { conditional, trySafe, tryThat } from '@silverhand/essentials';
import i18next from 'i18next';
Expand Down Expand Up @@ -52,7 +54,7 @@
// Temporarily removed 'EdDSA' since it's not supported by browser yet
const supportedSigningAlgs = Object.freeze(['RS256', 'PS256', 'ES256', 'ES384', 'ES512'] as const);

export default function initOidc(

Check warning on line 57 in packages/core/src/oidc/init.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/core/src/oidc/init.ts#L57

[max-params] Function 'initOidc' has too many parameters (5). Maximum allowed is 4.
envSet: EnvSet,
queries: Queries,
libraries: Libraries,
Expand Down Expand Up @@ -204,6 +206,7 @@
},
},
extraParams: Object.values(ExtraParamsKey),

extraTokenClaims: async (ctx, token) => {
const { isDevFeaturesEnabled, isCloud } = EnvSet.values;

Expand Down Expand Up @@ -239,6 +242,12 @@

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 &&
Expand All @@ -248,15 +257,21 @@

// `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.

Check warning on line 268 in packages/core/src/oidc/init.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/core/src/oidc/init.ts#L268

[no-warning-comments] Unexpected 'todo' comment: 'TODO (LOG-8555): the newly added...'.
// eslint-disable-next-line no-restricted-syntax
context: { user: logtoUserInfo as Record<string, Json> },
},
});
} catch {
// TODO: Log the error

Check warning on line 274 in packages/core/src/oidc/init.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/core/src/oidc/init.ts#L274

[no-warning-comments] Unexpected 'todo' comment: 'TODO: Log the error'.
}
},
extraClientMetadata: {
Expand Down
63 changes: 37 additions & 26 deletions packages/core/src/routes/logto-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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) {
/**
Expand Down
29 changes: 28 additions & 1 deletion packages/schemas/src/types/jwt-customizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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.
Expand Down
19 changes: 11 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading