{"record":{"id":"6c3576f76f5b2e1d","repo":"apereo/cas","slug":"redirect-uri-cannot-contain-a-fragment","errorCode":null,"errorMessage":"Redirect URI cannot contain a fragment","messagePattern":"Redirect URI cannot contain a fragment","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/dynareg/OidcDefaultClientRegistrationRequestTranslator.java","lineNumber":65,"sourceCode":"    private static final ObjectMapper MAPPER = JacksonObjectMapperFactory.builder()\n        .defaultTypingEnabled(false).build().toObjectMapper();\n\n    private static final int GENERATED_CLIENT_NAME_LENGTH = 8;\n\n    private final ObjectProvider<OidcConfigurationContext> configurationContext;\n\n    @Override\n    public OidcRegisteredService translate(\n        final OidcClientRegistrationRequest registrationRequest,\n        final Optional<OidcRegisteredService> givenService) throws Exception {\n\n        val context = configurationContext.getObject();\n\n        val containsFragment = registrationRequest.getRedirectUris()\n            .stream()\n            .anyMatch(uri -> uri.contains(\"#\"));\n        if (containsFragment) {\n            throw new IllegalArgumentException(\"Redirect URI cannot contain a fragment\");\n        }\n\n        val registeredService = givenService.orElseGet(OidcRegisteredService::new);\n        if (StringUtils.isNotBlank(registrationRequest.getClientName())) {\n            registeredService.setName(registrationRequest.getClientName());\n        } else if (StringUtils.isBlank(registeredService.getName())) {\n            registeredService.setName(RandomUtils.randomAlphabetic(GENERATED_CLIENT_NAME_LENGTH));\n        }\n\n        val serviceId = String.join(\"|\", registrationRequest.getRedirectUris());\n        registeredService.setServiceId(serviceId);\n\n        registeredService.setSectorIdentifierUri(registrationRequest.getSectorIdentifierUri());\n        registeredService.setSubjectType(registrationRequest.getSubjectType());\n        if (Strings.CI.equals(OidcSubjectTypes.PAIRWISE.getType(), registeredService.getSubjectType())) {\n            registeredService.setUsernameAttributeProvider(new PairwiseOidcRegisteredServiceUsernameAttributeProvider());\n        }\n","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-core-api/src/main/java/org/apereo/cas/oidc/web/controllers/dynareg/OidcDefaultClientRegistrationRequestTranslator.java#L47-L83","documentation":"During OIDC dynamic client registration, the translator validates the registration request before creating an OidcRegisteredService. The OIDC spec forbids fragment components ('#') in redirect URIs, so any redirect URI containing '#' causes an IllegalArgumentException in translate().","triggerScenarios":"Calling the dynamic client registration endpoint (translate in OidcDefaultClientRegistrationRequestTranslator) with a registration_request whose redirect_uris array contains at least one URI including a '#' character.","commonSituations":"Developers embedding a default fragment (e.g. 'https://app/callback#/home') copied from SPA frameworks, or building redirect URIs with hash-based routing; also test fixtures that accidentally include '#'.","solutions":["Remove the fragment component from every redirect URI in the registration request","Use path-based routing or a query parameter instead of a fragment for client-side routing state","Validate redirect URIs client-side before submitting the registration request"],"exampleFix":"// before\n\"redirect_uris\": [\"https://app.example.com/callback#/home\"]\n// after\n\"redirect_uris\": [\"https://app.example.com/callback?route=home\"]","handlingStrategy":"validation","validationCode":"List<String> redirectUris = request.getRedirectUris();\nif (redirectUris.stream().anyMatch(u -> u.contains(\"#\"))) {\n    throw new IllegalArgumentException(\"redirect_uris must not contain fragments\");\n}","typeGuard":"boolean hasNoFragment = redirectUris.stream().allMatch(u -> u.indexOf('#') < 0);","tryCatchPattern":null,"preventionTips":["Strip or reject '#' in redirect URIs during client onboarding","Use path or query parameters for SPA routing state, never fragments","Add a pre-submit validation test for redirect URIs"],"tags":["oidc","dynamic-client-registration","validation","redirect-uri"],"backgroundTag":"invalid-argument-value","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}