I searched the database but failed to find any hints. I try to use my aws amplify graphql API with an external service, however, as my platform is secured by Cognito (email & PW) I need some elegant way to authenticate an existing Cognito user in a Userpool for the graphql API (Cognitor user = owner in DynamoDB) to make simple queries to retrieve infos to show in a webpage. Is there a simple way to do this without doing much? What options exist?
At the moment I get a Unathorised Message when I connect via URL in a browser.
{ "errors" : [ { "errorType" : "UnauthorizedException", "message" : "Valid authorization header not provided." } ] }
I am really missing the trick here.
OK... after comparing the request headers I found the trick...
Remove the "Bearer" prefix in Authorization.
Related
Here are the seemingly clear instructions from Amazon.
Simply send the following: sellingPartnerId, developerId, and mwsAuthToken
I do this with httparty like so:
query = {
sellingPartnerId: "A3Kxxxxx",
developerId: "753xxxx",
mwsAuthToken: "amzn.mws.8abxxxxx-xxxx-xxxx-xxxx-xxxxxx",
}
and then
send = HTTParty.get("https://sellingpartnerapi-na.amazon.com/authorization/v1/authorizationCode",
query: query
)
This returns the following error:
{"errors"=>
[{"message"=>"Access to requested resource is denied.",
"code"=>"MissingAuthenticationToken"}]}
I've adjusted the call everyway I've seen. I've read the following articles:
This
This
Paged through the 695 issues on github for this API and still no luck.. I've adjusted my query to this with no luck either:
query = {
grant_type: "client_credentials",
sellingPartnerId: "A3K98Oxxxxxx",
developerId: "753xxxxxxxx",
mwsAuthToken: "amzn.mws.8abxxxxxxx-xxxx-xxxx-xxxx-xxxxxxx",
client_id: "amzn1.application-oa2-client.xxxxxxxxxxxxxxxxxxxxxxxx",
client_secret: "a473e76XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
scope: "sellingpartnerapi::migration"
}
Nothing I've tried has worked.. Any suggestions? Has anyone actually migrated their MWS to SP-API credential successfully?
Unfortunately the specific Amazon docs that you link to don't tell the whole story. There are a few other requirements you'll need in order to get the authorizationCode response that you're looking for:
Amazon OAuth Token
You'll need an access token from Amazon's OAuth API (an entirely different API). You can use the grantless workflow for this, since in your case the user hasn't actually authorized the SP-API yet:
POST https://api.amazon.com/auth/o2/token
body: {
grant_type: 'client_credentials',
scope: 'sellingpartnerapi::migration',
client_id: 'amzn1.application-oa2-client.xxxxxxxxxxxxxxxxxxxxxxxx',
client_secret: 'a473e76XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
}
This will return an access_token that you'll need for your actual migration request to https://sellingpartnerapi-na.amazon.com/authorization/v1/authorizationCode. The response will look something like:
{
"access_token": "Atc|xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"scope": "sellingpartnerapi::migration",
"token_type": "bearer",
"expires_in": 3600
}
Important: Take the access_token value from that response and add it as an x-amz-access-token header to your /authorization/v1/authorizationCode request.
Sign Your Request
This is the actual reason behind the error you're receiving. An unsigned request will not include the "authorization token" that you're being prompted for.
You'll need to sign your request using Amazon's SigV4 signing mechanism. It looks like you're using Ruby (HTTParty), so you can use the aws-sdk's Aws::Sigv4::Signer for this. You'll need to have setup IAM credentials as documented in the generic developer guide, and those credentials being provided to your Aws::Sigv4::Signer somehow (hardcoding, env vars, Aws::SharedCredentials, etc.)
Request signing will result in a few proprietary headers being added to your request. Once this is done, you should have all that you need to make the request successfully.
I am working on aws cognito with bearer token.
Below is the code to setup Bearer configuration.
I have written code to login via user name & password
I am able to logged in successfully and get the token
But when i am trying to access my authorise API it is throwing below error.
Could you please help me to resolve this?
Looks like your system is failing when trying to download OpenID Connect metadata. This will be a URL such as the following - so you should make sure your API is configured with a URL you can reach in the browser:
https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_qqJgVeuTn/.well-known/openid-configuration
The following type of code should work when validating Cognito tokens in .NET, if you add the Microsoft.AspNetCore.Authentication.JwtBearer library. Note that Cognito does not issue an Audience claim so you need to avoid validating it:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_qqJgVeuTn";
options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = false,
};
});
Note that this includes a website library in APIs and expected OpenID Connect endpoints to exist. Personally I prefer not to write OAuth secured APIs like this.
JWKS ENDPOINT
The standard way to do API token validation is for the API to only know about the JWKS endpoint, as in these examples:
Node.js API JWT Validation
Java API JWT Validation
I found this approach a little harder in .NET, where libraries didn't quite do what I wanted. Here is some code that shows how to use extensibility points, in case useful:
.NET API JWT Validation
I'm providing an external-facing REST GET API service in a kubernetes pod on AWS EKS. I had configured an ALB Ingress for this service which enforces Cognito user pool authentication. Cognito is configured with Authorization code grant with the openid OAuth scope enabled.
If I invoke my REST API from the browser, I get redirected to the Cognito login page. After a sucessful authentication on the form here, I can access my REST GET API just fine. This works, but this is not what I'd like to achieve.
Instead of this, I would need to use a Bearer token, after getting successfully authenticated. So first I invoke https://cognito-idp.ap-southeast-1.amazonaws.com using Postman with the request:
"AuthParameters" : {
"USERNAME" : "<email>",
"PASSWORD" : "<mypass>",
"SECRET_HASH" : "<correctly calculated hash>"
},
"AuthFlow" : "USER_PASSWORD_AUTH",
"ClientId" : "<cognito user pool id>"
}
and I get a successful response like:
"AuthenticationResult": {
"AccessToken": "...",
"ExpiresIn": 3600,
"IdToken": "...",
"RefreshToken": "...",
"TokenType": "Bearer"
},
"ChallengeParameters": {}
}
In the last step I'm trying to invoke my REST API service passing the Authorization HTTP header with the value Bearer <AccessToken> but I still get a HTML response with the login page.
How can I configure Cognito to accept my Bearer token for this call as an authenticated identity?
Quoting AWS support on this topic: "the Bearer token can not be used instead of the session cookie because in a flow involving bearer token would lead to generating the session cookie".
So unfortunately this usecase is not possible to implemented as of today.
STANDARD BEHAVIOUR
I would aim for a standard solution, which works like this:
API returns data when it receives a valid access token, or a 401 if the token is missing, invalid or expired - the API never redirects the caller
UIs do their own redirects to the Authorization Server when there is no token yet or when a 401 is received from the API
If it helps, my OAuth Message Workflow blog post demonstrates the 3 legged behaviour between UI, API and Authorization Server.
API GATEWAY PATTERN
It is perfectly fine to use an API Gateway Design Pattern, where token validation is done via middleware before hitting your API.
However that middleware must return a 401 when tokens are rejected rather than redirecting the API client.
IMPACT OF APIs REDIRECTING THE CLIENT
This may just about work for web UIs, though user experience will be limited since the UI will have no opportunity to save the user's data or location before redirecting.
For mobile / desktop apps it is more problematic, since the UI must redirect using the system browser rather than a normal UI view - see the screenshots on my Quick Start Page.
CHOICES
Any of these solutions would be fine:
Possibly the middleware you are using can be configured differently to behave like a proper API Gateway?
Or perhaps you could look for alternative middleware that does token validation, such as an AWS Lambda custom authorizer?
Or do the OAuth work in the API's code, as in this Sample API of mine
MY PREFERENCE
Sometimes I prefer to write code to do the OAuth work, since it can provide better extensibility when dealing with custom claims. My API Authorization blog post has some further info on this.
Amazon Web Services introduced a beta release of HTTP API as a new product on API Gateway early last month. Its authentication is managed using JSON Web Tokens and configured with a form asking for
"Name of the Authorizer"
"Identity Source... a selection expression that defines the source of the token"
"Issuer URL"
I'm not very familiar with authentication protocols at all or what these form fields are asking, and currently the documentation from AWS on how to configure this to work with Cognito is sparse. I'm not totally comfortable configuring this without guidance due to my lack of experience. Another Stack Overflow user seemed to have a similar issue but didn't get an answer.
AWS is using JWT Bearer Grant for this purpose.
Draft Specification here.
It allows HTTP API Gateway to accept JWT Tokens in the incoming Authorization HTTP header containing a self-contained JWT access token issued by third-party authorization servers (like Cognito, Azure AD, etc).
API Gateway validates the incoming JWT Token by matching the 'iss' value with the issuer URL to see if it can trust this token.
Try with these values.
Name of the authorizer: Registered client name in your Cognito User Pool .
Identity Source: Leave it as default, $request.header.Authorization .
Issuer URL: Check the metadata URL of your Cognito User Pool (construct the URL in this format :: https://cognito-idp.[region].amazonaws.com/[userPoolId]/.well-known/openid-configuration :: look for a claim named "issuer". Copy its Value and paste it here.
Audience: Client ID of your Registered client in Cognito
Good Luck!
cheers,
ram
Used #ram answer to get through, and was able to implement this
1.Name of the authorizer:
AWS Cognito > User pools > App Integration > App client settings > App client :
Example : xxxxxx_app_clientWeb
2.Identity Source : $request.header.Authorization
3.Issuer URL
construct the URL to get Cognito user pool metadata ( https://cognito-idp..amazonaws.com//.well-known/openid-configuration)
Example :
https://cognito-idp.us-east-1.amazonaws.com/us-east-1_FcgSrx2141/.well-known/openid-configuration
open the URL and you will see a json
take the "issuer" value
Example :
"issuer":"https://cognito-idp.us-east-1.amazonaws.com/us-east-1_FcgSrx2141"
Take: https://cognito-idp.us-east-1.amazonaws.com/us-east-1_FcgSrx2141
4. Audience: AWS Cognito > User pools > App Integration > App client settings > App clientID
Example :
ID 9sptej55gii5dfp08ulplc343
Take: 9sptej55gii5dfp08ulplc343
This video explains the whole process and configuration like no other.
https://www.coursera.org/lecture/building-modern-java-applications-on-aws/use-amazon-cognito-to-sign-in-and-call-api-gateway-s226R
I am thankful that the video is public.
Note: (As far as I know) The course is from AWS but offered to the public through different MOOC websites (not just this one).
Once you have read & played enough, you will start seeing the gems within the details.
Token for example, is mentioned in many docs, but it can be Access / Id / Refresh Token. If you don't realize about this you can be wasting your time.
For example the "Implicit grant" doesn't provide a Refresh-Token, so you cannot renew your Access-Token and trying to do it is useless.
What I want to achieve:
My case described in this article but I do not want to use SDK on my webpage - I want to keep my app simple and fast, but as it turned out - it is hard to find an example that does not use SDK. I stumped with http call from which I need to get limited IAM credentials for DynamoDB. In my investigation of the documentation, I found method GetCredentialsForIdentity, description:
Returns credentials for the provided identity ID. Any provided logins
will be validated against supported login providers. If the token is
for cognito-identity.amazonaws.com, it will be passed through to AWS
Security Token Service with the appropriate role for the token.
But on this page not mentioned url for that endpoint. I tried
https://cognito-idp.us-east-1.amazonaws.com
https://cognito-identity.us-east-1.amazonaws.com
https://<mydomain>.auth.us-east-1.amazoncognito.com
I getting 400 error now, maybe because of incorrect endpoint url. My current code:
fetch('https://cognito-identity.us-east-1.amazonaws.com', {
'method': 'POST',
'headers': {
'Content-Type': 'application/x-amz-json-1.1',
'X-Amz-Target': 'AWSCognitoIdentityService.GetCredentialsForIdentity',
},
'body': '{"IdentityId": "us-east-1:<GUID of the user>"}'
});
1) What is the http endpoint?
2) Am I digging in the right direction?
Correct endpoint is
https://cognito-identity.us-east-1.amazonaws.com
according to this page.
Also, I found that I need to provide not a sub from id_token but firstly GetId of the user.