1
votes

We're working with two ember applications that each run different version of ember and ember-simple-auth, and want to get ember-simple-auth to work well with both version.

The old app

  • Ember 1.8.1
  • Ember-simple-auth 0.7.3

The new app

  • Ember 2.3.1
  • Ember-simple-auth 1.0.1
  • Uses cookie session store

We trying to change the session API for the older version so that it stores the access and refresh tokens correctly so the new app can use it.

So far, we’ve tried overriding the setup and updateStore methods to work with the authenticated nested object but are still running into issues.

1
ESA 1.0 and < 1.0 use different session formats. Either version won't work with the data generated by the other. Your use case can (and should) most likely be solved in a better and cleaner way. Can you explain in more detail what you're trying to achieve?marcoow
Thanks @marcoow. Our goal is to have the session synced if the two apps are open in the same browser but different tabs. We have this working now. But the one hiccup is if we sign out of the app with newer ESA version, the browser tab with the old ESA version doesn't call syncData for exactly 60 seconds, then the session is expired there too. Any ideas?Patrick Berkeley

1 Answers

3
votes

Disclaimer - Patrick Berkeley and I work together. We found a solution after posting this question that I figured I would share.

In order for a 0.7.3 version of ember-simple-auth's cookie store to play nicely with a 1.0.0 version, we did have to normalize how the cookie was being formatted on the app with the earlier version in a few key places, mostly centered around the session object (the 0.7.3 session is an ObjectProxy that can be extended in the consuming app to create your own custom session).

The methods that we needed to override, centered around the structure of data being passed to the cookie store to persist and what was being returned when a session was being restored. The key difference is on version 0.7.3, the access_token, etc is stored top-level on the content object property of the session. With 1.0.0. this is nested inside another object inside content with the property name of authenticated. We therefore needed to ensure that everywhere we were making the assumption to set or get the access_token at the top level, we should instead retrieve one level deeper. With that in mind, we came up with these methods being overridden in our custom session object:

// alias access_token to point to new place
access_token: Ember.computed.alias('content.authenticated.access_token'),

// overridden methods to handle v2 cookie structure
restore: function() {
  return new Ember.RSVP.Promise((resolve, reject) => {
    const restoredContent = this.store.restore();
    const authenticator   = restoredContent.authenticated.authenticator;

    if (!!authenticator) {
      delete restoredContent.authenticated.authenticator;
      this.container.lookup(authenticator).restore(restoredContent.authenticated).then(function(content) {
        this.setup(authenticator, content);
        resolve();
      }, () => {
        this.store.clear();
        reject();
      });
    } else {
      this.store.clear();
      reject();
    }
  });
},

updateStore: function() {
  let data = this.content;
  if (!Ember.isEmpty(this.authenticator)) {
    Ember.set(data, 'authenticated', Ember.merge({ authenticator: this.authenticator }, data.authenticated || {}));
  }

  if (!Ember.isEmpty(data)) {
    this.store.persist(data);
  }
},

setup(authenticator, authenticatedContent, trigger) {
  trigger = !!trigger && !this.get('isAuthenticated');

  this.beginPropertyChanges();
  this.setProperties({
    isAuthenticated: true,
    authenticator
  });
  Ember.set(this, 'content.authenticated', authenticatedContent);
  this.bindToAuthenticatorEvents();
  this.updateStore();
  this.endPropertyChanges();

  if (trigger) {
    this.trigger('sessionAuthenticationSucceeded');
  }
},

clear: function(trigger) {
  trigger = !!trigger && this.get('isAuthenticated');

  this.beginPropertyChanges();
  this.setProperties({
    isAuthenticated: false,
    authenticator:   null
  });
  Ember.set(this.content, 'authenticated', {});
  this.store.clear();
  this.endPropertyChanges();

  if (trigger) {
    this.trigger('sessionInvalidationSucceeded');
  }
},

bindToStoreEvents: function() {
  this.store.on('sessionDataUpdated', (content) => {
    const authenticator = content.authenticated.authenticator;

    this.set('content', content);

    if (!!authenticator) {
      delete content.authenticated.authenticator;
      this.container.lookup(authenticator).restore(content.authenticated).then((content) => {
        this.setup(authenticator, content, true);
      }, () => {
        this.clear(true);
      });
    } else {
      this.clear(true);
    }
  });
}.observes('store'),

This took us most of the way there. We just needed to ensure that the authenticator name that we use matches the name on 1.0.0. Instead of 'simple-auth-authenticator:oauth2-password-grant', we needed to rename our authenticator via an initializer to 'authenticator:oauth2'. This ensures that the apps with the newer version will be able to handle the correct authenticator events when the cookie session data changes. The initializer logic is simple enough:

import OAuth2 from 'simple-auth-oauth2/authenticators/oauth2';

export default {
  name:   'oauth2',
  before: 'simple-auth',

  initialize: function(container) {
    container.register('authenticator:oauth2', OAuth2);
  }
};

The above satisfies our needs- we can sign in to an app using ember-simple-auth 0.7.3 and have the cookie session stored and formatted properly to be handled by another app on ember-simple-auth 1.0.0.

Ideally, we would just update the Ember and Ember Simple Auth versions of the app though business needs and the fact that we wanted to focus our energies on the v2 versions (which are completely fresh and new code bases) propelled us to go down this path.