ember-simple-auth: Refresh tokens don't refresh when device is asleep longer than expiration time.

I’m using refresh tokens with a 15 minute expiration with everything working well, however I’m having an issue where if the users device goes to sleep for longer than the refresh token is active, when their device wakes back up, the next ajax request will log them out.

The problem is that while the device is asleep, the refresh timer doesn’t kick off. Then, once the user wakes up their device and the application attempts to hit an endpoint, the access token is expired and the session is invalidated and user logs out.

Normally if a user reloads the application after the refresh token is expired, the session is restored and during that process a new access token is obtained. However, that session restore never happens as the timer expires but the device is asleep.

It would be nice if before making an ajax request, we can check to see if the token is expired and request a new one. This is the approach I take with Angular using an http interceptor to check for a 401, but Ember Data/ESA seems to have a different flow.

My approach to fix this is outlined below. The code isn’t the best, but it gets it done.

  1. Modify the ajax method in my adapter. I can access the session service and check if the token has expired. If it is, and user is authenticated, restore the session.

  2. After session restore promise resolves, perform the original request.

Any thoughts or suggested ways around this? Happy to help put a pull request together as well if this option should be added to the adapter mixin.

My code is below, but any other workarounds are appreciated. Below is what I added to my application adapter.

  ajax() {
    const session = this.get('session.session');

    // We'll need to manually do the _super apply, so
    // it's bound correctly from within our promise return
    var args = arguments,
      self = this,
      _super = this._super;

    if (new Date().getTime() > session.get('content.authenticated.expires_at')) {
      return session.restore()
        .then(function() {
          return _super.apply(self, args);
        });
    } else {
      return this._super(...arguments);
    }
  }

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Reactions: 1
  • Comments: 23 (12 by maintainers)

Most upvoted comments

@rhyek to make this._super work in that case, I kept the this._super reference inside variable and later used that reference.

ajax() {
    const ajax = this._super, that = this;
    const session = this.get('session');
    const sessionData = session.get('data.authenticated');
    const authenticator = Ember.getOwner(this).lookup(sessionData['authenticator']);
    if (new Date().getTime() > sessionData['expires_at']) {
      return authenticator._refreshAccessToken(sessionData['expires_in'], sessionData['refresh_token']).then(() => {
        return ajax.call(that, ...arguments);
      });
    } else {
      return ajax.call(that , ...arguments);
    }
}

PS: Though above solution is not working properly for me. It makes multiple refreshToken call if multiple ajax call fails. I’m trying to tackle this situation, If I succeed I’ll update this comment with my solution.

The difference I had in mind was for the session.{ensureNotExpired|validate|aquireAuthorizedData}() promise to resolve with auth data for a request, rather then just resolving and leaving it up to the consuming code to fetch the auth data from session.data.authenticated. I apologise if you’ve planned the same.

I think that’s mostly an implementation detail really. The idea is the same - making functionality available publicly that kind of “validates” the session and lets the authenticator do whatever necessary to make sure the session is valid.

I think if restore can be used for that, it’d be good to do so rather than introducing a new concept. restore should be renamed in that case though…