diff --git a/.changeset/fast-shirts-switch.md b/.changeset/fast-shirts-switch.md new file mode 100644 index 000000000..1be94aa99 --- /dev/null +++ b/.changeset/fast-shirts-switch.md @@ -0,0 +1,7 @@ +--- +"@logto/console": patch +"@logto/schemas": patch +"@logto/core": patch +--- + +allow non-http origins for application CORS diff --git a/packages/console/src/pages/ApplicationDetails/components/Settings.tsx b/packages/console/src/pages/ApplicationDetails/components/Settings.tsx index 7c30a31a6..26fdaf694 100644 --- a/packages/console/src/pages/ApplicationDetails/components/Settings.tsx +++ b/packages/console/src/pages/ApplicationDetails/components/Settings.tsx @@ -15,7 +15,6 @@ import { import TextInput from '@/ds-components/TextInput'; import TextLink from '@/ds-components/TextLink'; import useDocumentationUrl from '@/hooks/use-documentation-url'; -import { uriOriginValidator } from '@/utils/validator'; import * as styles from '../index.module.scss'; @@ -160,14 +159,6 @@ function Settings({ data }: Props) { name="customClientMetadata.corsAllowedOrigins" control={control} defaultValue={[]} - rules={{ - validate: createValidatorForRhf({ - pattern: { - verify: (value) => !value || uriOriginValidator(value), - message: t('errors.invalid_origin_format'), - }, - }), - }} render={({ field: { onChange, value }, fieldState: { error } }) => ( value?.filter(Boolean).map((uri) => decodeURIComponent(uri)); const mapToUriOriginFormatArrays = (value?: string[]) => - value?.filter(Boolean).map((uri) => decodeURIComponent(new URL(uri).origin)); + value?.filter(Boolean).map((uri) => decodeURIComponent(uri.replace(/\/*$/, ''))); function ApplicationDetails() { const { id } = useParams(); diff --git a/packages/console/src/utils/validator.ts b/packages/console/src/utils/validator.ts index f3b98dc76..6e642cf58 100644 --- a/packages/console/src/utils/validator.ts +++ b/packages/console/src/utils/validator.ts @@ -9,14 +9,6 @@ export const uriValidator = (value: string) => { return true; }; -export const uriOriginValidator = (value: string) => { - try { - return new URL(value).origin === value; - } catch { - return false; - } -}; - export const jsonValidator = (value: string) => { try { JSON.parse(value); diff --git a/packages/core/src/routes/application.test.ts b/packages/core/src/routes/application.test.ts index b61d99f2e..fbe7ccb23 100644 --- a/packages/core/src/routes/application.test.ts +++ b/packages/core/src/routes/application.test.ts @@ -52,7 +52,12 @@ const { createRequester } = await import('#src/utils/test-utils.js'); const applicationRoutes = await pickDefault(import('./application.js')); const customClientMetadata = { - corsAllowedOrigins: ['http://localhost:5000', 'http://localhost:5001', 'https://silverhand.com'], + corsAllowedOrigins: [ + 'http://localhost:5000', + 'http://localhost:5001', + 'https://silverhand.com', + 'capacitor://localhost', + ], idTokenTtl: 999_999, refreshTokenTtl: 100_000_000, }; diff --git a/packages/schemas/src/foundations/jsonb-types.ts b/packages/schemas/src/foundations/jsonb-types.ts index 7b4c5336e..5ac5fa73d 100644 --- a/packages/schemas/src/foundations/jsonb-types.ts +++ b/packages/schemas/src/foundations/jsonb-types.ts @@ -88,7 +88,7 @@ export enum CustomClientMetadataKey { } export const customClientMetadataGuard = z.object({ - [CustomClientMetadataKey.CorsAllowedOrigins]: z.string().url().array().optional(), + [CustomClientMetadataKey.CorsAllowedOrigins]: z.string().min(1).array().optional(), [CustomClientMetadataKey.IdTokenTtl]: z.number().optional(), [CustomClientMetadataKey.RefreshTokenTtl]: z.number().optional(), [CustomClientMetadataKey.RefreshTokenTtlInDays]: z.number().int().min(1).max(90).optional(),