{"record":{"id":"8158d023135b8f6a","repo":"apereo/cas","slug":"invalid-request-8158d0","errorCode":"invalid_request","errorMessage":"CAS cannot accept the request given the issuer is invalid.","messagePattern":"CAS cannot accept the request given the issuer is invalid\\.","errorType":"console","errorClass":null,"httpStatus":400,"severity":"error","filePath":"support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/oidc/vc/issuer/web/OidcVerifiableCredentialEndpointController.java","lineNumber":165,"sourceCode":"            nonces.add(issuedCredential.nonce());\n            responses.add(OidcVerifiableCredentialResponse\n                .builder()\n                .format(issuedCredential.format().getValue())\n                .credential(issuedCredential.credential())\n                .build());\n        }\n        nonces.forEach(oidcVerifiableCredentialNonceService::remove);\n        return responses.size() == 1\n            ? ResponseEntity.ok(responses.getFirst())\n            : ResponseEntity.ok(Map.of(\"credential_responses\", responses));\n    }\n\n    protected Couplet<@Nullable OAuth20AccessToken, @Nullable ResponseEntity> verifyRequest(\n        final HttpServletRequest httpRequest,\n        final HttpServletResponse httpResponse) {\n        val webContext = new JEEContext(httpRequest, httpResponse);\n        if (!getConfigurationContext().getIssuerService().validateIssuer(webContext, List.of(OidcConstants.VC_CREDENTIAL_URL))) {\n            LOGGER.warn(\"CAS cannot accept the request given the issuer is invalid.\");\n            val body = OAuth20Utils.getErrorResponseBody(OAuth20Constants.INVALID_REQUEST, \"Invalid issuer\");\n            return Couplet.right(ResponseEntity.badRequest().body(body));\n        }\n\n        val decodedAccessTokenId = getAccessTokenFromRequest(httpRequest).getValue();\n        val decodedToken = getConfigurationContext().getTicketRegistry().getTicket(decodedAccessTokenId, OAuth20AccessToken.class);\n        if (!validateAccessToken(decodedToken)) {\n            LOGGER.warn(\"The access token is invalid, expired, has an invalid grant type or no authorization details.\");\n            return Couplet.right(ResponseEntity.badRequest()\n                .body(OAuth20Utils.getErrorResponseBody(OAuth20Constants.ERROR, \"Invalid access token\")));\n        }\n        return Couplet.left(decodedToken);\n    }\n\n    protected boolean validateAccessToken(@Nullable final OAuth20AccessToken accessToken) {\n        return accessToken != null && !accessToken.isExpired()\n            && (accessToken.getGrantType() == OAuth20GrantTypes.PRE_AUTHORIZED_CODE || accessToken.hasAuthorizationDetails());\n    }","sourceCodeStart":147,"sourceCodeEnd":183,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-oidc-vc/src/main/java/org/apereo/cas/oidc/vc/issuer/web/OidcVerifiableCredentialEndpointController.java#L147-L183","documentation":"OidcVerifiableCredentialEndpointController.verifyRequest validates the request's issuer against the configured issuer service for the VC credential URL path. When the Host/issuer of the incoming request does not match the configured OIDC issuer, the endpoint returns HTTP 400 with error 'invalid_request' and description 'Invalid issuer'.","triggerScenarios":"Calling the verifiable-credential issuance endpoint with a request whose host/origin does not match cas.authn.oidc.issuer (issuerService.validateIssuer fails for OidcConstants.VC_CREDENTIAL_URL).","commonSituations":"Proxying behind a gateway that rewrites the Host header; accessing the server via localhost/IP instead of the configured issuer hostname; HTTP vs HTTPS mismatch; issuer configured with trailing path differences.","solutions":["Access the endpoint using the exact URL that matches the configured cas.authn.oidc.issuer.","Configure the proxy/load balancer to preserve Host and X-Forwarded-* headers.","Update the issuer configuration to include the scheme/host/path clients actually use."],"exampleFix":"// before (request through wrong host)\ncurl https://localhost:8443/cas/oidc/vc/credential\n// after\n# with issuer=https://sso.example.org/cas/oidc\ncurl https://sso.example.org/cas/oidc/vc/credential -H \"Authorization: Bearer ...\"","handlingStrategy":"validation","validationCode":"// Client-side: ensure request URL starts with the configured issuer\nString issuer = \"https://sso.example.org/cas/oidc\";\nif (!requestUrl.startsWith(issuer)) {\n    throw new IllegalArgumentException(\"Request must use the configured issuer URL \" + issuer);\n}","typeGuard":null,"tryCatchPattern":"// Detect the 400 invalid_request body\nResponse resp = client.send(req);\nif (resp.status() == 400 && body.contains(\"Invalid issuer\")) {\n    throw new IllegalStateException(\"Issuer mismatch: use \" + configuredIssuer);\n}","preventionTips":["Always derive endpoint URLs from the issuer metadata document, never hardcode hosts","Preserve Host/X-Forwarded headers at proxies","Use the same hostname in dev tooling as configured in the issuer"],"tags":["oidc","verifiable-credentials","issuer","http-400"],"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"}