{"record":{"id":"6c1150ca91106ecf","repo":"spring-projects/spring-security","slug":"missing-required-parameter-access-token","errorCode":null,"errorMessage":"Missing required parameter: access_token","messagePattern":"Missing required parameter: access_token","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/DefaultMapOAuth2AccessTokenResponseConverter.java","lineNumber":50,"sourceCode":"/**\n * A {@link Converter} that converts the provided OAuth 2.0 Access Token Response\n * parameters to an {@link OAuth2AccessTokenResponse}.\n *\n * @author Steve Riesenberg\n * @since 5.6\n */\npublic final class DefaultMapOAuth2AccessTokenResponseConverter\n\t\timplements Converter<Map<String, Object>, OAuth2AccessTokenResponse> {\n\n\tprivate static final Set<String> TOKEN_RESPONSE_PARAMETER_NAMES = new HashSet<>(\n\t\t\tArrays.asList(OAuth2ParameterNames.ACCESS_TOKEN, OAuth2ParameterNames.EXPIRES_IN,\n\t\t\t\t\tOAuth2ParameterNames.REFRESH_TOKEN, OAuth2ParameterNames.SCOPE, OAuth2ParameterNames.TOKEN_TYPE));\n\n\t@Override\n\tpublic OAuth2AccessTokenResponse convert(Map<String, Object> source) {\n\t\tString accessToken = getParameterValue(source, OAuth2ParameterNames.ACCESS_TOKEN);\n\t\tif (accessToken == null) {\n\t\t\tthrow new IllegalArgumentException(\"Missing required parameter: \" + OAuth2ParameterNames.ACCESS_TOKEN);\n\t\t}\n\t\tOAuth2AccessToken.TokenType accessTokenType = getAccessTokenType(source);\n\t\tlong expiresIn = getExpiresIn(source);\n\t\tSet<String> scopes = getScopes(source);\n\t\tString refreshToken = getParameterValue(source, OAuth2ParameterNames.REFRESH_TOKEN);\n\t\tMap<String, Object> additionalParameters = new LinkedHashMap<>();\n\t\tfor (Map.Entry<String, Object> entry : source.entrySet()) {\n\t\t\tif (!TOKEN_RESPONSE_PARAMETER_NAMES.contains(entry.getKey())) {\n\t\t\t\tadditionalParameters.put(entry.getKey(), entry.getValue());\n\t\t\t}\n\t\t}\n\t\t// @formatter:off\n\t\treturn OAuth2AccessTokenResponse.withToken(accessToken)\n\t\t\t\t.tokenType(accessTokenType)\n\t\t\t\t.expiresIn(expiresIn)\n\t\t\t\t.scopes(scopes)\n\t\t\t\t.refreshToken(refreshToken)\n\t\t\t\t.additionalParameters(additionalParameters)","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/endpoint/DefaultMapOAuth2AccessTokenResponseConverter.java#L32-L68","documentation":"DefaultMapOAuth2AccessTokenResponse.convert requires an access_token parameter in the token endpoint response map per RFC 6749 §5.1. If the map has no access_token, it throws IllegalArgumentException, which callers usually surface wrapped in an OAuth2AuthorizationException or conversion error.","triggerScenarios":"The authorization/token endpoint returned a 200 body that lacks access_token — e.g. an error JSON body delivered with HTTP 200, an HTML error page, or a provider returning only refresh_token.","commonSituations":"Misconfigured token URL hitting a login page; providers deviating from the spec on device-code or extension grants; proxies returning soft-200 error pages; using this converter on a non-token payload.","solutions":["Inspect the raw token endpoint response (enable HTTP logging) and fix why access_token is missing","Check that grant_type/client credentials are correct so the server returns a real success response with HTTP 200 and access_token","If the provider returns errors with HTTP 200, add a custom Converter<Map<String,Object>,OAuth2AccessTokenResponse> that detects error fields first and throws OAuth2ErrorException"],"exampleFix":"// before\n// provider returns {\"error\":\"invalid_grant\"} with HTTP 200 -> converter fails\n// after\n// set a tolerant converter\nconverter.setAccessTokenResponseConverter(params -> {\n  if (params.containsKey(\"error\")) throw new OAuth2ErrorException(new OAuth2Error((String) params.get(\"error\")));\n  return defaultConvert(params);\n});","handlingStrategy":"validation","validationCode":"if (!source.containsKey(OAuth2ParameterNames.ACCESS_TOKEN)) {\n    throw new OAuth2AuthorizationException(new OAuth2Error(\"invalid_token_response\", \"token response missing access_token\", null));\n}","typeGuard":"boolean hasAccessToken(Map<String,Object> body) {\n    return body != null && body.get(\"access_token\") instanceof String s && !s.isBlank();\n}","tryCatchPattern":"catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"access_token\")) {\n        // capture raw response body for diagnosis and fail with clear message\n    }\n}","preventionTips":["Check token endpoint responses with logging before production","Confirm grant_type and credentials so the provider returns a success body","Add a custom converter for providers that return errors with HTTP 200"],"tags":["oauth2","token-response","spec-compliance","parsing"],"backgroundTag":"unexpected-response-shape","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}