"Access token does not contain openid scope" in AWS Cognito - amazon-web-services

I am running a working AWS Cognito service on a frontend application which can successfully do the basic stuff - login, logout, signup, etc..
Right now I am trying to get user attributes through the backend API, such that:
1) The user login in the application and gets a JWT.
2) The JWT is being sent to the backend server.
3) The server has to extract the email of the user by using the access token
The closest thing that I found to what I need is this Cognito service.
So I am making a GET request to "https://mydomain.auth.eu-central-1.amazoncognito.com/oauth2/userInfo"
With Authorization Header as they are asking for, but I keep getting this response:
{
"error": "invalid_token",
"error_description": "Access token does not contain openid scope"
}
I have tried searching for this error but couldn't find any explanation about the error.
Thanks by advance

Erez, are you using a custom UI?
Because the custom UI uses flows that are completely separated from the OAuth2 ones (USER_SRP_AUTH, USER_PASSWORD_AUTH). Tokens that are released with these flows are not OpenID Connect compliant (basically they don't contain the openid scope) so you cannot use them to gather user infos (since the userinfo endpoint is OpenID Connect compliant and needs to be invoked with jwts compliant with OIDC standard).
We're also struggling on that, i'm sorry.

I had this exact problem and it was my fault. I was sending the id_token instead of access_token property of the token.
I program in PHP, so I was sending as header "Authorization: Bearer ".$token->id_token instead of "Authorization: Bearer ".$token->access_token. Now it works.
Hope it helps you or someone.

I am still experiencing the same issue. My problem relies on programmatic use of signIn service (not Hosted UI via federated login) in Amplify framework. After a long googling, I have discovered that this is because "openid" is not including in the scope of token. Only "aws.cognito.signin.user.admin" is included.
You can find a reference here, thread is still open https://github.com/aws-amplify/amplify-js/issues/3732
This solution seems to be fine for me How to verify JWT from AWS Cognito in the API backend?

If I understand correctly, you are successfully getting the #id_token sent to your front end from Cognito (steps 1-3). You can enable scopes on the #id_token by selecting the following options in your Cognito Pool App Client Settings:

I had a similar issue and I spent a couple of hours to find a solution. The access token you received it from cognito in your frontend application you need to send it to your backend then decode it and verify it. here is a good documentation from aws: https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html

Related

Aws Cognito Bearer Token : Unable to obtain configuration from: 'System.String'

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

How OAuth authorization works via API

It is clear how to get a token from Google(or any other OAuth provider). But I do not understand where shoud I do it - server part or client part.
For example: I have a backend on Flask with unified API for Android, iOS and web(js/react) apps.
Where do I need to get a token? On the client (Android for example) part and send it to server or in my Flask app after request from client? Where should I get data from provider? How at all works interaction between client and server while using OAuth?
Would be pleased for some explanations or links on some guides
Your UIs will manage redirecting the user to authenticate - after which the UI is given an access token to call the API with.
The OAuth provider is the entry point for authentication and issues an access token afterwards.
The API uses the access token to identify the user and authorize access to resources.
A good way to understand OAuth is the HTTP messages - my blog post highlights these, and they are largely the same for SPAs and mobile.
There are also some code samples on my blog which you can run, in case useful.

Using AWS Cognito Hosted UI (Code Grant) what do I do with the token once it is verified? Subsequent API Calls do not have the token

MY goal is to setup the Cognito Hosted UI to validate users after login. I have followed the steps laid out in the OAuth2 blog here: https://developer.okta.com/blog/2018/04/10/oauth-authorization-code-grant-type
My steps are as follows.
User logs into the AWS provided login screen.
It redirects to my website and I pull down the authorization code in Angular.
I send the code as part of my headers to the backend Nodejs
I use the code to get a token and then validate the token
This stream works but then what? I want to validate the AWS token for each API call but I have no idea how to access the token.
I am guessing that I am either missing the point of this procedure or that the token is somewhere I am unaware of.
Any help would be greatly appreciated.
Usually you have your own UI that redirects to Cognito to authenticate, after which the UI sends the access token to the API on every request.
All of the code samples on my Quick Start Page work like this and use Cognito.
If it helps, this is what the OAuth Technical Messages look like. Feel free to ask any follow up questions

AWS Cognito authentication with Bearer token

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.

Generate an OAuth2 token in a view

Let's say I have an AngularJS application that consumes the REST API of a Django application.
The Django application has got a built-in OAuth2 provider that can be called to retrieve an access token and use the protected endpoints of the API. This provider is using django-oauth-toolkit.
Let's assume there is a registered client with "password" grant type, so that the end users only need to provide their credentials in the front-end in order to get an access token from the back-end.
At some point we want to add some support for social networks login and we decide to use python-social-auth (PSA) to that end. Here is the workflow I want to achieve:
The user logs in on Facebook from the front-end (via the Facebook SDK) and we get an access token back from the OAuth2 provider of Facebook.
We send the Facebook token to an endpoint of our REST API. This endpoint uses the Facebook token and django-social-auth to authenticate the user in our Django application (basically matching a Facebook account to a standard account within the app).
If the authentication succeeds, the API endpoint requests an access token from the OAuth2 provider for this newly authenticated user.
The Django access token is sent back to the front-end and can be used to access the REST API in exactly the same way that a regular user (i.e. logged in with his credentials) would do.
Now my problem is: how do I achieve step 3? I first thought I would register a separate OAuth2 client with Client Credentials Grant but then the generated token is not user-specific so it does not make sense. Another option is to use the TokenAuthentication from DRF but that would add too much complexity to my project. I already have an OAuth server and I don't want to set up a second token provider to circumvent my problem, unless this is the only solution.
I think my understanding of PSA and django-oauth-toolkit is not deep enough to find the best way of reaching my goal, but there must be a way. Help!
I managed to get something working using urllib2. I can't speak towards whether or not this is good practice, but I can successfully generate an OAuth2 token within a view.
Normally when I'd generate an access token with cURL, it'd look like this:
curl -X POST -d "grant_type=password&username=<user_name>&password=<password>" -u"<client_id>:<client_secret>" http://localhost:8000/o/token/
So we're tasked with making urllib2 accomplish this. After playing around for some bit, it is fairly straightforward.
import urllib, urlib2, base64, json
# Housekeeping
token_url = 'http://localhost:8000/auth/token/'
data = urllib.urlencode({'grant_type':'password', 'username':<username>, 'password':<password>})
authentication = base64.b64encode('%s:%s' % (<client_id>, <client_secret>))
# Down to Business
request = urllib2.Request(token_url, data)
request.add_header("Authorization", "Basic %s" % authentication)
access_credentials = urllib2.urlopen(request)
json_credentials = json.load(access_credentials)
I reiterate, I do not know if this is in bad practice and I have not looked into whether or not this causes any issues with Django. AFAIK this will do this trick (as it did for me).