swagger-api / swagger-api/swagger-codegen

[Java][Retrofit2] OAuth does not support Client ID and Secret as HTTP Basic Auth

Open
#7,648 2 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Client: Java Issue: Bug
Dominant language
Mustache
Stars
17.8k
Forks
6k
PR merge metrics
No merged PRs in 30d

Description

Description

The Java clients using Oltu (Feign, Retrofit, Retrofit2) always send the Client ID and Secret in the url-encoded body during the Client Credential flow. This fails for strict OAuth2 server implementations that require it in the HTTP Basic Auth header and reject its appearance in the body.

The OAuth2 RFC 6749 - Section 2.3.1 states:

The authorization server MUST support the HTTP Basic
authentication scheme for authenticating clients that were issued a
client password.

...

Alternatively, the authorization server MAY support including the
client credentials in the request-body using the following
parameters:

Swagger-codegen version

2.2.1

Suggest a fix/enhancement

The problem lies partially in Oltu, but this line in OAuth.updateAccessToken is where the issue starts:

  OAuthJSONAccessTokenResponse accessTokenResponse = oauthClient.accessToken(this.tokenRequestBuilder.buildBodyMessage());

A workaround is to override TokenRequestBuilder:

public OAuthClientRequest buildBodyMessage() throws OAuthSystemException {
  // constructor is not not visible here, so let the parent create it
  OAuthClientRequest message = super.buildBodyMessage();

  // now undo what the parent did...
  Map<String, Object> paramsNoAuth = new HashMap<String, Object>(this.parameters);
  paramsNoAuth.remove(OAuth.OAUTH_CLIENT_ID);
  paramsNoAuth.remove(OAuth.OAUTH_CLIENT_SECRET);
		
  String body = OAuthUtils.format(paramsNoAuth.entrySet(), "UTF-8");
  message.setBody(body);
  
  // add basic-auth client_id:client_seret
  String id = (String) parameters.get(OAuth.OAUTH_CLIENT_ID);
  String secret = (String) parameters.get(OAuth.OAUTH_CLIENT_SECRET);
  String credentials = id + ":" + secret;
  String base64Credentials = Base64.getEncoder().encodeToString(credentials.getBytes());
  message.addHeader("Authorization", "Basic " + base64Credentials);
  return message;
}

Then change the generated OAuthOkHttpClient to read those headers (as the Oltu URLConnectionClient does )

// existing code
if(headers != null) {
    for (Entry<String, String> entry : headers.entrySet()) {
        if (entry.getKey().equalsIgnoreCase("Content-Type")) {
            mediaType = MediaType.parse(entry.getValue());
        } else {
            requestBuilder.addHeader(entry.getKey(), entry.getValue());
        }
    }
}

// added code
if (request.getHeaders() != null) {
    for (Entry<String, String> entry : request.getHeaders().entrySet()) {
        if (entry.getKey().equalsIgnoreCase("Content-Type")) {
            mediaType = MediaType.parse(entry.getValue());
        } else {
            requestBuilder.addHeader(entry.getKey(), entry.getValue());
        }
    }
}

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with modules/swagger-codegen/src/main/resources/Java/libraries/retrofit2/auth/OAuth.mustache and OAuthOkHttpClient.mustache, then trace how OAuthClientRequest headers reach the generated OkHttp request. Verify the generated Retrofit2 client sends client credentials via HTTP Basic Auth rather than the request body during the client-credentials flow, while preserving other request headers.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, authentication
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.