-2

I am using ember-simple-auth & ember-simple-auth-token to maintain session on Ember. I do not want to use the refresh-token approach, instead would like to receive a new jwt token in the response header from every service request.

how can i keep my session alive by updating the jwt token in the session every time I receive a response from the server?

  • I am receiving the JWT token as part of every response header from my backend service. The idea is to have an expiry time set to 30 mins initially during authentication and then with every interaction with the backened I am trying to capture the new token and update it in the session using the adapter-> handelResponse method. in a nutshell All I am trying to do is have a sliding session. – Prajwal Boloor May 30 '17 at 17:46
  • The issue I am facing is that Ember-simple-auth stores the token & expiry time inside session.data.authenticated and this is termed as readonly in the documentation. how can I actually keep the session alive if I am not allowed to update the token? – Prajwal Boloor May 30 '17 at 17:52

1 Answers1

0

This can be done, but will require extending ember-simple-auth and ember-simple-auth-token. It's not exactly recommended to use your approach, the access token plus refresh token is definitely the preferred method. It also scales better since your resource servers don't need the ability to authenticate, just to authorize and validate tokens. It also adds a bit of overhead to generate a new token for every single request. While I don't recommend this course, here's how it can be done:

As you've discovered, you cannot update the session directly, but if you dive into the source code you will discover that ember-simple-auth uses an event to update it. However, this event will only work if you override/customize an authenticator. Likely ember-simple-auth-token's authenticators/jwt.js. If you look at their authenticator, you can see an example of how to update the session.

So, I would recommend creating your own custom JWT token authenticator that extends ember-simple-auth-token's. It will likely add a method to update the session or perhaps better to handle the raw request headers that contain a new access token.

After that's complete, you have a choice. You can either override your own adapter and call that function yourself from there, or perhaps the better choice would be to override ember-simple-auth's data-adapter-mixin mixin.

If you override ember-simple-auth data-adapter-mixin, this seems to be the best starting point: handleResponse. If you override that function, you should have access to your raw API response from which you can call your update session function.

As you can see though, this is not a trivial change. It's definitely going against the grain of what these libraries were originally designed to do, but if you put in a good bit of work, it should be possible.

Updates for OP's comment
I actually implemented both of those requirements before: "1) to have session timeout after a pre configured idle time. 2) allow certain set of users to proxy in and switch the context of login user on the fly". It turns out that it's actually pretty easy to accomplish with a refresh token model.

To support #1, the refresh token expiration should be set to the length of the idle time before logout. e.g. if the refresh token is set to expire in 30 minutes and the access token expires in 5 minutes. The client will automatically fetch new access tokens (and optionally a refresh token) every ~5 minutes, based on leeway. However, if the user leaves the page for over 30 minutes, the refresh token will be expired and they will need to re-authenticate.

For #2, unfortunately, it does take a bit of overriding of ember-simple-auth-token to work, but it's relatively easy and something I have already implemented. Basically you create create an custom authenticator that has an addition function to exchange and access token for a new one with updated context/state. See my implementation below:

ember-simple-auth-token overridden authenticator:

 /**
    Provides a public method to call to exchange an access token for an
    updated access token with updated data encoded in it. This method
    essentially asks the backend to update an access token with different
    values. This is, at the moment, is only used to switch the company root
    attribute in the token (AKA switch companies for admins).

    @param {string} token - The valid access token
    @param {object} [headers] - An optional object which can add additional
    headers to the request

    @param {object} additionalData - An object which contains the data which should be updated on the token. It should look  something like this:
    ```
    {
        company_root_id: '<UUID of a valid company root>'
    }
    ```
    @return {Promise} A promise which is the request. It will either resolve
    with the updated session data or reject with the error.
  */
 exchangeAccessToken(token, headers, additionalData) {
     const data = this.makeRefreshData(token);
     Ember.merge(data, additionalData);

     return new Ember.RSVP.Promise((resolve, reject) => {
         this.makeRequest(this.serverTokenRefreshEndpoint, data, headers).then(response => {
             Ember.run(() => {
                 try {
                     const sessionData = this.handleAuthResponse(response);

                     this.trigger('sessionDataUpdated', sessionData);
                     resolve(sessionData);
                 } catch(error) {
                     reject(error);
                 }
             });
         }, (xhr, status, error) => {
             Ember.Logger.warn(`Access token could not be refreshed - server responded with ${error}.`);
             reject();
         });
     });
 }

Action to trigger the token exchange:

 switchCompany(companyId) {
     let session = this.get('_session.session');

     if(!this.get('isAdministrator')) {
         throw new Error('Logged in user is not an administrator, they cannot switch companies.');
     }

     let token = session.get('authenticated.access_token');
     let appInstance = Ember.getOwner(this);
     let authorizerName = session.get('authenticator');
     let authorizer = appInstance.lookup(authorizerName);
     if(!authorizer) {
         throw new Error(`Authorizer (${authorizerName}) does not exist`);
     }
     if(typeof authorizer.exchangeAccessToken !== 'function') {
         throw new Error(`Authorizer (${authorizerName}) does not have an \`exchangeAccessToken\` method.`);
     }
     return authorizer.exchangeAccessToken(token, null, {
         company_root_id: companyId
     });
 }

The backend would obviously need to be extended to accept additional parameters on the refresh token endpoint which allows the user to switch roles, if authorized.

Michael Boselowitz
  • 3,012
  • 1
  • 19
  • 22
  • Hi Michael, Thanks for you valuable suggestion. My thoughts on refreshing the token was based on two requirements, 1) to have session timeout after a pre configured idle time. 2) allow certain set of users to proxy in and switch the context of login user on the fly. As you suggested, I am evaluating other means to accomplish these tasks and by not having to override the base implementation of ember-simple-auth. If those approaches fail, then will try extending the authenticator and mixin as a last resort. – Prajwal Boloor Jun 08 '17 at 20:39
  • @PrajwalBoloor, I updated my answer to show how you can accomplish your requirements with a refresh token model. – Michael Boselowitz Jun 09 '17 at 18:13