From 7c4019b4081f9d38ef503ac9802594372c4f099a Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Fri, 1 Mar 2024 18:02:16 +0800 Subject: [PATCH] feat(core,phrases): add POST /configs/jwt-customizer API --- packages/core/src/__mocks__/index.ts | 26 ++++++- packages/core/src/middleware/koa-guard.ts | 2 +- packages/core/src/queries/logto-config.ts | 34 +++++++-- .../core/src/routes/logto-config.openapi.json | 46 ++++++++++++ packages/core/src/routes/logto-config.test.ts | 47 +++++++++--- packages/core/src/routes/logto-config.ts | 74 ++++++++++++++++++- .../integration-tests/src/api/logto-config.ts | 8 ++ .../src/tests/api/logto-config.test.ts | 29 ++++++++ .../phrases/src/locales/de/errors/index.ts | 2 + .../src/locales/de/errors/logto-config.ts | 9 +++ .../phrases/src/locales/en/errors/index.ts | 2 + .../src/locales/en/errors/logto-config.ts | 8 ++ .../phrases/src/locales/es/errors/index.ts | 2 + .../src/locales/es/errors/logto-config.ts | 9 +++ .../phrases/src/locales/fr/errors/index.ts | 2 + .../src/locales/fr/errors/logto-config.ts | 9 +++ .../phrases/src/locales/it/errors/index.ts | 2 + .../src/locales/it/errors/logto-config.ts | 9 +++ .../phrases/src/locales/ja/errors/index.ts | 2 + .../src/locales/ja/errors/logto-config.ts | 9 +++ .../phrases/src/locales/ko/errors/index.ts | 2 + .../src/locales/ko/errors/logto-config.ts | 9 +++ .../phrases/src/locales/pl-pl/errors/index.ts | 2 + .../src/locales/pl-pl/errors/logto-config.ts | 9 +++ .../phrases/src/locales/pt-br/errors/index.ts | 2 + .../src/locales/pt-br/errors/logto-config.ts | 9 +++ .../phrases/src/locales/pt-pt/errors/index.ts | 2 + .../src/locales/pt-pt/errors/logto-config.ts | 9 +++ .../phrases/src/locales/ru/errors/index.ts | 2 + .../src/locales/ru/errors/logto-config.ts | 9 +++ .../phrases/src/locales/tr-tr/errors/index.ts | 2 + .../src/locales/tr-tr/errors/logto-config.ts | 9 +++ .../phrases/src/locales/zh-cn/errors/index.ts | 2 + .../src/locales/zh-cn/errors/logto-config.ts | 9 +++ .../phrases/src/locales/zh-hk/errors/index.ts | 2 + .../src/locales/zh-hk/errors/logto-config.ts | 9 +++ .../phrases/src/locales/zh-tw/errors/index.ts | 2 + .../src/locales/zh-tw/errors/logto-config.ts | 9 +++ 38 files changed, 409 insertions(+), 21 deletions(-) create mode 100644 packages/phrases/src/locales/de/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/en/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/es/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/fr/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/it/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/ja/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/ko/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/pl-pl/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/pt-br/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/pt-pt/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/ru/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/tr-tr/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/zh-cn/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/zh-hk/errors/logto-config.ts create mode 100644 packages/phrases/src/locales/zh-tw/errors/logto-config.ts diff --git a/packages/core/src/__mocks__/index.ts b/packages/core/src/__mocks__/index.ts index 015732938..6ba66c493 100644 --- a/packages/core/src/__mocks__/index.ts +++ b/packages/core/src/__mocks__/index.ts @@ -159,7 +159,7 @@ export const mockCookieKeys: OidcConfigKey[] = [ { id: 'cookie', value: 'bar', createdAt: 987_654_321 }, ]; -export const mockLogtoConfigs: LogtoConfig[] = [ +const mockLogtoConfigs: LogtoConfig[] = [ { tenantId: 'fake_tenant', key: LogtoOidcConfigKey.PrivateKeys, @@ -172,6 +172,14 @@ export const mockLogtoConfigs: LogtoConfig[] = [ }, ]; +export const mockLogtoConfigRows = { + rows: mockLogtoConfigs, + rowCount: mockLogtoConfigs.length, + command: 'SELECT' as const, + fields: [], + notices: [], +}; + export const mockPasscode: Passcode = { tenantId: 'fake_tenant', id: 'foo', @@ -198,3 +206,19 @@ export const mockApplicationRole: ApplicationsRole = { applicationId: 'application_id', roleId: 'role_id', }; + +export const mockJwtCustomizerConfigForAccessToken = { + tenantId: 'fake_tenant', + key: 'jwt.accessToken', + value: { + script: 'console.log("hello world");', + envVars: { + API_KEY: '', + }, + contextSample: { + user: { + username: 'user', + }, + }, + }, +}; diff --git a/packages/core/src/middleware/koa-guard.ts b/packages/core/src/middleware/koa-guard.ts index 40ac61204..8b24f3f71 100644 --- a/packages/core/src/middleware/koa-guard.ts +++ b/packages/core/src/middleware/koa-guard.ts @@ -92,7 +92,7 @@ export const isGuardMiddleware = ( ): function_ is WithGuardConfig => function_.name === 'guardMiddleware' && has(function_, 'config'); -const tryParse = ( +export const tryParse = ( type: 'query' | 'body' | 'params' | 'files', guard: Optional>, data: unknown diff --git a/packages/core/src/queries/logto-config.ts b/packages/core/src/queries/logto-config.ts index 8ff441ae8..7b33b5aad 100644 --- a/packages/core/src/queries/logto-config.ts +++ b/packages/core/src/queries/logto-config.ts @@ -1,14 +1,19 @@ -import type { - AdminConsoleData, - LogtoConfig, - LogtoConfigKey, - LogtoOidcConfigKey, - OidcConfigKey, +import { + type jwtCustomizerConfigGuard, + LogtoTenantConfigKey, + LogtoConfigs, + type AdminConsoleData, + type LogtoConfig, + type LogtoConfigKey, + type LogtoOidcConfigKey, + type OidcConfigKey, + type LogtoJwtTokenKey, + type JwtCustomizerType, } from '@logto/schemas'; -import { LogtoTenantConfigKey, LogtoConfigs } from '@logto/schemas'; import { convertToIdentifiers } from '@logto/shared'; import type { CommonQueryMethods } from 'slonik'; import { sql } from 'slonik'; +import type { z } from 'zod'; const { table, fields } = convertToIdentifiers(LogtoConfigs); @@ -47,11 +52,26 @@ export const createLogtoConfigQueries = (pool: CommonQueryMethods) => { returning * `); + // Can not narrow down the type of value if we utilize `buildInsertIntoWithPool` method. + const insertJwtCustomizer = async ( + key: T, + value: z.infer<(typeof jwtCustomizerConfigGuard)[T]> + ) => + pool.one<{ key: T; value: JwtCustomizerType[T] }>( + sql` + insert into ${table} (${fields.key}, ${fields.value}) + values (${key}, ${sql.jsonb(value)}) + on conflict (${fields.tenantId}, ${fields.key}) do nothing + returning * + ` + ); + return { getAdminConsoleConfig, updateAdminConsoleConfig, getCloudConnectionData, getRowsByKeys, updateOidcConfigsByKey, + insertJwtCustomizer, }; }; diff --git a/packages/core/src/routes/logto-config.openapi.json b/packages/core/src/routes/logto-config.openapi.json index 772a248c3..bf586f3ee 100644 --- a/packages/core/src/routes/logto-config.openapi.json +++ b/packages/core/src/routes/logto-config.openapi.json @@ -104,6 +104,52 @@ } } } + }, + "/api/configs/jwt-customizer/{tokenType}": { + "post": { + "summary": "Create JWT customizer", + "description": "Create a JWT customizer for the given token type.", + "parameters": [ + { + "in": "path", + "name": "tokenType", + "description": "The token type to create a JWT customizer for." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "properties": { + "script": { + "description": "The script of the JWT customizer." + }, + "envVars": { + "description": "The environment variables for the JWT customizer." + }, + "contextSample": { + "description": "The sample context for the JWT customizer script testing purpose." + }, + "tokenSample": { + "description": "The sample raw token payload for the JWT customizer script testing purpose." + } + } + } + } + } + }, + "responses": { + "201": { + "description": "The created JWT customizer." + }, + "400": { + "description": "The request body is invalid." + }, + "409": { + "description": "The JWT customizer already exists." + } + } + } } } } diff --git a/packages/core/src/routes/logto-config.test.ts b/packages/core/src/routes/logto-config.test.ts index 40499e42b..af95dd06a 100644 --- a/packages/core/src/routes/logto-config.test.ts +++ b/packages/core/src/routes/logto-config.test.ts @@ -1,4 +1,4 @@ -import { LogtoOidcConfigKey, type AdminConsoleData } from '@logto/schemas'; +import { LogtoOidcConfigKey, type AdminConsoleData, LogtoJwtTokenKey } from '@logto/schemas'; import { generateStandardId } from '@logto/shared'; import { createMockUtils, pickDefault } from '@logto/shared/esm'; import Sinon from 'sinon'; @@ -6,8 +6,9 @@ import Sinon from 'sinon'; import { mockAdminConsoleData, mockCookieKeys, - mockLogtoConfigs, mockPrivateKeys, + mockLogtoConfigRows, + mockJwtCustomizerConfigForAccessToken, } from '#src/__mocks__/index.js'; import { MockTenant } from '#src/test-utils/tenant.js'; import { createRequester } from '#src/utils/test-utils.js'; @@ -52,13 +53,8 @@ const logtoConfigQueries = { }, }), updateOidcConfigsByKey: jest.fn(), - getRowsByKeys: jest.fn(async () => ({ - rows: mockLogtoConfigs, - rowCount: mockLogtoConfigs.length, - command: 'SELECT' as const, - fields: [], - notices: [], - })), + getRowsByKeys: jest.fn(async () => mockLogtoConfigRows), + insertJwtCustomizer: jest.fn(), }; const logtoConfigLibraries = { @@ -229,4 +225,37 @@ describe('configs routes', () => { [newPrivateKey2, newPrivateKey] ); }); + + it('POST /configs/jwt-customizer/:tokenType should add a record successfully', async () => { + logtoConfigQueries.getRowsByKeys.mockResolvedValueOnce({ + ...mockLogtoConfigRows, + rows: [], + rowCount: 0, + }); + logtoConfigQueries.insertJwtCustomizer.mockResolvedValueOnce( + mockJwtCustomizerConfigForAccessToken + ); + const response = await routeRequester + .post('/configs/jwt-customizer/access-token') + .send(mockJwtCustomizerConfigForAccessToken); + expect(logtoConfigQueries.insertJwtCustomizer).toHaveBeenCalledWith( + LogtoJwtTokenKey.AccessToken, + mockJwtCustomizerConfigForAccessToken + ); + expect(response.status).toEqual(201); + expect(response.body).toEqual(mockJwtCustomizerConfigForAccessToken.value); + }); + + it('POST /configs/jwt-customizer/:tokenType should fail ', async () => { + logtoConfigQueries.getRowsByKeys.mockResolvedValueOnce({ + ...mockLogtoConfigRows, + rows: [mockJwtCustomizerConfigForAccessToken], + rowCount: 1, + }); + const response = await routeRequester + .post('/configs/jwt-customizer/access-token') + .send(mockJwtCustomizerConfigForAccessToken); + expect(logtoConfigQueries.insertJwtCustomizer).not.toHaveBeenCalled(); + expect(response.status).toEqual(409); + }); }); diff --git a/packages/core/src/routes/logto-config.ts b/packages/core/src/routes/logto-config.ts index dcdbd3a08..3fac88e3a 100644 --- a/packages/core/src/routes/logto-config.ts +++ b/packages/core/src/routes/logto-config.ts @@ -12,11 +12,16 @@ import { type OidcConfigKeysResponse, type OidcConfigKey, LogtoOidcConfigKeyType, + LogtoJwtTokenKeyType, + jwtCustomizerAccessTokenGuard, + jwtCustomizerClientCredentialsGuard, + LogtoJwtTokenKey, } from '@logto/schemas'; import { z } from 'zod'; import RequestError from '#src/errors/RequestError/index.js'; -import koaGuard from '#src/middleware/koa-guard.js'; +import koaGuard, { tryParse } from '#src/middleware/koa-guard.js'; +import assertThat from '#src/utils/assert-that.js'; import { exportJWK } from '#src/utils/jwks.js'; import type { AuthedRouter, RouterInitArgs } from './types.js'; @@ -29,6 +34,11 @@ const getOidcConfigKeyDatabaseColumnName = (key: LogtoOidcConfigKeyType): LogtoO ? LogtoOidcConfigKey.PrivateKeys : LogtoOidcConfigKey.CookieKeys; +const getLogtoJwtTokenKey = (key: LogtoJwtTokenKeyType): LogtoJwtTokenKey => + key === LogtoJwtTokenKeyType.AccessToken + ? LogtoJwtTokenKey.AccessToken + : LogtoJwtTokenKey.ClientCredentials; + /** * 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`. @@ -60,8 +70,13 @@ const getRedactedOidcKeyResponse = async ( export default function logtoConfigRoutes( ...[router, { queries, logtoConfigs, invalidateCache }]: RouterInitArgs ) { - const { getAdminConsoleConfig, updateAdminConsoleConfig, updateOidcConfigsByKey } = - queries.logtoConfigs; + const { + getAdminConsoleConfig, + getRowsByKeys, + insertJwtCustomizer, + updateAdminConsoleConfig, + updateOidcConfigsByKey, + } = queries.logtoConfigs; const { getOidcConfigs } = logtoConfigs; router.get( @@ -182,4 +197,57 @@ export default function logtoConfigRoutes( return next(); } ); + + router.post( + '/configs/jwt-customizer/:tokenType', + koaGuard({ + params: z.object({ + tokenType: z.nativeEnum(LogtoJwtTokenKeyType), + }), + /** + * Use `z.unknown()` to guard the request body as a JSON object, since the actual guard depends + * on the `tokenType` and we can not get the value of `tokenType` before parsing the request body, + * we will do more specific guard as long as we can get the value of `tokenType`. + */ + body: z.unknown(), + response: jwtCustomizerAccessTokenGuard.or(jwtCustomizerClientCredentialsGuard), + status: [201, 400, 409], + }), + async (ctx, next) => { + const { + params: { tokenType }, + body, + } = ctx.guard; + + // Manually implement the request body type check, the flow aligns with the actual `koaGuard()`. + // Use ternary operator to get the specific guard brings difficulties to type inference. + if (tokenType === LogtoJwtTokenKeyType.AccessToken) { + tryParse('body', jwtCustomizerAccessTokenGuard, body); + } else { + tryParse('body', jwtCustomizerClientCredentialsGuard, body); + } + + const { rows } = await getRowsByKeys([getLogtoJwtTokenKey(tokenType)]); + assertThat( + rows.length === 0, + new RequestError({ + code: 'logto_config.jwt.customizer_exists', + tokenType, + status: 409, + }) + ); + + const jwtCustomizer = await insertJwtCustomizer( + getLogtoJwtTokenKey(tokenType), + // Since we applied the detailed guard manually, we can safely cast the `body` to the specific type. + // eslint-disable-next-line no-restricted-syntax + body as Parameters[1] + ); + + ctx.status = 201; + ctx.body = jwtCustomizer.value; + + return next(); + } + ); } diff --git a/packages/integration-tests/src/api/logto-config.ts b/packages/integration-tests/src/api/logto-config.ts index d2867f103..badc14b09 100644 --- a/packages/integration-tests/src/api/logto-config.ts +++ b/packages/integration-tests/src/api/logto-config.ts @@ -3,6 +3,9 @@ import { type AdminConsoleData, type OidcConfigKeysResponse, type LogtoOidcConfigKeyType, + type LogtoJwtTokenKeyType, + type JwtCustomizerAccessToken, + type JwtCustomizerClientCredentials, } from '@logto/schemas'; import { authedAdminApi } from './api.js'; @@ -30,3 +33,8 @@ export const rotateOidcKeys = async ( authedAdminApi .post(`configs/oidc/${keyType}/rotate`, { json: { signingKeyAlgorithm } }) .json(); + +export const insertJwtCustomizer = async (keyType: LogtoJwtTokenKeyType, value: unknown) => + authedAdminApi + .post(`configs/jwt-customizer/${keyType}`, { json: value }) + .json(); diff --git a/packages/integration-tests/src/tests/api/logto-config.test.ts b/packages/integration-tests/src/tests/api/logto-config.test.ts index 6b5adfa2e..57ae34a40 100644 --- a/packages/integration-tests/src/tests/api/logto-config.test.ts +++ b/packages/integration-tests/src/tests/api/logto-config.test.ts @@ -2,6 +2,7 @@ import { SupportedSigningKeyAlgorithm, type AdminConsoleData, LogtoOidcConfigKeyType, + LogtoJwtTokenKeyType, } from '@logto/schemas'; import { @@ -10,6 +11,7 @@ import { getOidcKeys, rotateOidcKeys, updateAdminConsoleConfig, + insertJwtCustomizer, } from '#src/api/index.js'; import { expectRejects } from '#src/helpers/index.js'; @@ -123,4 +125,31 @@ describe('admin console sign-in experience', () => { ]); expect(privateKeys2[1]?.id).toBe(privateKeys[0]?.id); }); + + it('should successfully add a new JWT customizer', async () => { + const accessTokenJwtCustomizerPayload = { + script: '', + envVars: {}, + contextSample: { + user: { + username: 'test', + id: 'fake-id', + }, + }, + }; + const clientCredentialsJwtCustomizerPayload = { + ...accessTokenJwtCustomizerPayload, + contextSample: {}, + }; + const accessToken = await insertJwtCustomizer( + LogtoJwtTokenKeyType.AccessToken, + accessTokenJwtCustomizerPayload + ); + expect(accessToken).toMatchObject(accessTokenJwtCustomizerPayload); + const clientCredentials = await insertJwtCustomizer( + LogtoJwtTokenKeyType.ClientCredentials, + clientCredentialsJwtCustomizerPayload + ); + expect(clientCredentials).toMatchObject(clientCredentialsJwtCustomizerPayload); + }); }); diff --git a/packages/phrases/src/locales/de/errors/index.ts b/packages/phrases/src/locales/de/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/de/errors/index.ts +++ b/packages/phrases/src/locales/de/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/de/errors/logto-config.ts b/packages/phrases/src/locales/de/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/de/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/en/errors/index.ts b/packages/phrases/src/locales/en/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/en/errors/index.ts +++ b/packages/phrases/src/locales/en/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/en/errors/logto-config.ts b/packages/phrases/src/locales/en/errors/logto-config.ts new file mode 100644 index 000000000..65863e250 --- /dev/null +++ b/packages/phrases/src/locales/en/errors/logto-config.ts @@ -0,0 +1,8 @@ +const logto_config = { + jwt: { + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/es/errors/index.ts b/packages/phrases/src/locales/es/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/es/errors/index.ts +++ b/packages/phrases/src/locales/es/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/es/errors/logto-config.ts b/packages/phrases/src/locales/es/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/es/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/fr/errors/index.ts b/packages/phrases/src/locales/fr/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/fr/errors/index.ts +++ b/packages/phrases/src/locales/fr/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/fr/errors/logto-config.ts b/packages/phrases/src/locales/fr/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/fr/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/it/errors/index.ts b/packages/phrases/src/locales/it/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/it/errors/index.ts +++ b/packages/phrases/src/locales/it/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/it/errors/logto-config.ts b/packages/phrases/src/locales/it/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/it/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/ja/errors/index.ts b/packages/phrases/src/locales/ja/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/ja/errors/index.ts +++ b/packages/phrases/src/locales/ja/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/ja/errors/logto-config.ts b/packages/phrases/src/locales/ja/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/ja/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/ko/errors/index.ts b/packages/phrases/src/locales/ko/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/ko/errors/index.ts +++ b/packages/phrases/src/locales/ko/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/ko/errors/logto-config.ts b/packages/phrases/src/locales/ko/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/ko/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/pl-pl/errors/index.ts b/packages/phrases/src/locales/pl-pl/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/pl-pl/errors/index.ts +++ b/packages/phrases/src/locales/pl-pl/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/pl-pl/errors/logto-config.ts b/packages/phrases/src/locales/pl-pl/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/pl-pl/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/pt-br/errors/index.ts b/packages/phrases/src/locales/pt-br/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/pt-br/errors/index.ts +++ b/packages/phrases/src/locales/pt-br/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/pt-br/errors/logto-config.ts b/packages/phrases/src/locales/pt-br/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/pt-br/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/pt-pt/errors/index.ts b/packages/phrases/src/locales/pt-pt/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/pt-pt/errors/index.ts +++ b/packages/phrases/src/locales/pt-pt/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/pt-pt/errors/logto-config.ts b/packages/phrases/src/locales/pt-pt/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/pt-pt/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/ru/errors/index.ts b/packages/phrases/src/locales/ru/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/ru/errors/index.ts +++ b/packages/phrases/src/locales/ru/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/ru/errors/logto-config.ts b/packages/phrases/src/locales/ru/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/ru/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/tr-tr/errors/index.ts b/packages/phrases/src/locales/tr-tr/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/tr-tr/errors/index.ts +++ b/packages/phrases/src/locales/tr-tr/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/tr-tr/errors/logto-config.ts b/packages/phrases/src/locales/tr-tr/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/tr-tr/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/zh-cn/errors/index.ts b/packages/phrases/src/locales/zh-cn/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/zh-cn/errors/index.ts +++ b/packages/phrases/src/locales/zh-cn/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/zh-cn/errors/logto-config.ts b/packages/phrases/src/locales/zh-cn/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/zh-cn/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/zh-hk/errors/index.ts b/packages/phrases/src/locales/zh-hk/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/zh-hk/errors/index.ts +++ b/packages/phrases/src/locales/zh-hk/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/zh-hk/errors/logto-config.ts b/packages/phrases/src/locales/zh-hk/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/zh-hk/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config); diff --git a/packages/phrases/src/locales/zh-tw/errors/index.ts b/packages/phrases/src/locales/zh-tw/errors/index.ts index 74995792d..239307676 100644 --- a/packages/phrases/src/locales/zh-tw/errors/index.ts +++ b/packages/phrases/src/locales/zh-tw/errors/index.ts @@ -7,6 +7,7 @@ import guard from './guard.js'; import hook from './hook.js'; import localization from './localization.js'; import log from './log.js'; +import logto_config from './logto-config.js'; import oidc from './oidc.js'; import organization from './organization.js'; import password from './password.js'; @@ -35,6 +36,7 @@ const errors = { verification_code, sign_in_experiences, localization, + logto_config, swagger, entity, log, diff --git a/packages/phrases/src/locales/zh-tw/errors/logto-config.ts b/packages/phrases/src/locales/zh-tw/errors/logto-config.ts new file mode 100644 index 000000000..8aff44c45 --- /dev/null +++ b/packages/phrases/src/locales/zh-tw/errors/logto-config.ts @@ -0,0 +1,9 @@ +const logto_config = { + jwt: { + /** UNTRANSLATED */ + customizer_exists: + 'Can not create a new customizer since there is already one for the token type `{{tokenType}}`.', + }, +}; + +export default Object.freeze(logto_config);