I am currently switching from IBM APIC to AWS API Gateway. I found a different behaviour that it's not detailed in the AWS documentation (https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html)
After i use the refresh_token to get a new access_token i have a different behavior:
In IBM the initial access_token is invalidated.
In AWS you can call the API with the initial access_token and with the "new" access_token.
Is there an option to invalidate the initial access_token when the refresh_token is used?
Thanks.
So I think what you're seeing here is that old token is still valid, in short no you can't invalidate it UNLESS you call global sign out. I think the minimum time a token takes to expire 60 minutes.
Cognito has some flaws still really, listUsers API doesn't search by custom attributes, if you use federated identities you can't retrospectively link by email as you've found the token implementation isn't quite ideal yet.
Related
I am planning to create a single page web application and all user authentication would be handled by AWS Cognito. By reading through some of articals, they all suggest using "code" grant flow + PKCE rather than implicit flow since implicit flow is deprecated in OAuth 2.1
I created a test user pool with AWS Cognito, by using Postman I successfully get access_token, id token and refresh token with code grant flow + PKCE.
I double checked with AWS Cognito Doc, seems refresh token will always be returned if using code grant flow and I cannot find anywhere in AWS console to disable returning refresh token. So just wondering how can I protect refresh_token? Cookie or localStorage can not stop XSS.
It is usual to route requests for tokens via a backend, eg a utility API, which also represents the refresh token as an HTTP Only SameSite=strict encrypted secure cookie. See this crypto code for an example.
The backend component also enables you to use a client secret, in addition to PKCE, which improves security a little. Cognito supports this, so I'd recommend using both.
BEST PRACTICE
OAuth for browser based apps recommends this approach. Stakeholders often expect you to be using the backend for frontend described, and it will make life more comfortable if you have to explain your app's security.
XSS
With the above HTTP only cookie approach, malicious code would not be able to steal your refresh tokens, and this is therefore the recommended way to handle them.
If your SPA has XSS vulnerabilities, then malicious code might be able to steal your data though, regardless of whether tokens or cookies are used to access data. Therefore it is critical to implement XSS protection carefully, regardless of your OAuth solution.
I am using the following doc by AWS for verifying the incoming token with Cognito to verify the user in cognito pool:
https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html
Now I also need to verify one of the custom attributes (email) of that user. (Basically this is to mitigate a case where some user can change their email-id in the API payload to access someone else's details).
The Cognito SDK I found to do this was this : https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_GetUser.html
It works just fine, however this led me to a question :
Since I'm sending access token in this SDK, do I need to use the former jwt-based token verification as well? Because this SDK also handles the case where token is invalid/expired and sends error codes accordingly. Is there something I'm missing which the former case handles and the latter one doesn't ?
Short answer: no, you can ignore the contents of the JWT.
Longer answer: JWT tokens provide a fast way to verify that a user has been authenticated, without the need to check a database or external service. This can be very important in a high-volume application.
However, they have several limitations:
A JWT token is issued with an expiration timestamp. If you rely on the JWT, you do not have a way to forcibly log-out a user until that timestamp expires.
To be secure, your JWT token must be signed using an asymmetric keypair (I mention this simply because a lot of people have implemented their own identity servers incorrectly; Cognito does it right).
The claims that are in the token (and are signed by the identity server) may not be sufficient for your needs. Best practice is to minimize the size of the token, and iirc Cognito follows this practice by only including the user attributes that are directly tied to authentication.
If any of these limitations apply to a particular use-case (and it sounds like the third applies to you), then you'll have to make that external service call to the identity server. And if you do that, and the server reports authentication status, then there's no need to pay attention to the token.
Hey I am getting started with the serverless framework, apigateway, lambdas and authorizers.
Here my questions:
In order to verify a proper JWT token (which seems nowadays the best solution for serverless authentication), I would need a client id. Since the authorizer lambda is not capable(?) of reaching any other parameters of the request into the lambda except for the token itself, this is a difficult task to do. How can I achieve this?
Since with every authenticated call, an additional lambda is called, my costs are doubled?! May be I am misunderstanding something here, but it seems to me like implementing a method for verifying my token without using the authorizer is cheaper and I don't need to implement the authorizer lambda itself.
The whole point of OAuth2 is that at the point of authorization you don't care who the bearer presenting you with a token is or which client they are using (web app, Postman, etc.), only that the token is valid for whatever the token bearer is trying to do. You trust that the authentication has happened because the token issuer has done the authentication.
When you talk about the aud(audience) (from comments) in JWTs, which may or may not be included dependent on the JWT issuer, this is intended to reflect the service or services that the JWT is valid for.
This could for example have a value of 'myAPIName' or 'myAPIName/test' or even ['myAPIName/test1', 'myAPIName/test2'].
If you need to validate an individual aud claim you have two choices, you can either have different Lambdas authorizing different api routes and methods with hardcoded aud variables or you can get the name of the api being called and map it back to something that would match an aud claim.
For example the method arn for the incoming request can be found in event.methodArn. This looks like arn:aws:execute-api:{regionId}:{accountId}:{apiId}/{stage}/{httpVerb}/ potentially with [{resource}/[{child-resources}]] dependent on your implementation. With a bit of string manipulation, you could map this back to the format of an audience claim (whatever that looks like for you).
If you would rather work an api name instead of an api name you can use the apigateway.getrestapi method for whatever sdk you are using. Documentation on this method for the JavaScript sdk can be found here.
The JWT may have a sub(subject claim), again dependent on implementation of the JWT issuer, this could relate to any of your users (or at least the users the JWT issuer knows about). Validating this beyond checking the signature of the JWT is pointless. The best you could do is check if that user exists and only if you have access to the same user database that the JWT issuer does. Although this can be used to ensure that UserA only has access to UserA's data. Again, you are trusting that the fact a bearer has a token is proof that they have authenticated (proved who they are).
I hope that answers part one of your question.
In regards to part 2, the advantage of using a Lambda Authorizer over doing the authorization in the target Lambda is caching.
So envisage I have a token valid for one hour and I call your API once every second for 30 minutes (1,800 calls). You have a Lambda authorizer with a response cache time of 10 minutes. That means you check the JWT 3 times for 1800 api calls.
But if you do the validation of that token in your target Lambda, you are doing that processing 1,800 times.
There is authentication with AWS Cognito which will allow you to use authentication outside your application and still being able to let your application divide the users into subgroups based on custom roles or similar.
If for some reason you cannot use AWS Cognito (Why not?) there is both the possibility of custom authentication (the same for each function with SLS) or even authenticating with custom code inside each lambda function.
I would recommend to use Cognito, and only custom if you know you need it.
I'm sorry for what appears to be somewhat of a dumb question, but I'm playing around in AWS' Web Identity Federation playground and am not clear what type of access (authorization) token I am receiving when I first log in and the provider redirects to the callback URL. I first log in with my Amazon.com account, and Amazon supplies me with an access token. I understand that I will trade this in for an STS token so I can assume a specific role defined for me, but I wanted to inspect the token using the jwt.io site:
This all makes sense to me, but when I paste it into the jwt.io form to see its plaintext contents, I get an Invalid Signature error. If this is not a JWT, where is the information for access scopes (like profile and expiration windows (like 3600) stored then?
I'm guessing this means that the access token is not a JWT token, but rather only a simple string combination that has no real-world meaning and
basically says "Check this token is valid and then grant whoever gave you this token the permissions associated with it"? Is this always true for every web identity provider (Amazon, Google, Facebook, etc.)? Does the fact that token_type=bearer in the query string of the redirect URL mean that it cannot be a JWT?
If jwt.io says Invalid Signature, it’s not necessarily because the token is not valid, it is probably because you have not provided jwt.io with a valid signing key. In the signature box on the right, paste the signing key.
A simple API endpoint, with a Cognito User Pool Authorizer, when using the Authorizer Test button ( or using postman/Insomnia ) with a valid token fails ( Screenshot bellow ):
I know the token is valid as I can make a successful call to the Cognito user pool user-info end-point using the same token and get the desired response back.
Also removing the authorizer ( setting it to None ), will return the desired response as well.
I've tried both with and without Bearer ..., I've also tried to change the Authorization Token source to method.request.header.Authorization as I've found in some older Question but immediately after saving, it changes it back to Authorization, while method.response.header.Authorization doesn't seem to work either.
The API was deployed through a CloudFormation stack.
I'm writing a complete guide to this issue as the documentation is lacking and it's not easy to find the right information for such a simple task. but it may take a few days, so till then I'll post a short answer here and once ( hopefully ) I finish the guide I'll update this answer:
My problem was that I was using the access_token, but I had to use the identity_token! The other problem is that none of the OAuth2 tools available ( like Auth modules of Postman and Insomnia ) return or use the identity_token, they don't even show the token! and although it is in the OAuth2 specs, nobody's using it ( except Cognito! ),...
So I made some changes to the Insomnia source-code of the OAuth2 module and used the correct token, then it started working!
I'm going to make a PR to Insomnia and if it went through the feature may become available in next releases, otherwise, I'll make my packaged binary available so peep can use it since otherwise, it's a nightmare to get tokens from AWS!
If your API methods - do not have OAuth scopes: must use ID tokens.
If your API methods - have OAuth scopes: must use access tokens.
The test method always uses an id-token. So to use this in Postman - add an OAuth scope to your API methods.
From:
https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-enable-cognito-user-pool.html
With the COGNITO_USER_POOLS authorizer, if the OAuth Scopes option isn't specified, API Gateway treats the supplied token as an identity token and verifies the claimed identity against the one from the user pool. Otherwise, API Gateway treats the supplied token as an access token and verifies the access scopes that are claimed in the token against the authorization scopes declared on the method.
Strange but I thought I have a similar issue, but I was aware of that in the response there are two tokens and I was trying both and none of them was working.
After reading so many things around the internet, nothing was working.
I went to the ApiGateway user interface, clicked on the authorizer, then edit and I hit save. AND VOILA, it works. Strange, maybe it will help someone else too.
I had the same issue and I tried both id_token and access_token as well but didn't work.
Also tried to redeploy my stack, but didn't work.
I'm using AWS CDK to deploy my stack.
My solution was to go to the user interface, click on the authorizer -> edit -> save without changes. Then I ran the "test" and it worked.
Don't forget to deploy it. I forgot it and spent hours debugging why it is working in the test method, but not working using Postman.