Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cognito.user.signOut() does not invalidate tokens #3435

Closed
jiachen247 opened this issue Jun 12, 2019 · 118 comments
Closed

cognito.user.signOut() does not invalidate tokens #3435

jiachen247 opened this issue Jun 12, 2019 · 118 comments
Assignees
Labels
Cognito Related to cognito issues feature-request Request a new feature pending-maintainer-response Issue is pending a response from the Amplify team. Service Team Issues asked to the Service Team

Comments

@jiachen247
Copy link

jiachen247 commented Jun 12, 2019

Describe the bug
On calling state.cognito.user.signOut(), session tokens are just removed localstorage.
The actual access tokens and refresh tokens are still valid for the lifecycle of the token.

Expected behavior
This is a security issue. Best practice dictates session tokens should be invalidated server side on a logout request not just deleted on the client. (OWASP session management)

Source Code found here
https://github.com/aws-amplify/amplify-js/blob/68a5ad2fe2b1d9f03cce80d1bf449e454b621760/packages/aws-amplify-react/src/Auth/SignOut.jsx

@haverchuck haverchuck added the Cognito Related to cognito issues label Jun 12, 2019
@mlabieniec
Copy link
Contributor

mlabieniec commented Jun 13, 2019

@jiachen247 Cognito issues short lived bearer access tokens (valid up to 1 hour). The access tokens are short lived (up to 1 hour) and Cognito has GlobalSignOut Api to invalidate all tokens issued in past. If you are using the cognito-identity-js sdk directly, then the globalSignOut method will invalidate all sessions (see use case #15 here: https://github.com/aws-amplify/amplify-js/tree/master/packages/amazon-cognito-identity-js). This i believe is what you are looking for to invalidate the server side. The user logout is specifically there as to not invalidate other sessions and just sign out the current local user.

cognitoUser.globalSignOut(callback);

@jiachen247
Copy link
Author

jiachen247 commented Jun 14, 2019

@mlabieniec Thanks for your response! yes and no actually. Its closer but its still different. Global sign out invalidates all open sessions. For example, lets say I log into the webapp and a mobile app both use Cognito for authentication at the same time. On global signout, both will get logged out. However, signout should just invalidate that particular session on the client side as well as the server side and not touch the other open sessions. That makes the most sense on reading the docs for the function is intended for.

@stale
Copy link

stale bot commented Jul 14, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@cbernardes
Copy link

I could not agree more with @jiachen247. I have the same situation of mobile and app usage and the session should be invalidated for each one separately. What actually concerns me more is the fact that I can still use the token after using the "signOut" method of @aws-amplify/auth.

@jani937
Copy link

jani937 commented Jul 19, 2019

Yes, i am facing the same issue. I am using Auth.SignOut({global: ture}) and it is kicking user to login page of that particuler window. If i open the app in another tab in the same window / access the app from other browser or machine they still find a valid user object from id token. I can still get hold of the application.

I am looking for a solution where if i logout from other tab and come back to another tab and do any operation, it should kick me back to login page.

Any workaround solution for this?

@ProdigySim
Copy link
Contributor

Is there some API endpoint we can hit to invalidate a single Refresh token? Auth.signOut({ global: true }) revokes all refresh tokens, and Auth.signOut() revokes no refresh tokens...

There must be some middleground available....

@stale
Copy link

stale bot commented Aug 24, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@cbernardes
Copy link

We are all waiting for it to be fixed.

@jordanranz jordanranz added the to-be-reproduced Used in order for Amplify to reproduce said issue label Sep 16, 2019
@cheeseandcereal
Copy link

Any news on this? It seems ridiculous that you can't invalidate a single set of tokens on the back end.

@sammartinez sammartinez added the React React related issues label Oct 17, 2019
@asnar
Copy link

asnar commented Oct 26, 2019

any updates from?

@sammartinez sammartinez added Service Team Issues asked to the Service Team bug Something isn't working and removed to-be-reproduced Used in order for Amplify to reproduce said issue labels Nov 18, 2019
@hckr
Copy link

hckr commented Nov 20, 2019

Is there someone to actually answer these issues/questions? Too frequently I see only tags added and more people writing they have the same problem. No actual support whatsoever and that's sad.

I also need the ability to:

  • sign out a single user session (only this one with which the sign out is requested) followed by the token invalidation
  • get information (an event?) that the session was invalidated (for example with a global sign out), so I could send the user back to the login screen, as the token is not usable nevertheless, but all Amplify functions don't seem to care, just throw errors.

@wlarch
Copy link

wlarch commented Nov 27, 2019

We encountered the same problem with the AWS Cognito PHP SDK. It seems the documentation is clear for the AdminUserGlobalSignOut function :

Signs out users from all devices, as an administrator. It also invalidates all refresh tokens issued to a user. The user's current access and Id tokens remain valid until their expiry. Access and Id tokens expire one hour after they are issued.

https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminUserGlobalSignOut.html

A much-needed feature would be to invalidate all tokens on AdminUserGlobalSignOut. Our use case : a user password as been changed by an admin or user, thus the user should be logged out automatically. By the documentation, the user would only be disconnected one hour after...

@jo-ma
Copy link

jo-ma commented Dec 9, 2019

A much-needed feature would be to invalidate all tokens on AdminUserGlobalSignOut. Our use case : a user password as been changed by an admin or user, thus the user should be logged out automatically. By the documentation, the user would only be disconnected one hour after...

By their nature, there is no mechanism to invalidate an access or id token.

The likely solution for your scenario is to track any revoke token events in your app. Then, as part of your token validation process, you can check to see if you've revoked that specific token and if so not authorize the request. You could maintain a database table of revoked tokens, cache the values for performance and have a hygiene process to remove expired, revoked tokens.

Your use-case / scenario is different from the original issue.

@ProdigySim
Copy link
Contributor

By their nature, there is no mechanism to invalidate an access or id token.

That's my understanding of JWTs as well. However, I've seen evidence that Cognito tracks individual access tokens.

If you do a global signout, but save your JWT tokens, and then try to hit another Cognito endpoint (like "global signout" again), you'll get a 400 with the message Access Token has been revoked. This suggests that Cognito is in fact tracking revocation of individual access tokens in some way.

@maros96
Copy link

maros96 commented Dec 30, 2019

If you do a global signout, but save your JWT tokens, and then try to hit another Cognito endpoint (like "global signout" again), you'll get a 400 with the message Access Token has been revoked. This suggests that Cognito is in fact tracking revocation of individual access tokens in some way.

I thought that when you do Global Signout, secret key(or private key and with that also public key) is changed and every token that was created before calling Signout is invalid. That would mean that validation of each issued token fails. But only thing that is invalid is refresh token. Access token and ID token remain valid until their expiration. More here:
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html

Beside that, there is no way to invalid token. You can only wait for token to expire and become invalid. There is a way to track by yourself which tokens should be revoked(after user logout put that token in database) and to check every time if token that is being used currently is in that database(that means it's invalid) or not(it is valid). But by doing this, you are loosing tokens stateless feature. More about this you can find here(maybe best approach gave Ashtonian):
https://stackoverflow.com/questions/21978658/invalidating-json-web-tokens

This is the best way to deal with everything in Cognito:
https://dzone.com/articles/aws-cognito-user-pool-access-token-invalidation-1

@soyiatgit
Copy link

soyiatgit commented Jan 20, 2020

Facing the same issue with cognito-identity for JS. user.signout() just removes the token from local storage. Anyone who had preserved the id token can still connect to the API gateway with it. Any updates or resolution around this ?

@maros96
Copy link

maros96 commented Jan 20, 2020

Facing the same issue with cognito-identity for JS. user.signout() just removes the token from local storage. Anyone who had preserved the id token can still connect to the API gateway with it. Any updates or resolution around this ?

Only way(for now) that is verified from AWS technical support team is explained here:
https://dzone.com/articles/aws-cognito-user-pool-access-token-invalidation-1

So, idea is to user CognitoIdentityServiceProvider. More about all its methods here: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CognitoIdentityServiceProvider.html

CognitoIdentityServiceProvider.globalSingOutUser(accessToken) revokes access token
CognitoIdentityServiceProvider.getUser(accessToken) either return error if token is revoked or users data if token is valid(not expired or revoked)

So, in your case, each time when user sings out(logs out) from application, you should call this globalSingOutUser(accessToken) method. And in case you want to check if user is singed out or not(if token is revoked or not) you should be checking also access token using this getUser(accessToken) method. That should be done before calling API Gateway. And if you are worried about someone calling API Gateway with just ID token, you can add this check inside for example Lambda(if API Gateway is calling Lambda afterwards). I don't know whole scenario that you are facing with, but you can find some workaround with this.

@soyiatgit
Copy link

soyiatgit commented Jan 20, 2020

Thanks @maros96 for the detailed explanation.

However, when I tried using accessToken as the Authorisation header to connect with API gateway, my API call failed with 401 -Unauthorised. The same call works fine while sending idToken in the Authorisation header. The AWS doc here says we can use either of idToken or accessToken. Is that correct. Can you suggest any cause for this behaviour.

@maros96
Copy link

maros96 commented Jan 20, 2020

Thanks @maros96 for the detailed explanation.

However, when I tried using accessToken as the Authorisation header to connect with API gateway, my API call failed with 401 -Unauthorised. The same call works fine while sending idToken in the Authorisation header. The AWS doc here says we can use either of idToken or accessToken. Is that correct. Can you suggest any cause for this behaviour.

Yes, thing is that Authorizer can depend on ID Token OR Access Token, but implementation of Authorizer is not the same for those 2 cases. You've created your authorizer to use ID Token. If you want to change it to use Access Token, you have to change firs implementation of authorizer and then put accessToken in Authorization header. That is why you get Unauthorized when you pass accessToken - Authorizer expects ID Token instead. On that link you've sent, on the end, you have links for doing everything in steps(obtain permissions, first create Cognito User Pool, ...) and you should read Integrate a REST API with an Amazon Cognito User Pool.

So, you can try to implement Authorizer to use Access Token(I've never done that and I am not 100% sure that in case that Access Token is revoked, authorizer will return Unauthorized or will let you use your API, but you should try that). You can try that and after testing that, put a comment here for everyone else, including me, to see how does that work :)

In case you don't manage to do that, you can still use ID Token for authorizer and call CognitoIdentitiServiceProvider.getUser(accessToken) after calling API Gateway and before doing everything else. Put that check between API Gateway and rest of your application. For example, as I suggested in previous comment, if you call Lambda from your API Gateway, you can first do this check in Lambda and in case access token is revoked, return error status with message "Access Token is revoked". Otherwise you just continue with Lambda execution(which means that access token is valid).

One more thing, in case access token is invalid in any sense(expired, wrong token use, revoked, signature is not the same as decode(hash(header.payload), publicKey) - which means that someone has change something inside that token), this getUser(accessToken) method will return error. If token is valid, it will return some user data. So, you are not just checking if token is revoked, you are also checking whole validity of Access Token.

@miyasudokoro
Copy link

We still need a way to invalidate an individual refresh token. I don't want to use Global Sign Out because a user might want to be signed in on multiple devices but just want to sign out of one specific device. It would be a poor user experience.

The current system relies solely on each device just "forgetting" the refresh token if a user logs out on that device. This is not a very secure or reliable way to discard refresh tokens because the tokens themselves remain usable. If you do the forgetting wrong, then the user stays logged in without realizing it. API calls using the "logged out" session still return 200, and so forth.

@ramburg
Copy link

ramburg commented Sep 13, 2021

Hi @jpignata and @sammartinez, it sounds like you are from AWS. As per my post from before, I upgraded all packages to the latest version, but the individual invalidation of a refresh-token doesn't work; there is no client-side call visible, as compare to "global: true", where you can clearly see the call of invalidation in Network commands.

Can you please confirm:

  1. whether this feature has been released and in which version?
  2. How the invalidation happens if not via a client-side call, visible in the Network tab, where else?
  3. If not yet released, when you expect this?

@sethhitch
Copy link

@ramburg make sure that you have enabled token revocation for your user pool client(s). I didn't see the client-side calls until I did this.

https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html#enable-token-revocation

@ramburg
Copy link

ramburg commented Sep 13, 2021

@sethhitch thanks for highlighting! There should be also a hint about it in the Amplify docs https://docs.amplify.aws/lib/auth/emailpassword/q/platform/js/#re-send-confirmation-code near "Amazon Cognito now supports token revocation and Amplify (from version 4.1.0) will revoke Amazon Cognito tokens if the application is online." in order to ensure this is clear.

@s1mrankaur
Copy link

@ramburg make sure that you have enabled token revocation for your user pool client(s). I didn't see the client-side calls until I did this.

https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html#enable-token-revocation

Hi @sethhitch How to verify if the option is enabled in the pool? From what I read, the option should be enabled by default.

@sethhitch
Copy link

sethhitch commented Sep 13, 2021

@s1mrankaur

  1. Go to App Clients
    Screen Shot 2021-09-13 at 4 17 02 PM

  2. Check the revocation box
    Screen Shot 2021-09-13 at 4 16 54 PM

@KristobalJunta
Copy link

KristobalJunta commented Sep 14, 2021

@s1mrankaur

From what I read, the option should be enabled by default.

It is enabled by default only for newly created user pools (after Jun 11th or so).

@s1mrankaur
Copy link

s1mrankaur commented Sep 16, 2021

@KristobalJunta @sethhitch Thanks. So with https://aws.amazon.com/about-aws/whats-new/2021/06/amazon-cognito-now-supports-targeted-sign-out-through-refresh-token-revocation/ - just the refresh token is revoked, the existing idToken and accessToken continue to be valid-This is what I am reading in comments BUT

here https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html it says
"When you revoke a refresh token, all access tokens that were previously issued by that refresh token become invalid. The other refresh tokens issued to the user are not affected."

Which one is it? Are access tokens revoked or is that not the case?

@sethhitch
Copy link

@s1mrankaur my experience is consistent with the AWS documentation at https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html

JWT tokens are self-contained with a signature and expiration time that was assigned when the token was created. Revoked tokens can't be used with any Cognito API calls that require a token. However, Revoked tokens will still be valid if they are verified using any JWT library that verifies the signature and expiration of the token.

So if you try to use an access token associated with the revoked refresh token to make a Cognito API call like GetUser, it will fail. However, if you make a REST API call to an API Gateway that uses the built-in Cognito authorizer, and you pass the ID token as a bearer token in the Authorization header, it will succeed - the API Gateway authorizer isn't doing revocation checks on the JWT.

@leonardopaiva
Copy link

leonardopaiva commented Oct 6, 2021

I'm asking because i am implementing my first backend authentication flow... would it be a big security issue to just let the token expire? whereas the token expires in 60 minutes.

Currently what I do is clear the localstorage that holds my token, and every request my endpoint receives depends on receiving the token in the autorization bearer, so without the token in the localstorage the user will have to log in again to use the endpoint again.

Someone mentioned a problem with tabs, the point is that using localStorage is different from using sessionStorage, since localStorage shares the token between tabs, so if I clear localstorage in one tab, the other tab won't be able to use the token anymore because it was cleaned.

But of course it's important to invalidate the token on the endpoint, I'm just asking if there are other situations to consider besides the problem with tabs.

@RubberChickenParadise
Copy link

@leonardopaiva the issue people are running into now is an issue inherent to the design or Json Web Tokens. The design goal was to have something that could be issued by a Security Token Service and then validated without a micro service needing to call the STS to validate the token. To do that JWTs are cryptographically signed with an expiration date. The micro service can validate the signature, check the expiration, and say “yep token is still good, off you go to what ever protected resource”.

The “fix” is to identify super critical resources and have that service validate the JWT with the STS. Most of the time, a lag of 5 to 60 min won’t matter. Adding items to a cart for example. Checking out is an example where you would want to validate the token with the STS.

@sethhitch
Copy link

@RubberChickenParadise that's an excellent, clear description of the inherent design challenge here, and it highlights the big remaining gap in the Cognito feature set - the ability to explicitly check a token for revocation when the risk is high. Obviously this is possible, since the Cognito APIs reject JWTs associated with revoked refresh tokens. I'd love to see an API that let the rest of us check revocation as well.

@revmischa
Copy link

I'm having a problem where a user logs out in a browser extension, invalidating their access token which is shared by the website associated with the extension. The token is successfully revoked, however the Auth API doesn't seem to know what to do when it gets an error from the API.

Screen Shot 2021-11-29 at 11 46 09 AM

Ideally the amplify/cognito/auth library would react to this error automatically by removing the access token from storage and logging the user out, since they clearly aren't logged in anymore. Instead my user is stuck in an in-between state where they're "logged in" but calls to Auth.currentAuthenticatedUser() et al fail. User can't do anything and can't log out.

@LinhTran1
Copy link

@s1mrankaur my experience is consistent with the AWS documentation at https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html

JWT tokens are self-contained with a signature and expiration time that was assigned when the token was created. Revoked tokens can't be used with any Cognito API calls that require a token. However, Revoked tokens will still be valid if they are verified using any JWT library that verifies the signature and expiration of the token.

So if you try to use an access token associated with the revoked refresh token to make a Cognito API call like GetUser, it will fail. However, if you make a REST API call to an API Gateway that uses the built-in Cognito authorizer, and you pass the ID token as a bearer token in the Authorization header, it will succeed - the API Gateway authorizer isn't doing revocation checks on the JWT.

This is unbelievable! API Gateway :((

@mengnans
Copy link

mengnans commented Apr 8, 2022

Still facing this issue, do we have a proper solution for it?

@scorobogaci
Copy link

I could not agree more with @jiachen247. I have the same situation of mobile and app usage and the session should be invalidated for each one separately. What actually concerns me more is the fact that I can still use the token after using the "signOut" method of @aws-amplify/auth.

why don't you use different app clients, for mobile and web separately ? that's why there are app clients, no?

@revmischa
Copy link

why don't you use different app clients, for mobile and web separately ? that's why there are app clients, no?

Because from a user experience point of view it's super lame to ask a user to sign in twice in the same browser, once to your extension and once to your website (in my case)

@StephenOkeleke97
Copy link

why don't you use different app clients, for mobile and web separately ? that's why there are app clients, no?

Solid suggestion.

@murdaneta
Copy link

With these items you can revoke a single refresh token
import { CognitoIdentityProvider, RevokeTokenCommand } from "@aws-sdk/client-cognito-identity-provider";

RevokeTokenCommand
ClientId:The client ID for the token that you want to revoke.
ClientSecret:The secret for the client ID. This is required only if the client ID has a secret.[optional]
Token:The refresh token that you want to revoke.

https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_RevokeToken.html

Here you can see how the implementation is:
https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-cognito-identity-provider/

@trunglt-2769
Copy link

@murdaneta
I cannot send the RevokeTokenCommand, there is an Error: Credential is missing. I'm using userpool of Cognito, the signUp, signIn, refreshToken works fine but error occurs at signOut function
Screen Shot 2023-06-20 at 19 13 58

This is providerClient variable:
Screen Shot 2023-06-20 at 19 16 23

This is signOut function
Screen Shot 2023-06-20 at 19 16 37

Do I miss something?

@essaji
Copy link

essaji commented Jun 30, 2023

From 2019 to 2023 & this is still an issue

@anaibol
Copy link

anaibol commented Jul 3, 2023

use supabase

@renaldo10
Copy link

renaldo10 commented Aug 7, 2023

JWT tokens are self-contained with a signature and expiration time that was assigned when the token was created. Revoked tokens can't be used with any Cognito API calls that require a token. However, Revoked tokens will still be valid if they are verified using any JWT library that verifies the signature and expiration of the token.

Working workaround:
GlobalSignOut invalidates refreshToken and accessToken, but not idToken that's why API Gateway calls with cognito authorizer still work.
I send idToken at the Authorization header but in places where i need high security I send accessToken in the body/param too, and I do a cognito call of GetUser method which takes accessToken as param and returns user info, but if the token is invalidated it returns
An error occurred (NotAuthorizedException) when calling the GetUser operation: Access Token has been revoked
So if the response tells you it has been revoked, you will sign out the device directly.

(even if the Cognito team would invalidate idToken, the API Gateway team does simple checks of the sign and expiration date of the token so you would still need the workaround)

@ngupta20
Copy link

At this point, AWS should just let us in and do it for them.

@AleksandarGT
Copy link

Still an issue in 2024. If this issue were a child, I would be sending it to its first day of school soon.

@BwL1289
Copy link

BwL1289 commented Aug 5, 2024

Is this seriously still not addressed? AWS support said this is "expected behavior." For the life of me, I cannot figure out how this could possibly be as designed.

@sammartinez why was this closed?

Linking to this comment for my own sanity.

@rdsedmundo
Copy link

Lots of information have already been said here, I'd just like to add that the Amplify's documentation makes it seem like the token revocation happens if you call Auth.signOut({ global: true }).

But this is not quite I'm looking for, I don't necessarily want to log out the user from their other devices, I just want a SDK call for this endpoint: https://docs.aws.amazon.com/cognito/latest/developerguide/revocation-endpoint.html, which is a functionality that AWS added only in 2021, but this issue was closed in 2019, so as far as I can tell it wasn't really implemented, or at least for some reason it was blended with the global sign out unnecessarily—there's also an use case for a separate endpoint or parameter where I can do a local sign out, revoking the local refresh token, leaving the global ones untouched.

@HuiSF @ashika112 @israx it's been years since the last AWS employee last commented here, are you able to shed a light here with a plan for this to be addressed? Should a new issue be created or could this be possibly reopened, or better clarified?

@github-actions github-actions bot added the pending-maintainer-response Issue is pending a response from the Amplify team. label Oct 14, 2024
@josefaidt
Copy link
Contributor

Hey folks in the thread 👋

As @rdsedmundo and others have mentioned, calling signOut with global: true invalidates all sessions, however calling it without any arguments invalidates the current session's tokens and removes them from state to prevent inadvertent usage with protected APIs after the user is signed out.

In versions 5 and 6 of the aws-amplify library tokens are invalidated and removed from localStorage when calling signOut.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Cognito Related to cognito issues feature-request Request a new feature pending-maintainer-response Issue is pending a response from the Amplify team. Service Team Issues asked to the Service Team
Projects
None yet
Development

No branches or pull requests