It appears that a vitally important resolve() is missing from the current user documentation. It does appear in the dummy code;
Additionally it appears as if this solution cannot be used reliably due to its async nature. Obviously the beforeModel hook is not async since Ember waits for all the Promises to resolve before continuing, but the sessionAuthenticated event _is_ async, so if it takes longer for the promise to resolve than it takes to render the next route, the transition will blow up.
Specifically what I'm doing is transitioning to dashboard after authentication. In the dashboard route I have a beforeModel that checks to see if the logged in user has taken the tour yet, if they have, I show them the dashboard, if they haven't I transitionTo the tour.
Because the current user promise hasn't resolved, the service doesn't have a user and the transition blows up.
Thanks for this. I'll look into this.
@thegranddesign and @kylemellander I have the exact same problem. model() triggers before sessionAuthenticated() resolves, which causes API lookup to fail since it relies on current user data. Is there a way to get around it?
Hi, just commenting that I have the same issue (transition to an other route but not being able to render the user properties in the template because they're still unresolved).
There was an issue regarding this, see #982. It was solved there by refreshing the application route after authentication succeeded (with an instance initialiser). I'm just wondering if that's really the best option or if there is a nicer way of doing this.
I'm fairly new to Ember and still learning a lot.
@manuelsteiner and @thegranddesign here is my solution to this problem. Rather than extending sessionAuthenticated method from ApplicationRouteMixin, I took its contents and run after current user promise resolves. See below. Notice how I removed this._super(...arguments).
sessionAuthenticated() {
const attemptedTransition = get(this, 'session.attemptedTransition');
this.loadUser()
.then(() => {
if (attemptedTransition) {
attemptedTransition.retry();
set(this, 'session.attemptedTransition', null);
} else {
this.transitionTo('dashboard');
}
})
.catch(() => get(this, 'session').invalidate());
},
Does anyone have a good solution/workaround for this?
Hey guys,
(Note: I only picked up Ember two days ago... so this comment might be completely irrelevant and I'm probably doing things all wrong but I am really enjoying ember! so there's that....)
Not sure if it's the same issue but I was successfully logging in and my app was working fine but on a page refresh session.isAuthenticated would return false. Turns out it was caused by a lack of resolve on my current user service.
I added resolve() to my service like this:
import Ember from 'ember';
const { inject: { service }, isEmpty, RSVP } = Ember;
export default Ember.Service.extend({
session: service('session'),
store: service(),
load() {
return new RSVP.Promise((resolve, reject) => {
let userId = this.get('session.data.authenticated.profile.id');
if (!isEmpty(userId)) {
return this.get('store').find('user', userId).then((user) => {
this.set('user', user);
resolve(); // Resolve added here.
}, reject);
} else {
resolve();
}
});
}
});
I hope this helps anyone.
Please upgrade to the latest release and reopen this if you have any issues. Refer to "Managing a Current User" for examples on handling current user.
@KrisOlszewski good solution thank you.
You would use this.transitionTo(get(this, 'routeAfterAuthentication')) instead of this.transitionTo('dashboard'); to transit to the configured route.
I'm not a JS expert, but there is a way we can call the original function:
sessionAuthenticated() {
let parentMethod = this._super.bind(this),
originalArguments = arguments;
this._loadCurrentUser().then(() => {
parentMethod(...originalArguments);
});
}
@tothpeter I don't quite follow what you're trying to do, but declared variables are accessible inside promises http://jsbin.com/pokujuwuda/1/edit?js,console.
are you on ember community slack? I recommend signing up and checking out the #-help or #e-simple-auth channels.
I wanted to call the original method after the current user is loaded. So the app would transition only after we have the current user.
Unfortunately this is not working:
sessionAuthenticated() {
let _this = this,
originalArguments = arguments;
this._loadCurrentUser().then(() => {
_this._super(...originalArguments);
});
}
@tothpeter this is what I do...
async sessionAuthenticated() {
await this._setCurrentUser();
// carry on to wherever we were going when logging in.
this._retryTransition();
},
@marcoow this issue must be reopened.
@villander: care to share a bit more info, e.g. why this needs re-opening?
@marcoow because currentUser is not resolved after session authentication is finished
I a querying the currently logged in user as described in the docs. When the beforeModel hook is hit the promise returned by this.get('currentUser').load() is nicely resolved when the route is loaded. No need to deal with promises there (e.g. when calculating permissions).
When the user is logging in and we hit the sessionAuthenticated() path this.get('currentUser').load() is obviously only resolved after the route is completely loaded as there is no one waiting for the promise to resolve.
to make it work I had to use this workaround
sessionAuthenticated() {
this._loadCurrentUser().then(() => {
// Bluntly copied from the super class.
// We must wait for the current user before we can attempt the transition.
const attemptedTransition = this.get('session.attemptedTransition');
if (attemptedTransition) {
attemptedTransition.retry();
this.set('session.attemptedTransition', null);
} else {
this.transitionTo(this.get('routeAfterAuthentication'));
}
}).catch(() => this.get('session').invalidate());
},
So yes, when sessionAuthenticated is called, that is when you'd start loading the current user as described in the docs but it is not available immediately. I'm not sure the guide suggests that is the case though? In fact, in the guides, we check for whether the current user is present:
{{#if currentUser.user}}
If you wanted something to only be visible once the session is authenticated and the current user has loaded, you'd check for that only (and not for session.isAuthenticated).
If you think the guide is unclear, it'd be great if you could submit a PR making it clearer 馃檹
Most helpful comment
@manuelsteiner and @thegranddesign here is my solution to this problem. Rather than extending
sessionAuthenticatedmethod fromApplicationRouteMixin, I took its contents and run after current user promise resolves. See below. Notice how I removedthis._super(...arguments).