{"record":{"id":"1eded593cf25fd46","repo":"spring-projects/spring-security","slug":"oauth2error-read-from-www-authenticate-header-or-e","errorCode":null,"errorMessage":"OAuth2Error read from WWW-Authenticate header or error response body (dynamic)","messagePattern":"OAuth2Error read from WWW-Authenticate header or error response body \\(dynamic\\)","errorType":"exception","errorClass":"OAuth2AuthorizationException","httpStatus":null,"severity":"error","filePath":"oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java","lineNumber":69,"sourceCode":"\tprivate final ResponseErrorHandler defaultErrorHandler = new DefaultResponseErrorHandler();\n\n\t@Override\n\tpublic boolean hasError(ClientHttpResponse response) throws IOException {\n\t\treturn this.defaultErrorHandler.hasError(response);\n\t}\n\n\t@Override\n\tpublic void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {\n\t\tif (HttpStatus.BAD_REQUEST.value() != response.getStatusCode().value()) {\n\t\t\tthis.defaultErrorHandler.handleError(url, method, response);\n\t\t}\n\t\t// A Bearer Token Error may be in the WWW-Authenticate response header\n\t\t// See https://tools.ietf.org/html/rfc6750#section-3\n\t\tOAuth2Error oauth2Error = this.readErrorFromWwwAuthenticate(response.getHeaders());\n\t\tif (oauth2Error == null) {\n\t\t\toauth2Error = this.oauth2ErrorConverter.read(OAuth2Error.class, response);\n\t\t}\n\t\tthrow new OAuth2AuthorizationException(oauth2Error);\n\t}\n\n\tprivate @Nullable OAuth2Error readErrorFromWwwAuthenticate(HttpHeaders headers) {\n\t\tString wwwAuthenticateHeader = headers.getFirst(HttpHeaders.WWW_AUTHENTICATE);\n\t\tif (!StringUtils.hasText(wwwAuthenticateHeader)) {\n\t\t\treturn null;\n\t\t}\n\t\tBearerTokenError bearerTokenError = getBearerToken(wwwAuthenticateHeader);\n\t\tif (bearerTokenError == null) {\n\t\t\treturn new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, null, null);\n\t\t}\n\t\tString errorCode = (bearerTokenError.getCode() != null) ? bearerTokenError.getCode()\n\t\t\t\t: OAuth2ErrorCodes.SERVER_ERROR;\n\t\tString errorDescription = bearerTokenError.getDescription();\n\t\tString errorUri = (bearerTokenError.getURI() != null) ? bearerTokenError.getURI().toString() : null;\n\t\treturn new OAuth2Error(errorCode, errorDescription, errorUri);\n\t}\n","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/http/OAuth2ErrorResponseErrorHandler.java#L51-L87","documentation":"OAuth2ErrorResponseErrorHandler is installed as the RestClient response error handler for OAuth2 calls. When the resource/token endpoint replies with an error status, it parses a Bearer-token error from the WWW-Authenticate header (RFC 6750) or, failing that, from the JSON error body, and throws OAuth2AuthorizationException carrying that OAuth2Error. The message is dynamic — it reflects the upstream server's error code/description.","triggerScenarios":"Thrown in handleError() whenever the protected endpoint returns an HTTP error status: e.g., WWW-Authenticate: Bearer error=\"invalid_token\", or an error body like {\"error\":\"invalid_grant\"}.","commonSituations":"Expired or revoked access token used against a resource server (invalid_token/insufficient_scope in WWW-Authenticate), token endpoint rejecting the grant (invalid_grant), or calling userinfo with a malformed token.","solutions":["Read getError().getErrorCode() from the resulting OAuth2AuthorizationException to see the server's error code (e.g., invalid_token, insufficient_scope, invalid_grant).","If invalid_token: obtain a fresh access token or refresh it before the request.","If insufficient_scope: request the missing scope in the authorization request.","If invalid_grant: fix the credentials/grant being sent to the token endpoint."],"exampleFix":"// before: reuse a cached token forever\nString token = cachedToken;\n// after: refresh on invalid_token\nif (isOAuth2Error(ex, \"invalid_token\")) { token = refreshAccessToken(); retry(request, token); }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"static boolean isBearerError(OAuth2AuthorizationException ex, String code) {\n    return ex.getError() != null && code.equals(ex.getError().getErrorCode());\n}","tryCatchPattern":"try {\n    ResponseEntity<String> r = rest.exchange(url, GET, new HttpEntity<>(headers(authToken)), String.class);\n} catch (HttpStatusCodeException ex) {\n    // handler will translate this into OAuth2AuthorizationException with the bearer error\n    if (ex.getResponseHeaders().getFirst(HttpHeaders.WWW_AUTHENTICATE) != null) {\n        refreshTokenAndRetry();\n    }\n}","preventionTips":["Refresh access tokens proactively before expiry instead of waiting for invalid_token.","Check WWW-Authenticate on any 401 to learn the exact bearer error code.","Request all needed scopes up front to avoid insufficient_scope.","Never cache access tokens longer than their expires_in."],"tags":["oauth2","http","bearer-token","www-authenticate","spring-security"],"backgroundTag":"http-error-response","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}