1
votes

Creating a simple login based on Ember.js Authentication - the right way - on first access the user get's transitioned to a session login view and once validated transitioned back to the route they first tried to access all works fine except the login form remains in view after the transition and render of the views for the new route.

Setup is:

Ember : 1.7.0 Ember Data : 1.0.0-beta.8.2a68c63a Handlebars : 1.3.0 jQuery : 1.11.1

Router:

Router.map(function() {
  this.resource('sessions', function() {
    this.route('logout');
    this.route('login');
  });

  this.resource('posts', { path: '/posts' }, function() {
    this.route('post', { path: '/post/:post_id' });
    this.route('new');
  });
});

Application route:

export default Ember.Route.extend({
  beforeModel: function() {
    this.transitionTo('posts');
  }
});

Posts route:

export default Ember.Route.extend({
  beforeModel: function(transition) {
    if(Ember.isEmpty(this.controllerFor('sessions').get('token'))) {
      return this.redirectToLogin(transition);
    }
  },

  redirectToLogin: function(transition) {
    this.controllerFor('sessions').set('attemptedTransition', transition);
    return this.transitionTo('sessions');
  },

  model: function() {
    return this.store.find('post');
  },

  actions: {
    ...
  }
});

Sessions route:

export default Ember.Route.extend({
  // Reset Session Controller to avoid date from a past authentication
  setupController: function(controller, context) {
    controller.reset();
  }
});

Sessions controller:

export default Ember.Controller.extend({
  // initialization method to verify if there is a access_token cookie set
  // so we can set our ajax header with it to access the protected pages
  init: function() {
    Ember.Logger.log('sessons.init');
    Ember.Logger.log(Ember.$);
    this._super();
    if (Ember.$.cookie('access_token')) {
      Ember.$.ajaxSetup({
        headers: {
          'Authorization': 'Bearer ' + Ember.$.cookie('access_token')
        }
      });
    }
  },

  // overwriting the default attemptedTransition attribute from the Ember.Controller object
  attemptedTransition: null,


  // create and set the token & current user objects based on the respective cookies
  token:               Ember.$.cookie('access_token'),
  currentUser:         Ember.$.cookie('auth_user'),

  // create a observer binded to the token property of this controller
  // to set/remove the authentication tokens
  tokenChanged: function() {
    Ember.Logger.log('tokenChanged');
    if (Ember.isEmpty(this.get('token'))) {
      Ember.$.removeCookie('access_token');
      Ember.$.removeCookie('auth_user');
    } else {
      Ember.$.cookie('access_token', this.get('token'));
      Ember.$.cookie('auth_user', this.get('currentUser'));
    }
  }.observes('token'),

  // reset the controller properties and the ajax header
  reset: function() {
    this.setProperties({
      username_or_email: null,
      password:          null,
      token:             null,
      currentUser:       null
    });
    Ember.$.ajaxSetup({
      headers: {
        'Authorization': 'Bearer none'
      }
    });
  },

  actions: {
    loginUser: function() {
      Ember.Logger.log('loginUser');

      var _this = this;

      var attemptedTrans = this.get('attemptedTransition');
      var data =           this.getProperties('username_or_email', 'password');
      Ember.Logger.log(data);

      // Clear form fields
      this.setProperties({
        username_or_email: null,
        password: null
      });

      // send a POST request to the /sessions api with the form data
      Ember.$.post('/api/session', data).then( function(response) {
        Ember.Logger.log(response);

        // set the ajax header
        Ember.$.ajaxSetup({
          headers: {
            'Authorization': 'Bearer ' + response.api_key.access_token
          }
        });

        // create an apiKey record on the local storage based on the returned object
        var key = _this.get('store').createRecord('apikey', {
          accessToken: response.api_key.access_token
        });

        // find a user based on the user_id returned from the request to the session api
        _this.store.find('user', response.api_key.user_id).then(function(user) {
          Ember.Logger.log('user->');
          Ember.Logger.log(user);
          // set this controller token & current user properties
          // based on the data from the user and access_token
          _this.setProperties({
            token:        response.api_key.access_token,
            currentUser:  user.getProperties('username') 
          });

          // set the relationship between the User and the ApiKey models & save the apiKey object
          key.set('user', user);
          key.save();

          user.get('api_keys').content.push(key);

          Ember.Logger.log(attemptedTrans);
          if(attemptedTrans) {
            attemptedTrans.retry();
            _this.set('attemptedTrans', null);
          }
        });
      });
    }
  }
});

It's the last attemptedTrans.retry() that initiates the transition. Am I missing something?

2

2 Answers

0
votes

You likely have bad html in your handlebars (missing closing tags etc).

0
votes

Kingpin2k's answer above pointed me in the right direction. For anyone else stumbling across this question it boiled down to an error in my use of {{outlet}}. My original application template had the following structure:

{{#if isAuthenticated}}
  <div>
    ... (Nav etc)
  </div>

  {{outlet}}
{{else}}
  {{outlet}}
{{/if}}

The effect of this was that once isAuthenticated is true the template is re-rendered and the script tags used to identify the login form elements are no longer valid - hence they don't get removed.

Simple solution was to change the template to:

{{#if isAuthenticated}}
  <div>
    ... (Nav etc)
  </div>
{{/if}}

{{outlet}}