{"record":{"id":"ac52f1a7c269006d","repo":"apereo/cas","slug":"invalid-sector-identifier-uri","errorCode":null,"errorMessage":"Invalid sector identifier uri","messagePattern":"Invalid sector identifier uri","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":276,"sourceCode":"    private void validate(final OidcClientRegistrationRequest registrationRequest,\n                          final OidcRegisteredService registeredService) throws Exception {\n        val context = configurationContext.getObject();\n        if (StringUtils.isNotBlank(registeredService.getSectorIdentifierUri())) {\n            HttpResponse sectorResponse = null;\n            try {\n                val exec = HttpExecutionRequest\n                    .builder()\n                    .method(HttpMethod.GET)\n                    .url(registeredService.getSectorIdentifierUri())\n                    .build();\n                sectorResponse = HttpUtils.execute(exec);\n                if (sectorResponse != null && sectorResponse.getCode() == HttpStatus.SC_OK) {\n                    try (val content = ((HttpEntityContainer) sectorResponse).getEntity().getContent()) {\n                        val result = IOUtils.toString(content, StandardCharsets.UTF_8);\n                        val expectedType = MAPPER.getTypeFactory().constructParametricType(List.class, String.class);\n                        val urls = MAPPER.readValue(JsonValue.readHjson(result).toString(), expectedType);\n                        if (!urls.equals(registrationRequest.getRedirectUris())) {\n                            throw new IllegalArgumentException(\"Invalid sector identifier uri\");\n                        }\n                    }\n                }\n            } finally {\n                HttpUtils.close(sectorResponse);\n            }\n        }\n\n        val oidc = context.getCasProperties().getAuthn().getOidc();\n        if (!oidc.getRegistration().getDynamicClientRegistrationMode().isProtected()\n            && (StringUtils.isNotBlank(registrationRequest.getPolicyUri()) || StringUtils.isNotBlank(registrationRequest.getLogo()))) {\n            val hosts = registrationRequest.getRedirectUris()\n                .stream()\n                .map(uri -> FunctionUtils.doUnchecked(() -> new URI(uri).getHost())).toList();\n            if (StringUtils.isNotBlank(registrationRequest.getLogo())) {\n                val logo = new URI(registrationRequest.getLogo()).getHost();\n                if (!hosts.contains(logo)) {\n                    throw new IllegalArgumentException(\"Invalid logo uri from an unknown host\");","sourceCodeStart":258,"sourceCodeEnd":294,"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#L258-L294","documentation":"When the registration request supplies a sector_identifier_uri, the translator fetches it and expects the document to be a JSON array of redirect URIs exactly equal to the request's redirect_uris. If the fetched list differs, translate() throws this IllegalArgumentException per the OIDC dynamic registration spec.","triggerScenarios":"translate() -> validate(): a sector_identifier_uri is provided, the remote document returns HTTP 200 and parses as a JSON array of strings, but that array does not exactly match (order-sensitive) registrationRequest.getRedirectUris().","commonSituations":"The sector identifier document is shared across several clients with different redirect URI sets, the document is stale after the client changed redirect URIs, or list ordering differs between the document and the request.","solutions":["Update the sector_identifier_uri document so its JSON array exactly equals the redirect_uris sent in the registration request (same values, same order)","If redirect URIs differ across clients, host a per-client sector identifier document or omit sector_identifier_uri","Check for trailing-slash or http vs https differences between the document and request URIs"],"exampleFix":"// before (sector document)\n[\"https://app.example.com/other-callback\"]\n// after (must match request redirect_uris)\n[\"https://app.example.com/callback\"]","handlingStrategy":"validation","validationCode":"List<String> sectorUrls = fetchSectorIdentifierUri(sectorUri);\nif (!sectorUrls.equals(request.getRedirectUris())) {\n    throw new IllegalStateException(\"sector_identifier_uri document must equal redirect_uris\");\n}","typeGuard":null,"tryCatchPattern":"try { translator.translate(request); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"sector identifier\")) { /* reconcile sector document with redirect_uris */ } else throw e; }","preventionTips":["Regenerate the sector identifier document whenever redirect URIs change","Keep element order identical between document and request","Host one sector document per client group only when URI sets are identical"],"tags":["oidc","dynamic-client-registration","sector-identifier","validation"],"backgroundTag":"schema-validation-failed","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"}