-
Notifications
You must be signed in to change notification settings - Fork 3.7k
Description
Describe the bug
When using Keycloak as Oauth2 provider you cannot get any Tokens via the C++ and Python client due to the missmatch in Content-Type.
The C++/python client sends the POST to the token-endpoint with Content-Type "application/json" but keycloak only supports/allows the content-type "application/x-www-form-urlencoded" and you will get a 400 Bad Request error because the credentials like client-id and client secret are missing.
Error on Python side:
2020-12-16 14:12:10.835 ERROR [140634212321088] AuthOauth2:312 | Response failed for issuerurl https://mysite.com/auth/realms/myrealm/.well-known/openid-configuration. response Code 400 passedin: { "grant_type": "client_credentials", "client_id": "my-client-id", "client_secret": "my-secret", "audience": "my-audience" }
Note: The error is a but wrong because it prints the wrong URL. The client can get the token-endpoit url just finde via the .well-known url. The error arises when it wants to get a token.
To Reproduce
- Download Keycloak standalone and setup a Client.
- Download and setup python + pulsar python client.
- Try to get a token with the python-client (small code below)
python code:
`creds = {
"grant_type": "client_credentials",
"client_id": "my-client-id",
"client_secret": "my-secret",
"issuer_url": "https://myurl.ai/auth/realms/myrealm",
"audience": "my-audience",
}
client = pulsar.Client(
"pulsar+ssl://localhost:6651",
use_tls=True,
tls_trust_certs_file_path="my-cert.crt",
authentication=AuthenticationOauth2(json.dumps(creds)),
)
my_topic = "test-topic"
producer = client.create_producer(topic=my_topic, send_timeout_millis=20000)`
Expected behavior
The python client should be able the get a token and authenticate with the pulsar broker.
Desktop (please complete the following information):
- OS: Ubuntu 19.10
- Pulsar cluster and python client: 2.7
- keycloak: 10.3
Additional context
I made a small change in the C++ Client code to get it to work for now. The optimal solution would be that you can choose via the AuthenticationOauth2 class in python which content-type you want to send the POST in.
Changes made in pulsar-client-cpp/lib/auth/AuthOauth2.cc in function Oauth2TokenResultPtr ClientCredentialFlow::authenticate():
First change content-type to application/x-www-form-urlencoded.
list = curl_slist_append(list, "Content-Type: application/x-www-form-urlencoded");
Then we need to add the client credential parameters to the POST:
Note: As you can see i commented out the part with the Json-parameters and built the string on my own. I am sure there are way better solutions but i just wanted to get it to work for now.
// fill in the request data
// boost::property_tree::ptree pt;
// pt.put("grant_type", "client_credentials");
// pt.put("client_id", clientId_);
// pt.put("client_secret", clientSecret_);
// pt.put("audience", audience_);
//boost::property_tree::json_parser::write_json(ss, pt);
std::stringstream ss;
ss << "grant_type" << "=" << "client_credentials" << "&" << "client_id" << "=" << clientId_ << "&" << "client_secret" << "=" << clientSecret_ << "&" << "audience" << "=" << audience_;
std::string ssString = ss.str();
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, ssString.c_str());
The rest is the same.