heartsentwined / ember-auth

Authentication framework for ember.js.
http://ember-auth.herokuapp.com/
397 stars 43 forks source link

Errors when calling App.reset() #129

Open conrad-vanl opened 10 years ago

conrad-vanl commented 10 years ago

Hey - I'm working on integration testing on an ember app that uses ember-auth. I noticed that calling App.reset() causes errors with ember-auth, but I'm not sure if it's something specific to our implementation or an actual bug. The error(s) I get when calling App.reset() depend on which modules I have enabled, but are always along the lines of:

Assertion failed: Error while loading route: TypeError: Cannot call method 'retrieve' of null 

EDIT: the following is inaccurate, see below for more info If I remove the rememberable module, the error references Cannot call method 'serialize' of null with a very similar stack trace. FWIW, the stack trace doesn't include anything either application specific or ember-auth specific (all of the line numbers reference ember core):

(anonymous function)
Ember.Router.reopenClass._defaultErrorHandler
triggerEvent
trigger
handleError
invokeCallback
(anonymous function)
EventTarget.trigger
(anonymous function)
DeferredActionQueues.flush
Backburner.end
Backburner.run
Ember.run
Ember.run.join
Ember.Application.Ember.Namespace.extend.reset
(anonymous function)
InjectedScript._evaluateOn
InjectedScript._evaluateAndWrap
InjectedScript.evaluate

Is anyone else using App.reset() and ember-auth with success?

conrad-vanl commented 10 years ago

I am now going to assume this is a bug with ember-auth, as the http://ember-auth-rails-demo.herokuapp.com/ demo app triggers the same error when I try to App.reset():

EmberAuthRailsDemo.reset()
Error while loading route: TypeError {} application-32258ef55df5047107c0d305aef7594b.js:5
undefined
Uncaught TypeError: Cannot call method 'retrieve' of undefined application-32258ef55df5047107c0d305aef7594b.js:14
Ember.Object.extend.retrieveToken application-32258ef55df5047107c0d305aef7594b.js:14
Ember.Object.extend.recall application-32258ef55df5047107c0d305aef7594b.js:14
(anonymous function) application-32258ef55df5047107c0d305aef7594b.js:14
d application-32258ef55df5047107c0d305aef7594b.js:6
(anonymous function) application-32258ef55df5047107c0d305aef7594b.js:6
i.trigger application-32258ef55df5047107c0d305aef7594b.js:6
(anonymous function) application-32258ef55df5047107c0d305aef7594b.js:6
r.flush application-32258ef55df5047107c0d305aef7594b.js:6
r.end application-32258ef55df5047107c0d305aef7594b.js:6
r.run application-32258ef55df5047107c0d305aef7594b.js:6
Ember.run application-32258ef55df5047107c0d305aef7594b.js:6
Ember.run.join application-32258ef55df5047107c0d305aef7594b.js:6
Ember.Application.Ember.Namespace.extend.reset application-32258ef55df5047107c0d305aef7594b.js:11
(anonymous function) VM71:2
InjectedScript._evaluateOn VM48:581
InjectedScript._evaluateAndWrap VM48:540
InjectedScript.evaluate VM48:459
conrad-vanl commented 10 years ago

Ran the ember-auth-rails-demo locally, here's a relevant trace with line numbers for ember-auth-rememerable. If I completely remove ember-auth-rememerable from source, I don't get an error on reset() any more. If you'd rather re-open this as an issue on that repo, I can do that.

Uncaught TypeError: Cannot call method 'retrieve' of null ember-auth-module-rememberable.js?body=1:68
Ember.Object.extend.retrieveToken ember-auth-module-rememberable.js?body=1:68
Ember.Object.extend.recall ember-auth-module-rememberable.js?body=1:41
(anonymous function) ember-auth-module-rememberable.js?body=1:84
invokeCallback ember.js?body=1:8055
(anonymous function) ember.js?body=1:8105
EventTarget.trigger ember.js?body=1:7878
(anonymous function) ember.js?body=1:8172
DeferredActionQueues.flush ember.js?body=1:5459
Backburner.end ember.js?body=1:5545
Backburner.run ember.js?body=1:5584
Ember.run ember.js?body=1:5915
Ember.run.join ember.js?body=1:5961
Ember.Application.Ember.Namespace.extend.reset ember.js?body=1:34882
(anonymous function) VM125:2
InjectedScript._evaluateOn VM97:581
InjectedScript._evaluateAndWrap VM97:540
InjectedScript.evaluate
conrad-vanl commented 10 years ago

I seemed to have so far fixed this error with the following patches (note this is coffeescript not emberscript):

# in an initializer:
app.inject 'adapter', 'auth', 'auth:main'

# in auth.coffee:
Em.Auth.RememberableAuthModule.reopen
  patch: ->
    self = this
    Em.Route.reopen
      beforeModel: ->
        self.auth._ensurePromise(@_super.apply this, arguments).then =>
          return unless self.config.autoRecall && !self.auth.signedIn
          @auth.get("module.rememberable").recall()

Em.Auth.EmberDataAuthModule.reopen
  patch: ->
    DS.RESTAdapter.reopen
      ajax: (url, type, settings) ->
        @_super url, type, @auth.get("_strategy").serialize(settings || {})

For whatever reason self within these methods gets null properties...I understand this "solution" is somewhat hacky but just wanted to document it here so that others can see what works and hopefully figure out a better fix.

skoryky commented 10 years ago

I ran into the same issue. I think the root cause is that the ember-auth initializers are re-run each time App.reset() is called, which causes beforeModel() to get patched repeatedly. The original patches are associated with auth objects that have been cleared out due to App.reset() (I'm not totally clear on the details here as I haven't dug much into the Ember code itself). If accessing auth from the app container directly, things work as expected. I actually ran into the same problem in the authRedirectable module (are you using that as well?).

No real idea on how to fix it properly. Would it be to remove the ember-auth initializers somehow (more likely the beforeModel patch needs to be undone)? I don't have a good understanding of Ember initializers in general. I wasn't sure how to implement the fix from @conrad-vanl above. How do you access this.auth inside the patched functions? Isn't this referring to the window? For now I'm just re-opening my App.Auth and setting autoRecall: false :sweat:

conrad-vanl commented 10 years ago

@skoryky, this would be referring to whatever class is being reopen'ed, I believe (atleast it seems to work that way for me). So for the Em.Auth.RememberableAuthModule patch, this refers to the route in use (Em.Route.reopen), which already has auth injected. For the second one, you have to inject auth into the adapter, thus the app.inject 'adapter', 'auth', 'auth:main' bit at the beginning that you have to put in an initializer some where that runs before ember-auth-load, like this:

Em.onLoad 'Ember.Application', (application) ->
  application.initializer
    name: "adapter-auth-injection"
    before: "ember-auth-load"

    initialize: (container, app) ->
      app.inject 'adapter', 'auth', 'auth:main'

Hope that helps. FWIW, I don't think it matters that beforeModel() is getting patched repeatedly, since it's just overriding the beforeModel, not extending it (there's no this._super() call anywhere). I'm really not sure what's going on yet.

skoryky commented 10 years ago

@conrad-vanl, what you say makes sense, but this is Window for me! :confused:

There is actually a this._super.apply() call on the first line of the patched beforeModel, no?