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

Force Token to Expire #1972

Open
hollygirouard opened this issue Oct 24, 2018 · 38 comments
Open

Force Token to Expire #1972

hollygirouard opened this issue Oct 24, 2018 · 38 comments
Assignees
Labels
Auth Related to Auth components/category Cognito Related to cognito issues feature-request Request a new feature Service Team Issues asked to the Service Team

Comments

@hollygirouard
Copy link

hollygirouard commented Oct 24, 2018

** Which Category is your question related to? **

Authentication

** What AWS Services are you utilizing? **

AWS Amplify Authentication

** Provide additional details e.g. code snippets **

I know the login or refresh time is in the jwt but I feel like there is another way I'm missing to force a logout.

I'm having some trouble understanding the best way to log a user out after, say 5 minutes, or x amount of inactivity. I've seen a lot of hacky solutions, and npm packages, but I'd much rather do it correctly and efficiently as you guys suggest - without needing to add any additional packages.

Here are some of the related dependencies I'm using in package.json:

   "aws-amplify": "^1.1.6",
   "aws-amplify-react": "^2.0.5",
   "aws-amplify-react-native": "^2.0.1",
   "expo": "^30.0.0",
   "react-native": "^0.55.0",
   "react-navigation": "^2.13.0",
   "react-redux": "^5.0.7",
   "redux": "^4.0.0",
   "redux-promise": "^0.6.0"

Here's my App.js file:

import React, { Component } from 'react'
import { Provider } from 'react-redux'
import { createStore, applyMiddleware } from 'redux'
import { Font, Asset, AppLoading } from 'expo'
import ReduxPromise from 'redux-promise'
import Amplify from 'aws-amplify'
import AppNavigator from './AppNavigator'
import reducers from './src/reducers'
import awsExports from './src/aws-exports'

Amplify.configure(awsExports)
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      fontLoaded: false,
      isReady: false,
    }
  }

  async componentDidMount() {
    await Font.loadAsync({
      'NunitoSans-Bold': require('./src/assets/fonts/NunitoSans-Bold.ttf'),
      'NunitoSans-Regular': require('./src/assets/fonts/NunitoSans-Regular.ttf'),
      'NunitoSans-SemiBold': require('./src/assets/fonts/NunitoSans-SemiBold.ttf'),
    })
    this.setState({ fontLoaded: true })
  }

  cacheResourcesAsync = async () => {
    const images = [
      require('./assets/fav.png'),
      require('./assets/splash.png'),
    ]

    const cacheImages = images.map(image => ( Asset.fromModule(image).downloadAsync()))
    return Promise.all(cacheImages)
  }

  render() {
    const store = createStoreWithMiddleware(reducers)
    return (
      this.state.fontLoaded && this.state.isReady ? (
          <Provider store={ store }>
              <AppNavigator />
          </Provider>
      ) : (
          <AppLoading
            startAsync={ this.cacheResourcesAsync }
            onFinish={ () => this.setState({ isReady: true }) }
            onError={ console.warn }
          />
      )
    )
  }
}

export default App

In my app's account settings, I created a successful Logout Function, which is what I'd like to call after the user should be logged out.

  handleSignOut = () => {
    cognitoSignOut()
      .then(() => {
        AsyncStorage.clear()
        this.navigate('Auth')
      })
      .catch(err => this.setState({ errorMessage: err.message }))
  }

I guess the biggest confusion for me is that Amplify has all kinds of documentation on how to keep the user logged in and how to refresh the token, but not much on forcing the token to expire.

Any suggestions you have will be very helpful. Thanks so much.

@nidsharm nidsharm added the Auth Related to Auth components/category label Oct 24, 2018
@powerful23
Copy link
Contributor

@hollyewhite if you want to expire/revoke the tokens, you can check this doc: https://aws-amplify.github.io/docs/js/authentication#sign-out

@hollygirouard
Copy link
Author

@powerful23 Thanks for the reply, but I've definitely seen that. My question is a little more detailed than what is in that doc.

@nidsharm
Copy link

nidsharm commented Oct 26, 2018

@hollyewhite , Amplify stores the jwt tokens for the user in the cache. We currently only use signOut as a way to expire these tokens and remove them from cache. You could have an inactivity counter and then call signOut as soon as the limit is reached. There isn't any other way using Amplify to expire these tokens as of now.

@hollygirouard
Copy link
Author

Thank you @nidsharm for the clarity. That's very helpful. Is it on your product roadmap?

@nidsharm
Copy link

We try to add things to our product roadmap based on the feature-requests we get in the issues for Amplify. I can make this issue into a feature-request and it keep it open until it gets picked up.

@nidsharm nidsharm added the feature-request Request a new feature label Oct 26, 2018
@hollygirouard
Copy link
Author

Thank you!

@cbernardes
Copy link

I am having the same issue as I have been working with financial institutions. Users usually are logout after 3 min of inactivity.

On top of that, the refreshToken only happens when the token is close to expire, which means close to 1 hour. Unfortunately I could not use any value from my user session to know the last activity.
Auth does not have the refreshToken method available, in which I could use to reinforce my user's activity.
Also, one hour makes very hard to test the application.

@undefobj
Copy link
Contributor

undefobj commented Dec 4, 2018

@hollyewhite @cbernardes we discussed this in a planning meeting today and having Amplify control when to call global sign out based on some timer would be a complex state tracking mechanism that could introduce unintended side effects. We would need to evaluate this very carefully before adding something like this which could be accomplished in app code that simply calls Auth.signOut({ global: true }) based on your business logic.

@cbernardes one option we discussed was also providing a state mechanism for you to know the last activity that took place within an Amplify category, perhaps with updates to a session object. This also could get complex and a simple event emitter using Hub might make more sense that you could leverage in your own state management layer.

There is no way for the library to know what "inactivity" means for all applications. Is this a mouse move, an http action, or something else like form entry. Could you give more concrete details on what requirements for such a feature might look like from your perspective if we are to look at it in the future?

@cbernardes
Copy link

@undefobj Thanks for considering and discussing this feature, however it is not quite the requirement. Sign out globally is not what is expected.
The point here is to configure a lower accessToken expiration, which now is hardcoded to 1hour.

About "There is no way for the library to know what "inactivity" means for all applications", Amplify triggers the refreshToken function everytime the user tab/page is active or rerendered.

Current scenario:

  • The user logs in. The token is generated to expire 1h later.
  • The user refresh the website.
  • Amplify automatically triggers the refreshToken.
  • The code verifies if the token exp is greater than current time.

Expected scenario.

  • The user logs in. The token is generated to expire after the time configured.
  • The user refresh the website.
  • Amplify automatically triggers the refreshToken.
  • The code verifies if the token exp is greater than current time.

Moreover, thanks for the suggestion to fix the problem. I have implemented something to manage the timeout myself.

@choxnox
Copy link

choxnox commented Dec 6, 2018

How can we keep the session intact but refresh user attributes? For example, I submit a form to the server which updates user attributes, then I redirect the form to another page but before displaying the page content I do let user = await Auth.currentAuthenticatedUser(); because I'd like to get updated attributes but unfortunately all this does is fetch attributes from the cache. Any workaround this use case?

@samuelcastro
Copy link

@choxnox You can bypass the cache: bypassCache: true

@samuelcastro
Copy link

@cbernardes How did you manage to fix this problem?

I'm facing the same issue, we want to kick the user out after an hour of inactivity however right now even after days the session is still valid due to the refresh token mechanism.

@annjawn
Copy link

annjawn commented Jun 4, 2019

How about giving us an ability to disable automatic refresh token? I guess I also don't understand the explanation:

we discussed this in a planning meeting today and having Amplify control when to call global sign out based on some timer would be a complex state tracking mechanism that could introduce unintended side effects. We would need to evaluate this very carefully before adding something like this which could be accomplished in app code that simply calls Auth.signOut({ global: true }) based on your business logic.

How does Amplify keep track of when to call the refreshToken method if it cannot keep track of it's expiration period. Providing a way within the library the capability for the app to respect the token's expiration and signOut should be available out of the box. What if I don't want Cognito refreshing the token automatically?

@masbaehr
Copy link

Hi, i came across this today as i'd like to develop an application which contains very confidential data, and the credentials should not auto-refresh on reboot or forced reset. Sure i could do some auto-logout after a while via Javascript, but when the expiration period of the refresh token is reached (e.g. on Cognito this can be set to 1DAY-3600DAY) it should re-ask for credentials. Not doing so is very bad in terms of security and should really be changed (or some easy way to set this should be given, e.g boolean flag)

@sammartinez sammartinez added the React Native React Native related issue label Oct 9, 2019
@r-moore
Copy link

r-moore commented Oct 21, 2019

This is an issue for us too.

Overriding expiration client side with an auto-logout timer just feels wrong. e.g. how might a browser closing/reopening affect the inactivity timer?

Better to issue a token with shorter validity, so that it can expire even if the browser is closed.

Is 1 day really the shortest option?

@cbernardes
Copy link

It is 1 hour the minimum.
I have written an article about Cognito if it is of help.

https://medium.com/@cbernardes/serverless-things-i-wish-i-had-known-before-i-started-part-1-aws-cognito-cf5d3a0c3d9d

@r-moore
Copy link

r-moore commented Oct 22, 2019

Yes 1 hour for the access token, but minimum 1 day expiry for the refresh token (which is kept in browser storage and so could, in theory, be used to re-authenticate & continuously refresh the session against Cognito without the need for username/password to be supplied again).

That is the security concern, which makes it hard to choose Cognito for say finance/banking apps that need to enforce short session timeouts.

Or am I missing something?

@cbernardes
Copy link

@r-moore I am not aware of this. The access token is prevalent over the secret token, therefore, if the access token is expired it can no longer be refreshed.
Could you share where your read this info about secret token expiration period?

@r-moore
Copy link

r-moore commented Oct 22, 2019

When I use Auth.signIn() in my SPA, I see the following created in browser storage:
image

accessToken, idToken are OK but it is the refreshToken that concerns me for an SPA.

https://aws-amplify.github.io/docs/js/authentication#token-refresh "By default, Amplify will automatically refresh the tokens for Google and Facebook...". I am assuming that in order to facilitate this Amplify keeps a refreshToken around.

According to https://auth0.com/learn/refresh-tokens/ refreshTokens are long-lived by nature (hence the minimum setting of 1 day validity within Cognito). If that's the case I would rather not have one at all in browser storage (or at least trust that it will be destroyed when the browser window closes).

I do feel like I am missing something though, I'd really like to understand this better!

@cbernardes
Copy link

Amplify only refreshes the token if the user is active.

@r-moore
Copy link

r-moore commented Oct 23, 2019

Yes understood... Amplify might not use the refreshToken but it still exists.

I am concerned that the following scenario is still possible on a computer that 'Bob and Alice' share:

  1. Bob's accessToken expires after 1hr of inactivity, he is taken back to the app login screen.
    ... some hours pass, Alice now uses the same computer...
  2. Alice goes to login screen, opens browser console and copies Bob's refreshToken from localStorage.
  3. Alice can now theoretically get a new accessToken impersonating Bob (by using his refreshToken - no password required)

I understand it is maybe a far fetched, but if the refreshToken validity is very long (1 year) the risk of this attack becomes more unacceptable. Is my concern unfounded?

@masbaehr
Copy link

I also think there should be a better way of handling this. Especially if you want to ensure a proper logout after a manually set timeout.

Ultimately i think a javascript solution will be the easiest to manually perform a logout. Better than hoping for an implementation change by cognito

A common way to do this would be to overwrite the XMLHttpRequest to check how much time has passed and if the user was inactive long enough , trigger a manual logout.

Example:

var oldXHR = window.XMLHttpRequest;
function newXHR() {
    var realXHR = new oldXHR();
    realXHR.addEventListener("readystatechange", function() { 
        console.log("an ajax request was made");
        //check passed time here, and reset if user was not inactive long enough: clearTimeout(reloadTimer);
      //if the user was inactive long enough:  perform  Auth.signOut()
    }, false);
    return realXHR;
}
window.XMLHttpRequest = newXHR;

Note: If for your application "inactivity" also includes mouse movements, you can perform the same operation for "mousemove"

@cbernardes
Copy link

@r-moore try it. I dont think it will work. Refresh token has one purpose and access token another. With only the refresh token you cant have access to the application

@annjawn
Copy link

annjawn commented Oct 23, 2019

There needs to be away to set appropriate timeout for the refresh token. I concur with @r-moore on this. We had to implement a browser based force logout but it doesn’t work if the user simply closes the browser and opens the webapp a few minutes later since the timer resets. Not at all ideal for financial, or even other mission critical apps. We are planning to phase out using Cognito because of this reason and a few others.

@cbernardes
Copy link

@annjawn as I wrote in the article I shared one big issue is AWS no invalidating the cognito access token. Another thing is using the refresh token to update the expiration time of a token. Another thing is the access token logout before 1h which has to be done "manually".

Can someone describe an use case?

@masbaehr
Copy link

It was already said: Banking, Sensitive data, Shared computers (forgot to logout properly) ecc.

@cbernardes
Copy link

Thanks @masbaehr . A use case is a sequence os actions which defines a potential scenario of a problem. What you just mentioned is the environment.

The use case @r-moore shared about "Bob and Alice" is not valid because the RefresToken DOES NOT create a token, but refreshes an existing one (if you have it). I did the test myself and deleted (from the localstorage) the IdToken generated by Amplify "CognitoIdentityServiceProvider.{code}.{code}.idToken" and new token wont be created.

The main point of this issue is that even deleting (or invalidating through the logout method) this IdToken it is still valid, so if I copy it back it will work.

This is the ROOT cause, the IdToken not being invalidated, not the RefreshToken.

@r-moore
Copy link

r-moore commented Oct 27, 2019

@cbernardes the idToken is not the credential token so deleting it is irrelevant really. idToken is just profile info about the user.

In my test, I have deleted the authToken (i.e. the credentials used for my API) and then after using Amplify's Auth.currentAuthenticatedUser() I can see that the authToken is recreated in my browser without having to provide username/password again.

In my test I also left it over 1 hour and did the same - it works so long as the refreshToken is still in the browser's localStorage and not expired.

@cbernardes
Copy link

@r-moore the point of having a token is to grant access to the API, and an API using cognito as an Authenticator uses idToken, the other one won't grant access.

Please check the AWS documentation.
"The primary purpose of the access token is to authorize API operations in the context of the user in the user pool"

https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-with-identity-providers.html

@deepku2
Copy link

deepku2 commented Dec 13, 2019

@cbernardes IdToken and AccessToken are not same. As @r-moore mentioned IdToken is just profile info about the user. AccessToken is the one used for authorization with APIs.
Storing RefreshToken in browser (Local Storage or Cookie) is not a good idea from security point of view for any enterprise application (financial or otherwise)

@n8behavior
Copy link

Agree with @deepku2 and so does AWS. See their docs for how to securely store data.

https://aws-amplify.github.io/docs/js/authentication#managing-security-tokens

Using that, store your data someplace more secure or encrypt it. You can even implement a storage object that filters out the refresh token so it never gets stored. This will force a new login. Maybe that's best for banks. I'd like to think my bank is not storing my refresh token in local storage :)

@ericclemmons ericclemmons added Cognito Related to cognito issues Service Team Issues asked to the Service Team labels May 11, 2020
@yourGuy
Copy link

yourGuy commented Aug 13, 2020

It's a long thread and I still don't understand even the very basics
Does the refresh token expire? And if so what does that do ? Does it get refreshed? In my experience tokens seem to work after many days.
Finally is it possible to log out a user after the refresh token expires, presumably one day?
I mean is anybody even logging users out? Can you describe your flow?

@masbaehr
Copy link

A manual logout was/is always possible and it is not the problem of this issue.

You can just do Auth.signOut() from @aws-amplify/auth if you want to do a regular logout.

However it seems the user will never logout if you close the browser tab without clicking "logout". What you can do is to reduce the validity to 1 hour in the cognito settings, but a much shorter timeout would be very useful

@yourGuy
Copy link

yourGuy commented Aug 13, 2020

Right, I know about Auth.signOut(), what I'm looking for is any kind of relevant event triggered by amplify-js, to call it from.
Anything else to me spells: "Do it by hand and consider the million security concerns you ran away from to here in the first place"

@j-lee8
Copy link

j-lee8 commented Mar 4, 2021

Is there a way to force the token to be expired? We need to test trying to refresh the token while the network is down to see how it fails, but right now the only option is waiting over an hour without any calls to the server for the token to be expired.

@nihp
Copy link

nihp commented Apr 20, 2021

My app makes me log out from the app 3 times a week. I don't know why the behaviour is like this? Any idea. My app is in production. (React-Native app).

I need to make the user logged in as it is.

@j-lee8
Copy link

j-lee8 commented Apr 20, 2021

My app makes me log out from the app 3 times a week. I don't know why the behaviour is like this? Any idea. My app is in production. (React-Native app)

We also have a similar issue. Users will go to the page the following morning and nothing will load, then log out and log back in and it's fine. If you look at the network tab it does call Cognito to refresh the token so I don't know what's going on.

@nadetastic nadetastic added the Expo For issues where expo is in use label Feb 7, 2023
@tannerabread tannerabread removed React Native React Native related issue Expo For issues where expo is in use labels Mar 13, 2023
@nadetastic nadetastic self-assigned this Sep 26, 2023
@ak37165
Copy link

ak37165 commented May 25, 2024

Agree with @deepku2 and so does AWS. See their docs for how to securely store data.

https://aws-amplify.github.io/docs/js/authentication#managing-security-tokens

Using that, store your data someplace more secure or encrypt it. You can even implement a storage object that filters out the refresh token so it never gets stored. This will force a new login. Maybe that's best for banks. I'd like to think my bank is not storing my refresh token in local storage :)

The link seems broken. I could not find any option to provide a custom storage option in Gen2 Amplify Auth SDK (there used to be one in amplify-cognito-js-sdk, the old one). So basically the tokens stay stored in localStorage.

Problems, even if we handle the beforeunload event in the browser and call signOut, then:

  • If the user puts the computer to sleep and then wakes the computer up, the idle/inactivity timer will not have counted the sleep time (Javascript will pretend that time during sleep did not ever happen) - the tokens will stay stored past the allowed time.
  • If the user does not close the tab but switches away, the idle timer will no longer be active (especially on battery operated devices) - so if the user comes back to the tab again, even if after the required timeout, the token will not have been deleted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Auth Related to Auth components/category Cognito Related to cognito issues feature-request Request a new feature Service Team Issues asked to the Service Team
Projects
None yet
Development

No branches or pull requests