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());
}
}
}