{"record":{"id":"d9884253f80e2c8c","repo":"apereo/cas","slug":"invalid-logo-uri-from-an-unknown-host","errorCode":null,"errorMessage":"Invalid logo uri from an unknown host","messagePattern":"Invalid logo uri from an unknown host","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":294,"sourceCode":"                            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\");\n                }\n            }\n\n            if (StringUtils.isNotBlank(registrationRequest.getPolicyUri())) {\n                val policy = new URI(registrationRequest.getPolicyUri()).getHost();\n                if (!hosts.contains(policy)) {\n                    throw new IllegalArgumentException(\"Invalid policy uri from an unknown host\");\n                }\n            }\n        }\n\n        if (Strings.CI.equalsAny(registeredService.getBackchannelTokenDeliveryMode(),\n            OidcBackchannelTokenDeliveryModes.PUSH.getMode(), OidcBackchannelTokenDeliveryModes.PING.getMode())) {\n            Assert.hasText(registeredService.getBackchannelClientNotificationEndpoint(),\n                \"Backchannel client notification endpoint must be specified\");\n            Assert.isTrue(Strings.CI.startsWith(registeredService.getBackchannelClientNotificationEndpoint(), \"https://\"),\n                \"Backchannel client notification endpoint MUST be an HTTPS url\");\n        }","sourceCodeStart":276,"sourceCodeEnd":312,"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#L276-L312","documentation":"The translator restricts the client's logo_uri to hosts already present in the request's redirect URIs, preventing a client from pointing its logo at an arbitrary host. If the logo URI's host is not among the redirect URI hosts, translate() -> validate() throws this IllegalArgumentException.","triggerScenarios":"translate() -> validate(): registrationRequest.getLogo() is non-blank and new URI(logo).getHost() is not contained in the set of hosts extracted from redirect_uris.","commonSituations":"Hosting the logo on a CDN (e.g. cdn.example.com) while redirect URIs point at app.example.com; using a placeholder or external image URL like a logo from a public icon service; protocol-relative or typo'd hosts.","solutions":["Host the logo on the same host as one of the redirect URis, or add that host to the redirect_uris","Register the client without a logo and attach it via service management afterward if allowed","Normalize the logo URI (correct scheme/host) so its host matches a redirect URI host"],"exampleFix":"// before\n\"redirect_uris\": [\"https://app.example.com/callback\"],\n\"logo_uri\": \"https://cdn.example.net/logo.png\"\n// after\n\"redirect_uris\": [\"https://app.example.com/callback\"],\n\"logo_uri\": \"https://app.example.com/logo.png\"","handlingStrategy":"validation","validationCode":"Set<String> hosts = request.getRedirectUris().stream()\n    .map(u -> URI.create(u).getHost()).collect(Collectors.toSet());\nif (request.getLogo() != null && !hosts.contains(URI.create(request.getLogo()).getHost())) {\n    throw new IllegalArgumentException(\"logo_uri host must match a redirect_uri host\");\n}","typeGuard":null,"tryCatchPattern":"try { translator.translate(request); } catch (IllegalArgumentException e) { if (e.getMessage().contains(\"logo uri\")) { /* fix logo_uri host */ } else throw e; }","preventionTips":["Serve client metadata assets (logo, policy) from the same host as redirect URIs","Avoid CDN or third-party hosts for logo_uri in dynamic registration","Validate URI hosts before submitting the registration request"],"tags":["oidc","dynamic-client-registration","validation","uri"],"backgroundTag":"invalid-url","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"}