Search code examples
oauth-2.0postmankeycloak

Postman using wrong (nonsense?) code in OAuth2 flow against Keycloak server


I'm attempting to use Postman (Version 7.20.0 - linux 5.5.8-200.fc31.x86_64 / x64)to authenticate using the OAuth2.0 "Authorization Code" flow against a Keycloak 9.0.0 server, backed by Google as an IdP.

Postman is sending the following to the .../token endpoint when trying to exchange the code for the access/refresh tokens:

grant_type:    authorization_code
code:          4/xgFPM8rkZXA1pWguPMHPKg8GS3BrI7whtmSq2U2K4_4Cy62m10y2l3IQp3KuiLRyaLaZWKCUiGJGEWVJ9K4zcTc
redirect_uri:  http://localhost:3002
client_id:     mission-control
client_secret: 3cc09c80-••••-••••-••••-••••••••

This fails in Keycloak with the following error, confirmed in the Postman console:

POST /auth/realms/test-realm/protocol/openid-connect/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
User-Agent: PostmanRuntime/7.23.0
Accept: */*
Cache-Control: no-cache
Postman-Token: f2cfc8be-a911-4bc6-b5be-dbfab46d3a56
Host: localhost:8080
Accept-Encoding: gzip, deflate, br
Content-Length: 246
Connection: keep-alive
grant_type=authorization_code&code=4%2FxgFPM8rkZXA1pWguPMHPKg8GS3BrI7whtmSq2U2K4_4Cy62m10y2l3IQp3KuiLRyaLaZWKCUiGJGEWVJ9K4zcTc&redirect_uri=http%3A%2F%2Flocalhost%3A3002&client_id=mission-control&client_secret=3cc09c80-••••-••••-••••-••••••••
HTTP/1.1 400
Connection: keep-alive
Cache-Control: no-store
Pragma: no-cache
Content-Type: application/json
Content-Length: 62
Date: Fri, 13 Mar 2020 08:36:02 GMT
{"error":"invalid_grant","error_description":"Code not valid"}

Keycloak logs show that this token has the wrong format:

keycloak_1  | 09:53:23,219 WARN  [org.keycloak.protocol.oidc.utils.OAuth2CodeParser] (default task-35) Invalid format of the code
keycloak_1  | 09:53:23,219 WARN  [org.keycloak.events] (default task-35) type=CODE_TO_TOKEN_ERROR, realmId=Test Realm, clientId=mission-control, userId=null, ipAddress=172.20.0.1, error=invalid_code, grant_type=authorization_code, client_auth_method=client-secret

To check whether Keycloak, or Postman was at fault I walked through the same steps on the CLI with the help of Netcat:

On the CLI, with help of netcat I can walk through the flow successfully, and I see a different token format:

  1. Start a netcat server to catch the callback from the browser window: $ nc -lk localhost 3002

  2. Open this in my browser http://localhost:8080/auth/realms/test-realm/protocol/openid-connect/auth?client_id=mission-control&redirect_uri=http%3A%2F%2Flocalhost%3A3002&response_type=code&scope=openid

  3. Click through the login flow with Google as the provider.
  4. The netcat server will receive something like this GET /?code=3b9ac786-f9d1-40f9-b884-35e17b2fa756.70a3be09-8edf-47ed-9803-d08550a07faa.8794bba2-6f2b-4512-8bd7-6d5073852d1c (and more)
  5. I can exchange this code for tokens successfully: curl -XPOST -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=authorization_code&code=3b9ac786-f9d1-40f9-b884-35e17b2fa756.70a3be09-8edf-47ed-9803-d08550a07faa.8794bba2-6f2b-4512-8bd7-6d5073852d1c&redirect_uri=http%3A%2F%2Flocalhost%3A3002&client_id=mission-control&client_secret=3cc09c80-48bc-46fd-bc91-232e6bbb681a" http://localhost:8080/auth/realms/test-realm/protocol/openid-connect/token

I don't know where the Postman OAuth flow is getting the "code" out of the response body, which it uses to send to the token exchange endpoint The difference in the tokens is clear, when walking through it by hand (same client, same token, same oauth2 server) then the code loosk like:

3b9ac786-f9d1-40f9-b884-35e17b2fa756.70a3be09-8edf-47ed-9803-d08550a07faa.8794bba2-6f2b-4512-8bd7-6d5073852d1c

When using Postman, it's sending this as the code:

4/xgFPM8rkZXA1pWguPMHPKg8GS3BrI7whtmSq2U2K4_4Cy62m10y2l3IQp3KuiLRyaLaZWKCUiGJGEWVJ9K4zcTc

What can I do to make postman take the callback ?code form the URL?


Solution

  • This is a confirmed bug in Postman when both the callback_uri and the token OAuth server endpoints are on the same (localhost) domain.

    Update: This issue was fixed in September 2020.