{"record":{"id":"6a98aaab4b080520","repo":"spring-projects/spring-security","slug":"oauth-2-0-token-introspection-parameter-paramet","errorCode":null,"errorMessage":"OAuth 2.0 Token Introspection Parameter: ${parameterName}","messagePattern":"OAuth 2\\.0 Token Introspection Parameter: (.+?)","errorType":"error_code","errorClass":"OAuth2AuthenticationException","httpStatus":400,"severity":"error","filePath":"oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenIntrospectionAuthenticationConverter.java","lineNumber":89,"sourceCode":"\n\t\tMap<String, Object> additionalParameters = new HashMap<>();\n\t\tparameters.forEach((key, value) -> {\n\t\t\tif (!key.equals(OAuth2ParameterNames.TOKEN) && !key.equals(OAuth2ParameterNames.TOKEN_TYPE_HINT)) {\n\t\t\t\tadditionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]));\n\t\t\t}\n\t\t});\n\n\t\tAuthentication clientPrincipal = SecurityContextHolder.getContext().getAuthentication();\n\t\tAssert.notNull(clientPrincipal, \"clientPrincipal cannot be null\");\n\n\t\treturn new OAuth2TokenIntrospectionAuthenticationToken(token, clientPrincipal, tokenTypeHint,\n\t\t\t\tadditionalParameters);\n\t}\n\n\tprivate static void throwError(String errorCode, String parameterName) {\n\t\tOAuth2Error error = new OAuth2Error(errorCode, \"OAuth 2.0 Token Introspection Parameter: \" + parameterName,\n\t\t\t\t\"https://datatracker.ietf.org/doc/html/rfc7662#section-2.1\");\n\t\tthrow new OAuth2AuthenticationException(error);\n\t}\n\n}\n","sourceCodeStart":71,"sourceCodeEnd":93,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenIntrospectionAuthenticationConverter.java#L71-L93","documentation":"The OAuth2TokenIntrospectionAuthenticationConverter throws this when a required Token Introspection request parameter (per RFC 7662 section 2.1) is missing or appears more than once in the request. The message embeds the offending parameterName so the client knows which parameter failed. It surfaces as an OAuth2AuthenticationException handled by the authorization server's error endpoint, producing an OAuth2 error response.","triggerScenarios":"POST to /oauth2/introspect without the required 'token' parameter, or with duplicated parameters (e.g. token=abc&token=def), or missing/invalid 'token_type_hint' value; throwError is called from convert() when parameter extraction/validation fails.","commonSituations":"Clients sending form-encoded introspection requests with the token omitted or repeated; proxies or HTTP client libraries that flatten/duplicate query or form parameters; misconfigured clients that put parameters in the wrong place (query string vs form body); custom frontends forwarding malformed introspection requests.","solutions":["Ensure the introspection request includes exactly one 'token' form parameter and, if sent, exactly one 'token_type_hint' (access_token or refresh_token).","Send parameters as application/x-www-form-urlencoded in the request body, not duplicated in the query string.","Inspect the error detail to identify the offending parameterName and log the raw request body to spot duplicates.","If a proxy/gateway is in the path, verify it does not re-append or duplicate parameters."],"exampleFix":"// before\nform.set('token', token);\nform.set('token', token); // duplicate -> invalid_request\n// after\nform.set('token', token);\nform.set('token_type_hint', 'access_token');","handlingStrategy":"validation","validationCode":"const params = new URLSearchParams(body);\nif (params.getAll('token').length !== 1 || !params.get('token')) {\n  throw new Error('introspection requires exactly one non-empty token parameter');\n}\nconst hint = params.getAll('token_type_hint');\nif (hint.length > 1 || (hint[0] && !['access_token','refresh_token'].includes(hint[0]))) {\n  throw new Error('invalid token_type_hint');\n}","typeGuard":"function hasSingleParam(params, name) {\n  const values = params.getAll(name);\n  return values.length === 1 && values[0].length > 0;\n}","tryCatchPattern":"try {\n  const response = await fetch('/oauth2/introspect', { method: 'POST', body });\n  const data = await response.json();\n  if (data.error === 'invalid_request') {\n    console.error('Introspection parameter error:', data.error_description);\n  }\n} catch (e) { /* network/handling */ }","preventionTips":["Always send introspection parameters as a form-encoded body with a single token value.","Build the request with URLSearchParams or an equivalent library to avoid manual encoding duplicates.","Never put introspection parameters in the query string.","Log the raw request body in tests to catch duplicates introduced by interceptors or proxies."],"tags":["oauth2","token-introspection","rfc7662","invalid-request"],"backgroundTag":"missing-required-argument","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}