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:
Start a netcat server to catch the callback from the browser window: $ nc -lk localhost 3002
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
Click through the login flow with Google as the provider.
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)
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?
This is a confirmed bug in Postman when both the callback_uri and the token OAuth server endpoints are on the same (localhost) domain.
https://github.com/postmanlabs/postman-app-support/issues/7105
Update: This issue was fixed in September 2020.
Related
I have the following requests to get a bearer token from the Docker Hub API and then use that token for a GET request to the given endpoint:
#!/usr/bin/env bash
raw_token="$(curl https://auth.docker.io/token?service=registry.docker.io&scope=repository:library/ubuntu:list)";
token="$(node -pe "JSON.parse('$raw_token').token")"
curl -i -H "Authorization: Bearer $token" https://registry-1.docker.io/v2/library/ubuntu/tags/list
If I run that, I get this error:
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
Docker-Distribution-Api-Version: registry/2.0
Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="repository:library/ubuntu:pull",error="insufficient_scope"
Date: Wed, 20 Jun 2018 08:02:24 GMT
Content-Length: 157
Strict-Transport-Security: max-age=31536000
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"repository","Class":"","Name":"library/ubuntu","Action":"pull"}]}]}
Does anyone know how I can make the right request, and get the right token?
When you get token, you specify scope=repository:library/ubuntu:list. When you use token, it says you need scope="repository:library/ubuntu:pull".
I want to revoke a token provided to the user through an OAuth2 login. I want to do an HTTP POST request from a JavaScript app. The WSO2 documentation provides a Curl example.
Tried:
Tried with curl by providing the syntax. It works perfectly fine
curl -X POST --basic -u "clientid:clientsecret" -H "Content-Type: application/x-www-form-urlencoded;charset=UTF-8" -k -d "token=tokenfromwso2&token_type_hint=access_token" https://localhost:9443/oauth2/revoke
Tried to do the same using Http Request but the server returned 500 internal server error.
Header:
POST /oauth2/revoke HTTP/1.1
Host: localhost:9443
Connection: keep-alive
Content-Length: 14
Accept: application/json, text/plain, */*
Origin: http://localhost:4200
Authorization: Basic clientid:clientsecret
User-Agent: Mozilla/5.0 (BB10; Touch) AppleWebKit/537.10+ (KHTML, like Gecko) Version/10.0.9.2372 Mobile Safari/537.10+
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Referer: http://localhost:4200/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Body:
{"token":access_token}
Grant Type is not implicit so I do not want the user to redirect to wso2 Identity server. I even tried to encode the client id secret but that too returned an internal server error.
You need to base64 encode the client id and secret. Also, you need to provide the token as url encoded value. What you have given is a json payload for the body. Sample request can be found below.
I have successfully registered my application for graph.microsoft.com, but now it also needs to work with graph.cloudapi.de.
The Application Registration Portal does not seem to be the correct one for the German National Cloud.
Moreover, applications registered in portal.microsoftazure.de only work with the SharePoint API, not Graph.
Here's a sample HTTP exchange between my program and the German Microsoft Cloud. I am hand coding HTTP requests in Delphi. The exchange works with graph.microsoft.com, but not graph.microsoft.de.
I start the authentication via the following https URL:
login.microsoftonline.de/common/oauth2/v2.0/authorize?response_type=code&client_id=xyz&prompt=login&scope=https%3A%2F%2Fgraph.microsoft.de%2Fuser.read%20&response_mode=query&state=5736109994698155204&redirect_uri=https%3A%2F%2Fwww.syncovery.com%2Foauthresult.php
The cloud then directs to my redirect URL with a long code parameter, such as
code=AQABAAIAAQDnLpu3ikefR73l_aNlxt5xxdvNhQ9JVAI7b0ciTej............
So far, so good. Now the hard part:
POST /common/oauth2/v2.0/token HTTP/1.1
Host: login.microsoftonline.de
Keep-Alive: 300
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 805
client_id=xyz&client_secret=abc&code=AQABAAIAAQD_very_long&redirect_uri=https%3A%2F%2Fwww.xyz.com%2Foauthresult.php&grant_type=authorization_code&scope=https%3A%2F%2Fgraph.microsoft.de%2Fuser.read%20
Reply:
HTTP/1.1 401 Unauthorized
Cache-Control: no-cache, no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.5
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
x-ms-request-id: e83986c0-5da4-4af7-92fc-46e2fb950100
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
Set-Cookie: esctx=AQABAAAAA_longish domain=.login.microsoftonline.de; path=/; secure; HttpOnly
X-Powered-By: ASP.NET
Date: Wed, 07 Feb 2018 11:11:44 GMT
Content-Length: 449
{
"error": "invalid_client",
"error_description": "AADSTS70002: Error validating credentials. AADSTS50012: Invalid client secret is provided.\r\nTrace ID: e83986c0-5da4-4af7-92fc-46e2fb950100\r\nCorrelation ID: 044f44e3-ec09-4f76-b073-0ff6b72b696a\r\nTimestamp: 2018-02-07 11:11:45Z",
"error_codes": [70002, 50012],
"timestamp": "2018-02-07 11:11:45Z",
"trace_id": "e83986c0-5da4-4af7-92fc-46e2fb950100",
"correlation_id": "044f44e3-ec09-4f76-b073-0ff6b72b696a"
}
The apps.dev.microsoft.com portal is global, there isn't a national cloud instance of the portal. You do however need to register your app under an AAD account using "Azure AD only applications" rather than "Converged applications". This is because the v2 Endpoint itself isn't supported by national cloud endpoints yet.
Another problem here is that your URI is also incorrect.
Some notes from the documentation related to Germany:
Microsoft Graph Root Endpoint: https://graph.microsoft.de (not graph.cloudapi.de)
OAuth2.0 Endpoint: https://login.microsoftonline.de
The Azure AD v2.0 authorization and token endpoints are available on the global service only; they are not yet supported for use with national cloud deployments.
I'm attempting to get a catalog listing for Docker hub, but so far I'm just getting an error in response. My understanding is I'd need to pass a bearer token with the catalog request, so I start by getting that token with the related scope:
curl -u "username:password "https://auth.docker.io/token?service=registry.docker.io&scope=registry:catalog:*"
(this is using username/password from my Docker Hub account)
I then pass the returned token to the registry:
curl -vL -H "Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6I(...)" https://registry-1.docker.io/v2/_catalog
In response to that request, I'm getting:
* Trying 54.86.130.73...
* Connected to registry-1.docker.io (54.86.130.73) port 443 (#0)
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate: *.docker.io
* Server certificate: RapidSSL SHA256 CA - G3
* Server certificate: GeoTrust Global CA
> GET /v2/_catalog HTTP/1.1
> Host: registry-1.docker.io
> User-Agent: curl/7.43.0
> Accept: */*
> Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6I(...)
>
< HTTP/1.1 401 Unauthorized
HTTP/1.1 401 Unauthorized
< Content-Type: application/json; charset=utf-8
Content-Type: application/json; charset=utf-8
< Docker-Distribution-Api-Version: registry/2.0
Docker-Distribution-Api-Version: registry/2.0
< Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="registry:catalog:*",error="insufficient_scope"
Www-Authenticate: Bearer realm="https://auth.docker.io/token",service="registry.docker.io",scope="registry:catalog:*",error="insufficient_scope"
< Date: Fri, 06 May 2016 23:00:08 GMT
Date: Fri, 06 May 2016 23:00:08 GMT
< Content-Length: 134
Content-Length: 134
< Strict-Transport-Security: max-age=31536000
Strict-Transport-Security: max-age=31536000
<
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Name":"catalog","Action":"*"}]}]}
...which seems to be asking me to go back and get authorized with the URL I entered above.
Should this be possible? If so, what am I missing?
I got the same problem, raised this with docker support and got the following answer,
"The catalog endpoint does not work against Docker Hub because that endpoint actually lists all the repositories on a Registry, and we disabled it as it would list all repositories on Docker Hub."
If you want repository information for a username or organization, an alternative is to use V1 api. The problem with this is you need to make multiple calls based on the number of repos available.
The following command will give the repos available for a given username :
curl -k -H "Authorization: Basic <encripted username:password>" -H "Accept: application/json" -X GET "https://index.docker.io/v1/search?q=<username>&n=100"
Here q is the query and n is the number of items to be returned in each page.
Another call can be made based on what data you need by passing repo as a parameter.
I have a WebAPI service deployed on a server. There is also an MVC app for testing the API. One such tester runs on my dev machine and another copy (same version) runs on the server where API resides.
The MVC tester app supports calling API directly and also through a built-in 'proxy' (http handler) to bypass the "Access-Control-Allow-Origin" errors. So for example if I run the tester app on my dev machine and I want to receive data from the server, I must use the proxy. This setup works nicely and all of the calls are going through and data is passed properly.
Problem occurs when I don't provide enough inputs (for testing purposes) and API generates a "400 Bad request" error. It only happens when I make a call from my dev machine to remote machine and works fine if I make the same call on the server.
I tested with Postman on the remote server:
post directly to API: POST to 177.77.77.77/v1/feature {JSON payload}
the response I get back contains proper headers, a body with a JSON object describing the error. Same thing happens when I send the same exact command but through server's proxy:
post through proxy: POST to 177.77.77.77/proxy/feature {JSON payload}
both results are identical and this is the expected behavior, which I think allows to make a conclusion that proxy is working and API is working.
When I go back to my dev machine and try the same calls, my result for posting directly to API is the same as above but if I use the server's proxy something else happens. Here's output from Fiddler for the working case (from dev machine directly to API):
POST http://177.77.77.77/v1/feature HTTP/1.1
Host: 177.77.77.77
Connection: keep-alive
Content-Length: 514
User-Agent: Mozilla/5.0 xx..
Cache-Control: no-cache
Origin: chrome-extension://xx-postman-xx
Authorization: Basic pwd=
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
{payload}
response:
HTTP/1.1 400 Bad Request
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Mon, 30 Mar 2015 15:48:52 GMT
Content-Length: 139
{"code":"INVALID_REQUEST","message":"proper error message"}
this is the correct behavior with expected message body (JSON) and correct content-lenght. If I make a request to the server's proxy, my request becomes:
POST 177.77.77.77/proxy/feature HTTP/1.1
Host: 177.77.77.77
Connection: keep-alive
Content-Length: 514
User-Agent: Mozilla/5.0 xx..
Cache-Control: no-cache
Origin: chrome-extension://xx-postman-xx
Authorization: Basic pwd=
Content-Type: application/json
Accept: */*
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
Cookie: appSettings=xx
{payload}
then the response I get is:
HTTP/1.1 400 Bad Request
Cache-Control: private
Content-Type: text/html
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 30 Mar 2015 15:51:37 GMT
Content-Length: 11
Bad Request
the body/payload of the response is lost or replaced by status, content-type changed from JSON to text/html and cache-control became private.
The cookie in the non-working case plays no role. I tried removing it and result is still wrong.
Why do you think this is happening? How can I troubleshoot this and find what portion of code (or maybe IIS setting?) is responsible for sending a different response for what seems to be same requests? After all everything works fine when I run only on dev or only on remote. Could this be some permission issue or something in IIS on the server?
I tried remote-debugging of the proxy code where I would step through the code while sending Post commands (with the same payload) through Postman. In one scenario I ran Postman on the server and made a request to server's proxy and in another scenario I ran Postman on dev and also made requests to server's proxy. In VS I can see that I'm going through the same path in code for both scenarios and that API's response is the same.
Thanks for reading and any attempts to help will be greatly appreciated!
added this to web.config:
<system.webServer>
<httpErrors existingResponse="PassThrough"></httpErrors>
</system.webServer>
found the solution here:
In IIS7.5 what module removes the body of a 400 Bad Request