0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-01-13 21:30:30 -05:00

feat(console,schemas): add grant context to custom jwt (#6184)

This commit is contained in:
wangsijie 2024-07-08 14:51:24 +08:00 committed by GitHub
parent 4c6fb767f0
commit c1a01d6925
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 68 additions and 6 deletions

View file

@ -3,6 +3,7 @@ import fs from 'node:fs';
import { import {
accessTokenPayloadGuard, accessTokenPayloadGuard,
clientCredentialsPayloadGuard, clientCredentialsPayloadGuard,
jwtCustomizerGrantContextGuard,
jwtCustomizerUserContextGuard, jwtCustomizerUserContextGuard,
} from '@logto/schemas'; } from '@logto/schemas';
import prettier from 'prettier'; import prettier from 'prettier';
@ -13,6 +14,7 @@ const filePath = 'src/consts/jwt-customizer-type-definition.ts';
const typeIdentifiers = `export enum JwtCustomizerTypeDefinitionKey { const typeIdentifiers = `export enum JwtCustomizerTypeDefinitionKey {
JwtCustomizerUserContext = 'JwtCustomizerUserContext', JwtCustomizerUserContext = 'JwtCustomizerUserContext',
JwtCustomizerGrantContext = 'JwtCustomizerGrantContext',
AccessTokenPayload = 'AccessTokenPayload', AccessTokenPayload = 'AccessTokenPayload',
ClientCredentialsPayload = 'ClientCredentialsPayload', ClientCredentialsPayload = 'ClientCredentialsPayload',
EnvironmentVariables = 'EnvironmentVariables', EnvironmentVariables = 'EnvironmentVariables',
@ -43,6 +45,11 @@ const createJwtCustomizerTypeDefinitions = async () => {
'JwtCustomizerUserContext' 'JwtCustomizerUserContext'
); );
const jwtCustomizerGrantContextTypeDefinition = inferTsDefinitionFromZod(
jwtCustomizerGrantContextGuard,
'JwtCustomizerGrantContext'
);
const accessTokenPayloadTypeDefinition = inferTsDefinitionFromZod( const accessTokenPayloadTypeDefinition = inferTsDefinitionFromZod(
accessTokenPayloadGuard, accessTokenPayloadGuard,
'AccessTokenPayload' 'AccessTokenPayload'
@ -58,6 +65,8 @@ ${typeIdentifiers}
export const jwtCustomizerUserContextTypeDefinition = \`${jwtCustomizerUserContextTypeDefinition}\`; export const jwtCustomizerUserContextTypeDefinition = \`${jwtCustomizerUserContextTypeDefinition}\`;
export const jwtCustomizerGrantContextTypeDefinition = \`${jwtCustomizerGrantContextTypeDefinition}\`;
export const accessTokenPayloadTypeDefinition = \`${accessTokenPayloadTypeDefinition}\`; export const accessTokenPayloadTypeDefinition = \`${accessTokenPayloadTypeDefinition}\`;
export const clientCredentialsPayloadTypeDefinition = \`${clientCredentialsPayloadTypeDefinition}\`; export const clientCredentialsPayloadTypeDefinition = \`${clientCredentialsPayloadTypeDefinition}\`;

View file

@ -9,6 +9,7 @@ import * as styles from './index.module.scss';
export enum CardType { export enum CardType {
UserData = 'user_data', UserData = 'user_data',
GrantData = 'grant_data',
TokenData = 'token_data', TokenData = 'token_data',
FetchExternalData = 'fetch_external_data', FetchExternalData = 'fetch_external_data',
EnvironmentVariables = 'environment_variables', EnvironmentVariables = 'environment_variables',

View file

@ -16,6 +16,7 @@ import {
accessTokenPayloadTypeDefinition, accessTokenPayloadTypeDefinition,
clientCredentialsPayloadTypeDefinition, clientCredentialsPayloadTypeDefinition,
jwtCustomizerUserContextTypeDefinition, jwtCustomizerUserContextTypeDefinition,
jwtCustomizerGrantContextTypeDefinition,
} from '@/pages/CustomizeJwtDetails/utils/type-definitions'; } from '@/pages/CustomizeJwtDetails/utils/type-definitions';
import * as tabContentStyles from '../index.module.scss'; import * as tabContentStyles from '../index.module.scss';
@ -77,6 +78,24 @@ function InstructionTab({ isActive }: Props) {
/> />
</GuideCard> </GuideCard>
)} )}
{tokenType === LogtoJwtTokenKeyType.AccessToken && (
<GuideCard
name={CardType.GrantData}
isExpanded={expendCard === CardType.GrantData}
setExpanded={(expand) => {
setExpendCard(expand ? CardType.GrantData : undefined);
}}
>
<Editor
language="typescript"
className={styles.sampleCode}
value={jwtCustomizerGrantContextTypeDefinition}
height="400px"
theme="logto-dark"
options={typeDefinitionCodeEditorOptions}
/>
</GuideCard>
)}
<GuideCard <GuideCard
name={CardType.FetchExternalData} name={CardType.FetchExternalData}
isExpanded={expendCard === CardType.FetchExternalData} isExpanded={expendCard === CardType.FetchExternalData}

View file

@ -1,7 +1,9 @@
import type { import {
AccessTokenPayload, GrantType,
ClientCredentialsPayload, type AccessTokenPayload,
JwtCustomizerUserContext, type ClientCredentialsPayload,
type JwtCustomizerUserContext,
type JwtCustomizerGrantContext,
} from '@logto/schemas'; } from '@logto/schemas';
import { type EditorProps } from '@monaco-editor/react'; import { type EditorProps } from '@monaco-editor/react';
@ -27,6 +29,7 @@ declare interface CustomJwtClaims extends Record<string, any> {}
*/ */
declare type Context = { declare type Context = {
user: ${JwtCustomizerTypeDefinitionKey.JwtCustomizerUserContext}; user: ${JwtCustomizerTypeDefinitionKey.JwtCustomizerUserContext};
grant?: ${JwtCustomizerTypeDefinitionKey.JwtCustomizerGrantContext};
} }
declare type Payload = { declare type Payload = {
@ -199,8 +202,16 @@ const defaultUserContext: Partial<JwtCustomizerUserContext> = {
organizationRoles: [], organizationRoles: [],
}; };
const defaultGrantContext: Partial<JwtCustomizerGrantContext> = {
type: GrantType.TokenExchange,
subjectTokenContext: {
foo: 'bar',
},
};
export const defaultUserTokenContextData = { export const defaultUserTokenContextData = {
user: defaultUserContext, user: defaultUserContext,
grant: defaultGrantContext,
}; };
export const accessTokenPayloadTestModel: ModelSettings = { export const accessTokenPayloadTestModel: ModelSettings = {
@ -223,6 +234,6 @@ export const userContextTestModel: ModelSettings = {
language: 'json', language: 'json',
icon: <UserFileIcon />, icon: <UserFileIcon />,
name: 'user-token-context.json', name: 'user-token-context.json',
title: 'User data', title: 'Context data',
defaultValue: JSON.stringify(defaultUserTokenContextData, null, 2), defaultValue: JSON.stringify(defaultUserTokenContextData, null, 2),
}; };

View file

@ -3,6 +3,7 @@ import {
accessTokenPayloadTypeDefinition, accessTokenPayloadTypeDefinition,
clientCredentialsPayloadTypeDefinition, clientCredentialsPayloadTypeDefinition,
jwtCustomizerUserContextTypeDefinition, jwtCustomizerUserContextTypeDefinition,
jwtCustomizerGrantContextTypeDefinition,
} from '@/consts/jwt-customizer-type-definition'; } from '@/consts/jwt-customizer-type-definition';
import { type JwtCustomizerForm } from '../type'; import { type JwtCustomizerForm } from '../type';
@ -12,11 +13,14 @@ export {
accessTokenPayloadTypeDefinition, accessTokenPayloadTypeDefinition,
clientCredentialsPayloadTypeDefinition, clientCredentialsPayloadTypeDefinition,
jwtCustomizerUserContextTypeDefinition, jwtCustomizerUserContextTypeDefinition,
jwtCustomizerGrantContextTypeDefinition,
} from '@/consts/jwt-customizer-type-definition'; } from '@/consts/jwt-customizer-type-definition';
export const buildAccessTokenJwtCustomizerContextTsDefinition = () => { export const buildAccessTokenJwtCustomizerContextTsDefinition = () => {
return `declare ${jwtCustomizerUserContextTypeDefinition} return `declare ${jwtCustomizerUserContextTypeDefinition}
declare ${jwtCustomizerGrantContextTypeDefinition}
declare ${accessTokenPayloadTypeDefinition}`; declare ${accessTokenPayloadTypeDefinition}`;
}; };

View file

@ -30,6 +30,11 @@ const jwt_claims = {
title: 'User data', title: 'User data',
subtitle: 'Use `data.user` input parameter to provide vital user info.', subtitle: 'Use `data.user` input parameter to provide vital user info.',
}, },
grant_data: {
title: 'Grant data',
subtitle:
'Use `data.grant` input parameter to provide vital grant info, only available for token exchange.',
},
token_data: { token_data: {
title: 'Token data', title: 'Token data',
subtitle: 'Use `token` input parameter for current access token payload. ', subtitle: 'Use `token` input parameter for current access token payload. ',

View file

@ -10,6 +10,7 @@ import {
type UserSsoIdentity, type UserSsoIdentity,
} from '../../db-entries/index.js'; } from '../../db-entries/index.js';
import { mfaFactorsGuard, type MfaFactors } from '../../foundations/index.js'; import { mfaFactorsGuard, type MfaFactors } from '../../foundations/index.js';
import { GrantType } from '../oidc-config.js';
import { scopeResponseGuard, type ScopeResponse } from '../scope.js'; import { scopeResponseGuard, type ScopeResponse } from '../scope.js';
import { userInfoGuard, type UserInfo } from '../user.js'; import { userInfoGuard, type UserInfo } from '../user.js';
@ -67,11 +68,23 @@ export const jwtCustomizerUserContextGuard = userInfoGuard.extend({
.array(), .array(),
}) satisfies ZodType<JwtCustomizerUserContext>; }) satisfies ZodType<JwtCustomizerUserContext>;
export const jwtCustomizerGrantContextGuard = z.object({
type: z.literal(GrantType.TokenExchange), // Only support token exchange for now
subjectTokenContext: jsonObjectGuard,
});
export type JwtCustomizerGrantContext = z.infer<typeof jwtCustomizerGrantContextGuard>;
export const accessTokenJwtCustomizerGuard = jwtCustomizerGuard export const accessTokenJwtCustomizerGuard = jwtCustomizerGuard
.extend({ .extend({
// Use partial token guard since users customization may not rely on all fields. // Use partial token guard since users customization may not rely on all fields.
tokenSample: accessTokenPayloadGuard.partial().optional(), tokenSample: accessTokenPayloadGuard.partial().optional(),
contextSample: z.object({ user: jwtCustomizerUserContextGuard.partial() }).optional(), contextSample: z
.object({
user: jwtCustomizerUserContextGuard.partial(),
grant: jwtCustomizerGrantContextGuard.partial().optional(),
})
.optional(),
}) })
.strict(); .strict();