Having a bizarre issue in Mule 4.3 (Anypoint 7.5.1) using the HTTP Connector to POST to a url to and get back an access token that will be subsequently used in another call for bearer authentication.
The returning access_token when using the Mule4 HTTP Request is 1277 characters, whereas every other tool I have tried, Curl, Postman, Advanced Rest Client all return an access_token that is 1266 characters.
The larger token (from Mule) is not valid and fails subsequent requests with a 401. Wheres the smaller token (from Curl/Postman) is valid and can be copied into the Mule flow and pass authentication.
I am at a complete loss as to where the extra 11 characters in the access_token are coming from.
This is the request in Mule
<http:request
method="POST"
doc:name="get-authToken"
doc:id="e7a7fe05-4085-4d43-a2ee-998364ea7518"
config-ref="Workforce_Auth_Configuration"
path="/connect/token"
sendBodyMode="ALWAYS" target="authToken" targetValue="#[message]" requestStreamingMode="NEVER" outputMimeType="application/json" outputEncoding="UTF-8">
<http:body><![CDATA[#[%dw 2.0
output application/x-www-form-urlencoded
---
{
client_id: Mule::p("secure::workforce-api.client_id"),
client_secret: Mule::p("secure::workforce-api.client_secret"),
grant_type: "password",
scope: "openid profile api offline_access",
username: Mule::p("secure::workforce-api.username"),
password: Mule::p("secure::workforce-api.password")
}]]]></http:body>
<http:headers ><![CDATA[#[output application/java
---
{
"Accept-Encoding" : "gzip, deflate, br"
}]]]></http:headers>
</http:request>
<logger level="INFO" doc:name="Logger" doc:id="afafbd11-1081-4525-a32d-ef1a98284d81" message="********* GOT AUTH TOKEN *********"/>
<logger level="INFO" doc:name="authToken" doc:id="9f03d5dc-fe8f-40b1-be11-b0ea60526703" message="#[vars.authToken.payload.access_token]"/>
<logger level="INFO" doc:name="sizeOf" doc:id="df6879cf-ab78-4823-a6bb-072e84eb8c64" message='#["********* SIZE OF AUTH TOKEN " ++ sizeOf(vars.authToken.payload.access_token) as String]'/>
The logs end up with
LoggerMessageProcessor: ********* GOT AUTH TOKEN *********
LoggerMessageProcessor: eyJhbGciOiJSUzI1NiIsImtpZCI6ImNkYTlkODUyYjdmNGIyNWFlYTZlMjg4NmM2MGQzMTRlIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1OTQ4MjI2MTcsImV4cCI6MTU5NDgyNjIxNywiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5leHByZXNzcHJvcy5jb206NDQzIiwiYXVkIjpbImh0dHBzOi8vYWNjb3VudHMuZXhwcmVzc3Byb3MuY29tOjQ0My9yZXNvdXJjZXMiLCJhcGkiXSwiY2xpZW50X2lkIjoiZXhwcmVzc21vYmlsZSIsInN1YiI6ImQ4YjQ3OGJhLWQyMGUtNDljMi0zMTIzLTA4ZDRjOGQ4MjcyZCIsImF1dGhfdGltZSI6MTU5NDgyMjYxNywiaWRwIjoibG9jYWwiLCJuYW1lIjoia2xydXNzZWxsIiwiZGlzcGxheV9uYW1lIjoiS3Jpc3RvcGhlciBSdXNzZWxsIiwidXNlcl90eXBlIjoiV29ya2ZvcmNlIiwiZ2l2ZW5fbmFtZSI6IktyaXN0b3BoZXIiLCJmYW1pbHlfbmFtZSI6IlJ1c3NlbGwiLCJlbWFpbCI6ImtydXNzb2tjQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjoiVHJ1ZSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL21vYmlsZXBob25lIjoiNDA1Mjg2NDQ0NSIsIm1vYmlsZXBob25lX3ZlcmlmaWVkIjoiVHJ1ZSIsImlzX2FjdGl2ZSI6IlRydWUiLCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIiwiYXBpIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbInB3ZCJdfQ.QwREQmLZ2s7UjRwW07SkgFvvLu8fLgYmLag-XALBTM60y74oJpRLSq9vnDP8KEF33ATKiKxJP-A_lPqYWTHSAmF9HJDBA_KqIQ8Igo-9Vd5Xl_nU2ldcM5DBcoOrbudROYII70cmF7njU937EOcJ4STJ2cvTJnKZ8_xmT1A1cr5B7_IxsTMQJ2IzfCYh62RMks3_dqqZb5LZ4z4w7KTHZAwYtBFF6EdaLT1cNDvn2mHacMqKwRrrjOTDIJgx-3vNlkkCgbknYtiNbZhCrYKUzZgq-hS6pI-JFXckmHCP4U2Ohhw33TzUbRrEiC_8KXDSoK1lNDHNpIND2bZX_WuLuw
LoggerMessageProcessor: ********* SIZE OF AUTH TOKEN 1277
The request returns a json object that looks like
{
"access_token" : "eyJhbGciOiJSUzI1NiIsImtpZCI6ImNkYTlkODUyYjdmNGIyNWFlYTZlMjg4NmM2MGQzMTRlIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1OTQ4MjI2MTcsImV4cCI6MTU5NDgyNjIxNywiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5leHByZXNzcHJvcy5jb206NDQzIiwiYXVkIjpbImh0dHBzOi8vYWNjb3VudHMuZXhwcmVzc3Byb3MuY29tOjQ0My9yZXNvdXJjZXMiLCJhcGkiXSwiY2xpZW50X2lkIjoiZXhwcmVzc21vYmlsZSIsInN1YiI6ImQ4YjQ3OGJhLWQyMGUtNDljMi0zMTIzLTA4ZDRjOGQ4MjcyZCIsImF1dGhfdGltZSI6MTU5NDgyMjYxNywiaWRwIjoibG9jYWwiLCJuYW1lIjoia2xydXNzZWxsIiwiZGlzcGxheV9uYW1lIjoiS3Jpc3RvcGhlciBSdXNzZWxsIiwidXNlcl90eXBlIjoiV29ya2ZvcmNlIiwiZ2l2ZW5fbmFtZSI6IktyaXN0b3BoZXIiLCJmYW1pbHlfbmFtZSI6IlJ1c3NlbGwiLCJlbWFpbCI6ImtydXNzb2tjQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjoiVHJ1ZSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL21vYmlsZXBob25lIjoiNDA1Mjg2NDQ0NSIsIm1vYmlsZXBob25lX3ZlcmlmaWVkIjoiVHJ1ZSIsImlzX2FjdGl2ZSI6IlRydWUiLCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIiwiYXBpIiwib2ZmbGluZV9hY2Nlc3MiXSwiYW1yIjpbInB3ZCJdfQ.QwREQmLZ2s7UjRwW07SkgFvvLu8fLgYmLag-XALBTM60y74oJpRLSq9vnDP8KEF33ATKiKxJP-A_lPqYWTHSAmF9HJDBA_KqIQ8Igo-9Vd5Xl_nU2ldcM5DBcoOrbudROYII70cmF7njU937EOcJ4STJ2cvTJnKZ8_xmT1A1cr5B7_IxsTMQJ2IzfCYh62RMks3_dqqZb5LZ4z4w7KTHZAwYtBFF6EdaLT1cNDvn2mHacMqKwRrrjOTDIJgx-3vNlkkCgbknYtiNbZhCrYKUzZgq-hS6pI-JFXckmHCP4U2Ohhw33TzUbRrEiC_8KXDSoK1lNDHNpIND2bZX_WuLuw",
"expires_in" : 3600,
"token_type" : "Bearer",
"refresh_token" : "2fcacafc7f850f5812110a921302aab08c1c8124b7d0505691bba34c969210a2",
"scope" : "api offline_access openid profile"
}
Extracting the access token, you can see that it is 1277 characters, which confirms the log entry.
Running the same request in Curl
curl --location --request POST 'https://***************/connect/token'
--header 'Content-Type: application/x-www-form-urlencoded'
--data-urlencode 'client_id=***************'
--data-urlencode 'client_secret=***************'
--data-urlencode 'grant_type=password'
--data-urlencode 'scope=openid profile api offline_access'
--data-urlencode 'username=***************'
--data-urlencode 'password=***************'
Returns the following JSON
{"access_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6ImNkYTlkODUyYjdmNGIyNWFlYTZlMjg4NmM2MGQzMTRlIiwidHlwIjoiSldUIn0.eyJuYmYiOjE1OTQ4MjQ4NzcsImV4cCI6MTU5NDgyODQ3NywiaXNzIjoiaHR0cHM6Ly9hY2NvdW50cy5leHByZXNzcHJvcy5jb20iLCJhdWQiOlsiaHR0cHM6Ly9hY2NvdW50cy5leHByZXNzcHJvcy5jb20vcmVzb3VyY2VzIiwiYXBpIl0sImNsaWVudF9pZCI6ImV4cHJlc3Ntb2JpbGUiLCJzdWIiOiJkOGI0NzhiYS1kMjBlLTQ5YzItMzEyMy0wOGQ0YzhkODI3MmQiLCJhdXRoX3RpbWUiOjE1OTQ4MjQ4NzcsImlkcCI6ImxvY2FsIiwibmFtZSI6ImtscnVzc2VsbCIsImRpc3BsYXlfbmFtZSI6IktyaXN0b3BoZXIgUnVzc2VsbCIsInVzZXJfdHlwZSI6Ildvcmtmb3JjZSIsImdpdmVuX25hbWUiOiJLcmlzdG9waGVyIiwiZmFtaWx5X25hbWUiOiJSdXNzZWxsIiwiZW1haWwiOiJrcnVzc29rY0BnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6IlRydWUiLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9tb2JpbGVwaG9uZSI6IjQwNTI4NjQ0NDUiLCJtb2JpbGVwaG9uZV92ZXJpZmllZCI6IlRydWUiLCJpc19hY3RpdmUiOiJUcnVlIiwic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSIsImFwaSIsIm9mZmxpbmVfYWNjZXNzIl0sImFtciI6WyJwd2QiXX0.H9TEAtoKdXkHlcCMDSn_yVKLigF0m-OhUJpnQE202QSEUkmwPJqU28-Dy8neJen6gHpqChZ-nuQmffTONrelQRG6ASh9lO9jiA8_9AzWeJ54eHBF52sSsf36E149MAyDd9yK-K2Umzfk9U9Ob9bW0OX04ih4wvTo7X-lAifk9P4SZrjRgYsVL9IGumSMI6G3EFrshHORuYqL2QjhRVtaCr_A9cAieCzarztEjyvEIYCPHmkxY-P0xAQvhnrplEBbqvoGTajEuDeuCPQ4cSeM1LSYjJpKnST46zfkUoBc-7aztusWnvCF7zbzLleDN7CcvIn8-3JnXqNt-GO_Al5plA","expires_in":3600,"token_type":"Bearer","refresh_token":"311de44f613b547efc9172d3d3d6e7f587706d77804468a426954ef19d9e3f62","scope":"api offline_access openid profile"}
Extracting the access_token here shows it is 1266 characters.
If I copy this token and use in it in the subsequent Mule Requests, the requests are authorized and proceed as expected.
I thought that this could be an encoding issue, so I set the outputMimeType to application/json and the outputEncoding to "UTF-8", but it does not seem to make a difference.
Any thoughts on what could be causing this?
EDIT
After the recommendation to enable Wire Logging, I noticed that the content-lengths are different.
The Mule4 content-length is 171, whereas Curl and Postman are 177.
It looks like Mule is converting the scope from this script
%dw 2.0
output application/x-www-form-urlencoded
---
{
client_id: Mule::p("secure::workforce-api.client_id"),
client_secret: Mule::p("secure::workforce-api.client_secret"),
grant_type: "password",
scope: "openid profile api offline_access",
username: Mule::p("secure::workforce-api.username"),
password: Mule::p("secure::workforce-api.password")
}
to scope=openid+profile+api+offline_access
whereas Curl and Postman are using scope=openid%20profile%20api%20offline_access.
Could this be the issue?
If I try to make Mule4 to use the %20 in place of the '+', it turns into scope=openid%2520profile%2520api%2520offline_access which then gives me an invalid scope error.
EDIT
I don't think that is the issue. I stripped the profile down to just api and compared the results.
This time the content-lengths on the request are the same, however the access_tokens are still different by 11 characters.
The Curl/Postman token works whereas the Mule one does not.
Enable HTTP wire logging in the Mule 4 application and use verbose (-v) in the curl command line to compare both requests and responses raw. There might be some small difference in the requests. Also compare the headers in both.
Be sure to use the latest version of the HTTP connector just in case.
UPDATE:
Based on this new comment
scope=openid+profile+api+offline_access
whereas Curl and Postman are using
scope=openid%20profile%20api%20offline_access.
It looks like DataWeave is using this interpretation of URL encoding for forms while Curl is using percent encoding. It is a bit unclear but seems that both are valid.
I don't see any option to change the way it is encoding. It is a hack but you could replace the plus signs as text and see if that helps:
%dw 2.0
output text/plain
---
write({
password: "xxx",
scope: "openid profile api offline_access"
}, "application/x-www-form-urlencoded") replace "+" with ("%20")
Related
In the Keycloak server, we created a client that has an Access Type of confidential.
By calling /protocol/openid-connect/token endpoint with the client_id and the client_secret, we got the access_token, which was supposed to authenticate the application (client) in question in the follow-up requests.
Even though the token is valid (which I double-checked by calling the introspect endpoint), I am getting a 500 Error from the server for any request that has this access_token
So in short:
Calling this endpoint <base_url>/auth/realms/<realm>/protocol/openid-connect/token gives an access_token
Provided data: client_id,client_secret, grant_type: "client_credentials"
Calling <base_url>/auth/realms/<realm>/protocol/openid-connect/userinfo gives 500Internal Server Error
{
"error": "unknown_error"
}
Any insights on what the issue could be?
This should be fixed in Keycloak 13.0.0.
See this commit: https://github.com/keycloak/keycloak/commit/056b52fbbe5af06aab957d37405215f1f4ed6ecd
It is not quite clear from the question how you are sending the token to the userInfo endpoint, Provided that the access token you have received is valid make sure that you are sending the token in the request header. Try the below curl command:-
curl
-X GET
-H "Authorization: Bearer <Access Token>"
-H "Content-type: application/json"
http://{hostname}/auth/realms/{realm_name}/protocol/openid-connect/userinfo
I'd like to download an attachment from the conversation via REST API (Circuit Sandbox)
If I query the Conversation Item, I can see the attachments and within that the fileID. Then, if I am logged with a user account who is a member of the conversation, I can run the following to download the attachment or paste it in the browser where I am logged to the sandbox:
'''
start chrome https://circuitsandbox.net/rest/v2/fileapi?fileid=MyFileIdHere
'''
And that works. Is there a way to achieve the same with a Bot via REST?
A regular GET request will work.
curl https://circuitsandbox.net/rest/fileapi?fileid=<fileId> \
-H "Authorization: Bearer <ACCESS_TOKEN>"
and here is the REST notation.
GET rest/fileapi?fileid=<fileId> HTTP/1.1
Host: circuitsandbox.net
Authorization: Bearer <ACCESS_TOKEN>
The access token for a bot (client credentials grant) is obtained via OAuth 2.0:
curl https://circuitsandbox.net/oauth/token \
-d 'grant_type=client_credentials&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>&scope=READ_CONVERSATIONS,WRITE_CONVERSATIONS'
REST notation:
POST /oauth/token HTTP/1.1
Host: circuitsandbox.net
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=<CLIENT_ID>&client_secret=<CLIENT_SECRET>&scope=READ_CONVERSATIONS,WRITE_CONVERSATIONS'
I'm defining (small parts of) an existing API (Samanage) using OpenAPI to assist with some integration work.
I need to authenticate using Bearer auth, but by sending the token in a header other than Authorize.
The server expects Bearer authentication in a header named X-Samanage-Authorization like this example:
curl -H "X-Samanage-Authorization: Bearer <TokenGoesHere>" -H 'Accept: application/vnd.samanage.v2.1+json' -H 'Content-Type: application/json' -X GET https://api.samanage.com/incidents.json
I'm aware of https://swagger.io/docs/specification/authentication/bearer-authentication/, but it doesn't seem to help me fully.
This (OpenAPI 3)
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
...
security:
- bearerAuth: []
Results in an authentication header named the default (Authorization)
curl -X GET "https://api.samanage.com/incidents/12341234.json" -H "accept: application/json" -H "Authorization: Bearer <TokenGoesHere>"
Which then fails (401).
I feel like I want this:
components:
securitySchemes:
bearerAuth:
type: http
name: X-Samanage-Authorization
in: header
scheme: bearer
But that fails validation in Swagger Editor as I believe a type of http doesn't allow the name component (like a type of apiKey would). I couldn't quite make full sense of the docs here to be honest.
I did read about Specification Extensions but being completely new to OpenAPI, I couldn't find any examples on how to actually implement what I need.
Any insight much appreciated!
type: http is for HTTP authentication as defined by RFC 7235 and the IANA HTTP Authentication Scheme Registry. HTTP authentication, by definition, uses the Authorization header.
To use a custom header name, you need to define it as an API key (type: apiKey):
components:
securitySchemes:
bearerAuth:
type: apiKey
name: X-Samanage-Authorization
in: header
Note that since it's a non-standard Bearer scheme, the clients will need to manually add the "Bearer " prefix to the token value. For example, when you click "Authorize" in Swagger UI, you'll need to enter "Bearer TOKEN" instead of just "TOKEN".
I want to create a skype bot and am referring to https://developer.microsoft.com/en-us/skype/bots/docs/api/chat , for authorization the doc refers to https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-oauth-code/ but it is unclear about the scope to be mentioned for using skype bot apis.
So can anybody tell me how to obtain authorization token for making skype bot rest api calls.
try this
curl -X POST -H "Cache-Control: no-cache" -H "Content-Type: application/x-www-form-urlencoded" -d 'client_id=<your-app-id>&client_secret=<your-app-secret>&grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default' 'https://login.microsoftonline.com/common/oauth2/v2.0/token'
I'm trying to try token access to the skype bot.
I have also tried the above mentioned cURL function in "POSTMAN" but I have this return message:
{
"error": "invalid_request",
"error_description": "AADSTS90014: The request body must contain the following parameter: 'grant_type'.\r\nTrace ID: 53dcaec0-...\r\nCorrelation ID: d6d650ec...\r\nTimestamp: 2018-01-15 09:53:04Z",
"error_codes": [
90014
],
"timestamp": "2018-01-15 09:53:04Z",
"trace_id": "53dcaec0-...",
"correlation_id": "d6d650ec-..."
}
The following is a header and body structure in POSTMAN:
HEADER
Content_type: application/x-www-url-form-urlencoded
Cache-Control: no-cache
BODY
client_id=&client_secret=&grant_type=client_credentials&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
In BODY I replace the fields client_id and client_secret with the correct codes provided during registration.
Thanks to the availability...
I'm working with an API and I have to send a POST request. I know how to set a header (-H) and (-d) is the body, but what is "--user".
If I submit this with Postman, or in a text editor with axios or just regular XMLRequest, where do I add this?
The docs say it is for regular http auth.
curl -X POST -H "Content-Type: application/json" \
--user "<client_id>:<client_secret>" \
-d '{"grant_type": "client_credentials", "scope": "public"}' \
...
Late to the party, but here goes...
You can use curl with the -v (verbose) parameter to see the headers sent. You will then see that the information provided with --user is transformed into a header, such as:
Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l
The text after the Basic keyword is a base64 encoded text string of the username:password combination provided with the --user parameter
To manually generate the base64 encoded credentials on Linux, you can simply call:
echo -n "username:password" | base64 -w0
For windows, save the "username:password" to a file, then use certutil.exe to create a base64 encoded file:
certutil -encode credentials.txt credentials.asc
To test this end to end, you can remove --user username:password and substitute with --header Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l and it will still authenticate just fine.
In summary, to do this manually without curl, you would need to base64 encode username:password combination. You would then need to set the HTTP Authorization header with the type as Basic along with the base64 encoded string.
--user parameter in curl used for server authentication. So if you don't define authentication type via other parameters like --digest or --negotiate, it means USER parameter for http basic authentication, it also could be combined with :PASSWORD chunk to set a password as well. The full answer on your question depends on what kind authentication is used behind API you are sending request to, and maybe curl would not be enough for it, as it support a limited set of authentication schemes ...
--user (or -u) in curl provides a basic auth to your request.
In Postman you can achieve the same result with a choice in Authorization tab.
--user "<client_id>:<client_secret>" becomes
Type: Basic Auth
Username: client_id
Password: client_secret
Specify the user name and password to use for server authentication. If you simply specify the user name, curl will prompt for a password.
If your curl request does not have any -- user, then
server that requires authentication sends back a 401 response code and an associated WWW-Authenticate: header that lists all the authentication methods that the server supports.
< HTTP/1.1 401
< WWW-Authenticate: Basic realm="oauth2/client"
Then you will know the server is using Basic authentication
You can add --basic to explicitly tell it is Basic authentication
Please refer to HTTP authentication for more information
Sometimes (depending on server implementation) the --user will negotiate a digest authenticated session. The headers for digest users are a one-time use. I believe a request to the server will first fail with a 401, but include a WWW-Authenticate response, including the digest realm, and the nonce secret. With these, a second request can be made with a new header Authorization value.
example:
Authorization: Digest username="LXAIQKBC", realm="MMS Public API", nonce="rE3sYnLXEhVMbh72JyUK7kfLIb+bAbKj", uri="/api/atlas/v1.0/groups", cnonce="YTVhM4YwMDB3ZjZjMTkxbCNiODA1ODnxZDFjOGMyMzE=", nc=00000001, qop=auth, response="7a5fcb8e4f92a665315bf62cdd87a67d", algorithm="MD5"
As an addition to Jahmic's answer, Nodejs programmers can do this to convert to base64 string:
const cryptoJS = require("crypto-js");
const base64Str = cryptoJS.enc.Base64.stringify(cryptoJS.enc.Utf8.parse(`${username}:${password}`))