Cognito Service Authentication returns valid JWT but subsequent JWT validations fail - authentication

I have a lambda that accepts a username and password and returns a JWT. The code (seen below) basically uses cognito adminInitiateAuth() to use the username and password to get the JWT.
let USER_POOL_CLIENT_ID = "6adc4ziG7GCzYmMwhWWVnJySbP";
let USER_POOL_ID = "us-east-1_upxvqiJUP";
AWS.config.update({region: 'us-east-1'});
const cognito = new AWS.CognitoIdentityServiceProvider();
let params = {
AuthFlow: "ADMIN_NO_SRP_AUTH",
ClientId: USER_POOL_CLIENT_ID,
UserPoolId: USER_POOL_ID,
AuthParameters: {
USERNAME: event.username,
PASSWORD: event.password
}
};
//console.log(`${JSON.stringify(params)}`);
const jwtContainer = await cognito.adminInitiateAuth(params).promise();
console.log(`jwtContainer = ${JSON.stringify(jwtContainer)}`);
This seems to work and, in fact, does return a valid JWT with a kid decoded by jwt.io. Whenever I attempt to use the JWT on a different endpoint that is JWT protected (as seen below) I get an error:
curl -v https://myapi.execute-api.us-east-1.amazonaws.com/reset-service-auth-password \
-H 'Accept-Language: en-US,en;q=0.9' \
-X POST \
-d '{"password": "xyz123","confirmPassword": "22221"}' \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "Origin: http://localhost:8080" \
-H 'Authorization: Bearer jwtdataxxxxxxxxxxxxxxxx'
But when I execute the above I get this error:
www-authenticate: Bearer scope="aws.cognito.signin.user.admin" error="invalid_token" error_description="unable to find an appropriate key in the JWKS. Ensure a "kid" is provided in the JWT, and that your JWKS has a supported cert available for this ID"
I checked my JWKS and the kid in the JWT is in fact in the JWKS. I am not sure what to do next or what is really wrong.
My authorizer expects the scope aws.cognito.signin.user.admin and my client does not require a secret. Please ask any clarification that you might need to help me with this issue.

I've had the same issue today. I hope you figured it out since then but, just in case, and for others...
I followed the same investigation steps that you did. I was convinced there was something wrong with AWS. There's not.
In my case, the token was generated against our production pool, but a development endpoint leaked in the config, and we were trying to authenticate on a wrong endpoint.
So my guess is that you got something similar: recheck your URLs, there's probably a mismatch between your pool ID and the API you request.

Related

NPE in Keycloak internal token-exchange

I'm trying to achieve an internal token exchange in Keycloak 17.0.1, however, the server returns an unknown error (NullPointerException).
My scenario is: I have three microservices, A, B, and C. A calls B, which is an intermediate service that needs to call service C. So, I don't want to propagate the original token (A) to call (C). Instead, I want to exchange the token, so B makes a token-exchange request to Keycloak to get a new token and then calls service C.
What I have done:
I have a client "original" who has his own client-id/client-secret
I created another client "target" and configured the policy for token exchange, assuming the "original" client in that policy.
And finally the cURL call:
curl -L -X POST 'http://localhost:8080/realms/myrealm/protocol/openid-connect/token' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=target' \
--data-urlencode 'client_secret=<< TARGET SECRET >>' \
--data-urlencode 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange' \
--data-urlencode 'subject_token=<< ORIGINAL CLIENT TOKEN >>' \
--data-urlencode 'requested_token_type=urn:ietf:params:oauth:token-type:refresh_token' \
--data-urlencode 'audience=original'
Response:
2022-04-19 16:05:16,154 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-37) Uncaught server error: java.lang.NullPointerException
at org.keycloak.protocol.oidc.TokenManager.attachAuthenticationSession(TokenManager.java:539)
at org.keycloak.protocol.oidc.DefaultTokenExchangeProvider.exchangeClientToOIDCClient(DefaultTokenExchangeProvider.java:336)
at org.keycloak.protocol.oidc.DefaultTokenExchangeProvider.exchangeClientToClient(DefaultTokenExchangeProvider.java:315)
at org.keycloak.protocol.oidc.DefaultTokenExchangeProvider.tokenExchange(DefaultTokenExchangeProvider.java:233)
at org.keycloak.protocol.oidc.DefaultTokenExchangeProvider.exchange(DefaultTokenExchangeProvider.java:123)
at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.tokenExchange(TokenEndpoint.java:789)
at org.keycloak.protocol.oidc.endpoints.TokenEndpoint.processGrantRequest(TokenEndpoint.java:204)
at jdk.internal.reflect.GeneratedMethodAccessor344.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:170)
at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:660)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:524)
at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:474)
at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:476)
at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:434)
at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:192)
at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:152)
Am I missing something?
UPDATE
The only way I've managed it to work was to "force" a session to be created in Keycloak by using a "password" grant type in the request of client A. So, I created a user foo and got a token in this way:
POST http://localhost:{{keycloak_port}}/realms/{{keycloak_realm}}/protocol/openid-connect/token
Authorization: Basic original:12345
Content-Type: application/x-www-form-urlencoded
grant_type=password
&username=foo
&password=bar
This way, a session was created for the client original and the token exchange request for the target client did work.
I'm wondering if it is a correct approach, though.
client configuration
As I'm using client_credentials OAuth2.0 flow, I had to enable the "Use Refresh Tokens For Client Credentials Grant" in Keycloak (clients/settings/OpenID Connect Compatibility Modes) and then toggle the option mentioned earlier.
Although OAuth2.0 states that refresh_tokens should not be used in this flow, I could not find another solution to this.
See attached image for more details.

Watson speech to text authentication

I am trying to get Transcribe from Microphone working on my server as a starting point.
The code is straightforward but I am having trouble with the token.
on IBMcloud I created a IAM-Service id with Access Policies
Viewer, Reader 14 Speech to Text service in all resource groups
created an apikey from that
created the token file
curl -k -X POST --output token \
--header "Content-Type: application/x-www-form-urlencoded" \
--header "Accept: application/json" \
--data-urlencode "grant_type=urn:ibm:params:oauth:grant-type:apikey" \
--data-urlencode "apikey={apikey}" \
"https://iam.cloud.ibm.com/identity/token"
the response has 'access_token' but the javascript SDK 0.38.1 looks for 'accessToken'
when I start microphone I get a socket error 'wss://stream.watsonplatform.net/speech-to-text/api/v1/recognize"
I checked token expiration.
I confirmed it is an auth problem:
curl -X GET "https://stream.watsonplatform.net/speech-to-text/api/v1/models?access_token="{accessToken}"
responds "unauthorized"
I have researched and am unsure what to do next. My best guess is I am generating the token improperly.
I would leave the token generation to the code. All the SDKs have an IAMAuthenticator component. The full documentation for Node.js is here. It has a very simple example where you pass in the API key:
import { IamAuthenticator } from 'ibm-cloud-sdk-core';
const authenticator = new IamAuthenticator({
apikey: '{apikey}',
});
Thereafter, you instantiate the service, e.g., STT.

Waste Management API - Authorization: Bearer problem

Im reading this documentation:
https://api.wm.com/howtotest/#make-an-api-call
And I want to make this test request in the postman:
curl -i https://apitest.wm.com/v1/helloworld \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhbnlvbmVAYW55LmNvbSIsInN1YiI6Ildhc3RlIG1hbmFnZW1lbnQgIHRlYW0iLCJqdGkiOiIwQkQyRTVDQkM2RDE2Mzc0RkNFQSIsInNjb3BlIjpbInNlbGYiLCJoZWxsb3dvcmxkIl0sImlhdCI6MTQ5MDg5ODk1NSwiZXhwIjoxNTIyNDM0OTU1fQ.O2k-senypXFZQwW4Ln3mBg60qzOSo-diPQWVfir3m6Q" \
-H "ClientId: 0BD2E5CBC6D16374FCEA" \
-H "Request-Tracking-Id: 12132"
But i recived response like this:
{
"message": "'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhbnlvbmVAYW55LmNvbSIsInN1YiI6Ildhc3RlIG1hbmFnZW1lbnQgIHRlYW0iLCJqdGkiOiIwQkQyRTVDQkM2RDE2Mzc0RkNFQSIsInNjb3BlIjpbInNlbGYiLCJoZWxsb3dvcmxkIl0sImlhdCI6MTQ5MDg5ODk1NSwiZXhwIjoxNTIyNDM0OTU1fQ.O2k-senypXFZQwW4Ln3mBg60qzOSo-diPQWVfir3m6Q' not a valid key=value pair (missing equal-sign) in Authorization header: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJhbnlvbmVAYW55LmNvbSIsInN1YiI6Ildhc3RlIG1hbmFnZW1lbnQgIHRlYW0iLCJqdGkiOiIwQkQyRTVDQkM2RDE2Mzc0RkNFQSIsInNjb3BlIjpbInNlbGYiLCJoZWxsb3dvcmxkIl0sImlhdCI6MTQ5MDg5ODk1NSwiZXhwIjoxNTIyNDM0OTU1fQ.O2k-senypXFZQwW4Ln3mBg60qzOSo-diPQWVfir3m6Q'."
}
This is the example of their official documentation, how it can be it doesnt works, or I didnt do something good?
I think that the error message is potentially misleading. If you are using the test example you need to request a clientid and token from WM per the documentation.
From the documentation:
With a valid access token and clientId, you are ready to make requests to Waste Management API.

'Client secret validation failed for client' error on identity server 4

I'm trying to get an access token from my Identity Server 4 with client_credentials , i think i almost finished the Google pages with my search and i found nothing. So please:
This is my GetClients method
My PostMan Request, in the header i have only the Content_Type which is application/x-www-form-urlencoded
And the error :
You started with the correct row of code: "secret".Sha256() but later forgot it.Try the following:
var secret = new Secret {Value = "test".Sha512()};
ClientSecrets = new[] {secret};
The secret must be hashed.
Briefly tested with
curl https://localhost:5001/connect/token -d "grant_type=client_credentials&client_id=azure-client-id&client_secret=test"
and
curl https://localhost:5001/connect/token -H "Authorization: Basic YXp1cmUtY2xpZW50LWlkOnRlc3Q=" -d "grant_type=client_credentials"

What is -u from cURL when adding an authorization header to an http request

I am trying to test one of Mix Panel's API endpoints. I'm using Postman to do this, and in Mix Panel's documentation they use cURL to show you how to make the request. When entering the URL, and the POST data for the request, it works in the sense that it hits the right place, and tells me that I need to be authenticated by adding an authorization header. What I'm confused on is, what should the key be for the header ? in their cURL example its -u API_SECRET, so would the authorization header key be 'username' ?
From documentation
# this uses a test project API secret, replace ce08d087255d5ceec741819a57174ce5
# with your own API secret
curl https://mixpanel.com/api/2.0/jql \
-u ce08d087255d5ceec741819a57174ce5: \
--data-urlencode params='{"from_date":"2016-01-01", "to_date": "2016-01-07"}' \
--data-urlencode script='function main(){ return Events(params).groupBy(["name"], mixpanel.reducer.count()) }'
If I wanted to create an AJAX query for example
$.ajax({
method: 'POST',
url: 'https://mixpanel.com/api/2.0/jql',
data: {
'params': '{"from_date":"2016-01-01", "to_date": "2016-01-07"}',
'script': '\'function main(){ return Events(params).groupBy(["name"], mixpanel.reducer.count()) }\''
},
headers: {
<WHAT GOES HERE>: API_SECRET
}
}).then(function success(response){
console.log('SUCCESS');
console.log(response)
}, function error(response){
console.log('There was an error running JQL');
console.log(response.error)
});
In this case, your API_SECRET is the username and there is no password. So using curl -u <API_SECRET>: without any "username" key is correct.
From the mixpanel documentation on an example call https://mixpanel.com/help/reference/data-export-api
Authorization steps The Data Export API accepts Basic access
authentication over HTTPS as an authorization method. To make an
authorized request, put your project's API Secret in the "username"
field of the Basic access authentication header. Make sure you use
HTTPS and not HTTP - our API rejects requests made over HTTP, since
this sends your API Secret over the internet in plain text.
Examples Here's an example of a properly-authenticated request made
with cURL:
curl https://mixpanel.com/api/2.0/segmentation/ \
-u YOUR_API_SECRET: \
-d from_date="2016-02-11" -d to_date="2016-02-11" -d event="Viewed Page"