Ember Simple Auth transition after login - javascript

I have login code on my application route, as per examples in the docs, but the call to authenticate does not seem to return a promise. The response I get in 'then' is undefined. Therefore the transition does not work. I have to manually refresh the page, and then the top redirect is called.
import Ember from 'ember';
// Make 'session' available throughout the application
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
redirect: function () {
this.transitionTo('orders');
},
actions: {
authenticate: function () {
var data = {
identification: this.controller.get('identification'),
password: this.controller.get('password')
};
this.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', data).then(
function(response) {
console.log(response); // undefined
this.transitionTo('orders'); // can't call on undefined
}
);
},
}
});

My issue was 'this' inside the function call was the wrong object. Solved by using var _this = this;
I'll post the full working code.;
import Ember from 'ember';
// Make 'session' available throughout the application
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
export default Ember.Route.extend(ApplicationRouteMixin, {
redirect: function () {
this.transitionTo('orders');
},
actions: {
authenticate: function () {
var data = {
identification: this.controller.get('identification'),
password: this.controller.get('password')
};
var _this = this;
this.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', data).then(
function(response) {
console.log(_this.get('session')); // this correctly gets the session
_this.transitionTo('orders');
}
);
},
}
});

The promise returned by the session's authenticate method doesn't resolve with a value. You can access data that the authenticator resolves with via the session's secure property, e.g. this.get('session.secure.token)'.

Related

React/reflux how to do proper async calls

I recently started to learn ReactJS, but I'm getting confused for async calls.
Lets say I have a Login page with user/pass fields and login button. Component looks like:
var Login = React.createClass({
getInitialState: function() {
return {
isLoggedIn: AuthStore.isLoggedIn()
};
},
onLoginChange: function(loginState) {
this.setState({
isLoggedIn: loginState
});
},
componentWillMount: function() {
this.subscribe = AuthStore.listen(this.onLoginChange);
},
componentWillUnmount: function() {
this.subscribe();
},
login: function(event) {
event.preventDefault();
var username = React.findDOMNode(this.refs.email).value;
var password = React.findDOMNode(this.refs.password).value;
AuthService.login(username, password).error(function(error) {
console.log(error);
});
},
render: function() {
return (
<form role="form">
<input type="text" ref="email" className="form-control" id="username" placeholder="Username" />
<input type="password" className="form-control" id="password" ref="password" placeholder="Password" />
<button type="submit" className="btn btn-default" onClick={this.login}>Submit</button>
</form>
);
}
});
AuthService looks like:
module.exports = {
login: function(email, password) {
return JQuery.post('/api/auth/local/', {
email: email,
password: password
}).success(this.sync.bind(this));
},
sync: function(obj) {
this.syncUser(obj.token);
},
syncUser: function(jwt) {
return JQuery.ajax({
url: '/api/users/me',
type: "GET",
headers: {
Authorization: 'Bearer ' + jwt
},
dataType: "json"
}).success(function(data) {
AuthActions.syncUserData(data, jwt);
});
}
};
Actions:
var AuthActions = Reflux.createActions([
'loginSuccess',
'logoutSuccess',
'syncUserData'
]);
module.exports = AuthActions;
And store:
var AuthStore = Reflux.createStore({
listenables: [AuthActions],
init: function() {
this.user = null;
this.jwt = null;
},
onSyncUserData: function(user, jwt) {
console.log(user, jwt);
this.user = user;
this.jwt = jwt;
localStorage.setItem(TOKEN_KEY, jwt);
this.trigger(user);
},
isLoggedIn: function() {
return !!this.user;
},
getUser: function() {
return this.user;
},
getToken: function() {
return this.jwt;
}
});
So when I click the login button the flow is the following:
Component -> AuthService -> AuthActions -> AuthStore
I'm directly calling AuthService with AuthService.login.
My question is I'm I doing it right?
Should I use action preEmit and do:
var ProductAPI = require('./ProductAPI')
var ProductActions = Reflux.createActions({
'load',
'loadComplete',
'loadError'
})
ProductActions.load.preEmit = function () {
ProductAPI.load()
.then(ProductActions.loadComplete)
.catch(ProductActions.loadError)
}
The problem is the preEmit is that it makes the callback to component more complex. I would like to learn the right way and find where to place the backend calls with ReactJS/Reflux stack.
I am using Reflux as well and I use a different approach for async calls.
In vanilla Flux, the async calls are put in the actions.
But in Reflux, the async code works best in stores (at least in my humble opinion):
So, in your case in particular, I would create an Action called 'login' which will be triggered by the component and handled by a store which will start the login process. Once the handshaking ends, the store will set a new state in the component that lets it know the user is logged in. In the meantime (while this.state.currentUser == null, for example) the component may display a loading indicator.
For Reflux you should really take a look at https://github.com/spoike/refluxjs#asynchronous-actions.
The short version of what is described over there is:
Do not use the PreEmit hook
Do use asynchronous actions
var MyActions = Reflux.createActions({
"doThis" : { asyncResult: true },
"doThat" : { asyncResult: true }
});
This will not only create the 'makeRequest' action, but also the 'doThis.completed', 'doThat.completed', 'doThis.failed' and 'doThat.failed' actions.
(Optionally, but preferred) use promises to call the actions
MyActions.doThis.triggerPromise(myParam)
.then(function() {
// do something
...
// call the 'completed' child
MyActions.doThis.completed()
}.bind(this))
.catch(function(error) {
// call failed action child
MyActions.doThis.failed(error);
});
We recently rewrote all our actions and 'preEmit' hooks to this pattern and do like the results and resulting code.
I also found async with reflux kinda confusing. With raw flux from facebook, i would do something like this:
var ItemActions = {
createItem: function (data) {
$.post("/projects/" + data.project_id + "/items.json", { item: { title: data.title, project_id: data.project_id } }).done(function (itemResData) {
AppDispatcher.handleViewAction({
actionType: ItemConstants.ITEM_CREATE,
item: itemResData
});
}).fail(function (jqXHR) {
AppDispatcher.handleViewAction({
actionType: ItemConstants.ITEM_CREATE_FAIL,
errors: jqXHR.responseJSON.errors
});
});
}
};
So the action does the ajax request, and invokes the dispatcher when done. I wasn't big on the preEmit pattern either, so i would just use the handler on the store instead:
var Actions = Reflux.createActions([
"fetchData"
]);
var Store = Reflux.createStore({
listenables: [Actions],
init() {
this.listenTo(Actions.fetchData, this.fetchData);
},
fetchData() {
$.get("http://api.com/thedata.json")
.done((data) => {
// do stuff
});
}
});
I'm not big on doing it from the store, but given how reflux abstracts the actions away, and will consistently fire the listenTo callback, i'm fine with it. A bit easier to reason how i also set call back data into the store. Still keeps it unidirectional.

Should I and how to transitionToRoute in a promise callback

In an Ember controller, I want to call my API to create a user and on success, transitionToRoute. This is what I currently want to work:
import ajax from "ic-ajax";
import Ember from "ember";
export default Ember.Controller.extend({
actions: {
createAndLoginUser: function() {
var user = { "user": this.getProperties("email", "name") };
ajax({ url: "api/users", type: "POST", data: user })
.then(transitionToHome);
}
}
});
var transitionToHome = function() {
this.transitionToRoute("home")
}
But when I place a debugger in the method, this is no longer the controller object and is out of scope to call transitionToRoute.
I've only ever written hacky javascript, but I'm trying to learn core concepts and some frameworks. Is this the right way to use a promise? And is this the right place in Ember to put a transition?
Your problem has nothing to do with Ember or transitions; it's all about this handling. The simplest solution is just
.then(transitionToHome.bind(this));
Putting transition method inside the controller
You could also consider putting transitionToHome inside the controller as a method. Then, you could call it as in the following:
export default Ember.Controller.extend({
transitionToHome: function() { this.transitionToRoute("home"); },
actions: {
createAndLoginUser: function() {
var self = this;
var user = { "user": this.getProperties("email", "name") };
ajax({ url: "api/users", type: "POST", data: user })
.then(function() { self.transitionToHome(); });
}
}
});
This might be a simpler approach which is more readable and doesn't require reasoning about this and using bind (but does require using self).
Moving the transition to the route?
Outside the scope of your question, but some would say that route-related logic (including transitioning) logically belongs in the route, not the controller. If you agree, you could refactor the above to do a send
createAndLoginUser: function() {
var user = { "user": this.getProperties("email", "name") };
var self = this;
ajax({ url: "api/users", type: "POST", data: user })
.then(function() { self.send("goHome"); });
}
}
and then implement the goHome action in your route. Or, you could implement goHome in a higher-level route, even the application route, thus making it available from any controller or lower-level route.
Moving the AJAX logic to a service?
Others might say that the ajax logic does not really belong here in the controller, and should rightfully be in a services layer, so
// services/user.js
export function createUser(data) {
return ajax({ url: "api/users", type: "POST", data: { user: data } });
}
// controller
import { createUser } from 'app/services/user';
export default Ember.Controller.extend({
createAndLoginUser: function() {
var data = this.getProperties("email", "name"));
var self = this;
createUser(data) . then(function() { self.send("goHome"); });
}
};
Using ES6 syntax, we can avoid the self, simplifying a bit in the process:
createAndLoginUser: function() {
var data = this.getProperties("email", "name"));
var goHome = () => this.send("goHome");
createUser(data) . then(goHome);
}
Now this code is starting to look more semantic and readable.
An alternative solution is to simply use ES6 arrow syntax to maintain the context of this:
import ajax from "ic-ajax";
import Ember from "ember";
export default Ember.Controller.extend({
actions: {
createAndLoginUser() {
let user = { "user": this.getProperties("email", "name") };
ajax({ url: "api/users", type: "POST", data: user })
.then(() => {
this.transitionToRoute("home");
});
}
}
});

Error Nothing handled the action 'sessionInvalidationSucceeded' using Simple Auth Ember JS

So I have been setting up a auth manager through my ember for the past week a and finally got it working. However, I'm still getting a error when invalidating the user.
Nothing handled the action 'sessionInvalidationSucceeded'
Can't figure out what the best way to handle the error?
import Ember from 'ember';
import DS from 'ember-data';
export default Ember.Object.extend({
authenticate: function(controller, user) {
var app = this.container.lookup('controller:application');
var session = app.get('session').authenticate('simple-auth-authenticator:oauth2-password-grant', user);
session.then(function() {
console.log('Session Started');
controller.transitionToRoute('brands');
});
},
endSession: function() {
var app = this.container.lookup('controller:application');
var session = app.get('session').invalidate();
session.then(function() {
app.store = DS.Store.create();
console.log('Session Ended');
app.transitionToRoute('index');
app.store.destroy();
});
}
});
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
sessionEnded: function() {
this.authManagerService.endSession();
}
},
currentUser: function() {
return this.store.find('user', this.session.get('user_id');
}.property('#each.user')
});
You need to include the Simple Auth Route mixin on the route you are authenticating
import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
or handle the action in your initializer
Ember.Application.initializer({
name: 'authentication',
after: 'simple-auth',
initialize: function(container, application) {
var applicationRoute = container.lookup('route:application');
var session = container.lookup('simple-auth-session:main');
// handle the session events
session.on('sessionInvalidationSucceeded', function() {
applicationRoute.transitionTo('index');
});
}
});
Take a look at the api, it's really helpful
http://ember-simple-auth.com/ember-simple-auth-api-docs.html#SimpleAuth-ApplicationRouteMixin-sessionInvalidationSucceeded

Emberjs authentication session not working

I have followed Authentication Tutorial, but running into some issues.
I have a php backend api which resides in another domain, http://rest.api {local development}
The ember js application uses ember-app-kit and connects to the rest api.
When the user submits the login form it sends the username/email with password to one of the route defined in the rest api Session Controller
import AuthManager from 'lms/config/auth_manager';
var SessionNewController = Ember.ObjectController.extend({
attemptedTransition : null,
loginText : 'Log In',
actions: {
loginUser : function() {
var self = this;
var router = this.get('target');
var data = this.getProperties('identity', 'password');
var attemptedTrans = this.get('attemptedTransition');
$.post('http://rest.api/login',
data,
function(results) {
console.log(results.session);
console.log(results.user_id);
AuthManager.authenticate(results.session, results.user_id);
if(attemptedTrans) {
attemptedTrans.retry();
self.set('attemptedTransition', null);
} else {
router.transitionTo('index');
}
}
)
}
}
});
export default SessionNewController;
After receiving the api result in the results variable which looks like this :
Object {success: "user login success", session: "2OmwKLPclC.YhYAT3745467my7t0m2uo", user_id: "1"}
But as soon as I capture the data and send it to the AuthManager which resides in Auth Manager Code
import User from 'lms/models/user';
import Application from 'lms/adapters/application';
var AuthManager = Ember.Object.extend({
init: function() {
this._super();
var accessToken = $.cookie('access_token');
var authUserId = $.cookie('auth_user');
if(!Ember.isEmpty(accessToken) || !Ember.isEmpty(authUserId)) {
this.authenticate(accessToken, authUserId);
}
},
isAuthenticated: function() {
return !Ember.isEmpty(this.get('ApiKey.accessToken')) && !Ember.isEmpty(this.get('ApiKey.user'));
},
authenticate: function(accessToken, userId) {
$.ajaxSetup({
headers: { 'Authorization': 'Bearer ' + accessToken }
});
var user = User.store.find(userId);
console.log(user);
this.set('ApiKey', ApiKey.create({
accessToken: accessToken,
user: user
}));
},
reset: function() {
this.set('ApiKey', null);
$.ajaxSetup({
headers: { 'Authorization': 'Bearer None' }
});
},
apiKeyObserver: function() {
Application.accessToken = this.get('apikey.accessToken');
if (Ember.isEmpty(this.get('ApiKey'))) {
$.removeCookie('access_token');
$.removeCookie('auth_user');
} else {
$.cookie('access_token', this.get('ApiKey.accessToken'));
$.cookie('auth_user', this.get('ApiKey.user.id'));
}
}.observes('ApiKey')
});
export default AuthManager;
I got an error in the console saying
Uncaught TypeError: Object function () {
if (!wasApplied) {
Class.proto(); // prepare prototype...
}
o_defineProperty(this, GUID_KEY, undefinedDescriptor);
o_defineProperty(this, '_super', undefinedDescriptor);
var m = met...<omitted>...e' new.js:23
(anonymous function) new.js:23
jQuery.Callbacks.fire jquery.js:1037
jQuery.Callbacks.self.fireWith jquery.js:1148
done jquery.js:8074
jQuery.ajaxTransport.send.callback jquery.js:8598
It is not able to pass the variables to the imported function.
Finally got this working. The error that was I doing is after extending the Ember.Object.extend() on auth_manager.js, I didn't create the object anywhere. Thats why it couldnt set create a cookie and throwing that error message.
All I had to do was, .create() after extending the object.
Don't know whether it is the right method or not. But it certainly works.

ember.js Uncaught TypeError: Object data-size has no method 'transitionTo'

I am very new to ember and trying to implement authentication via facebook
I am using ember-facebook.js library to connect with facebook. Once the authentication is successful, I want to transition to some other route e.g. '/index'. This library creates a App.FBUser object in mixin which is populated from the facebook response. The blog say following:
Whenever the user changes (login, logout, app authorization, etc) the method updateFBUser is called, updating the App.FBUser object on your application. You can do whatever you want with this binding, observe it, put it in the DOM, whatever.
Ember.Facebook = Ember.Mixin.create({
FBUser: void 0,
appId: void 0,
fetchPicture: true,
init: function() {
this._super();
return window.FBApp = this;
},
appIdChanged: (function() {
var _this = this;
this.removeObserver('appId');
window.fbAsyncInit = function() {
return _this.fbAsyncInit();
};
return $(function() {
var js;
js = document.createElement('script');
$(js).attr({
id: 'facebook-jssdk',
async: true,
src: "//connect.facebook.net/en_US/all.js"
});
return $('head').append(js);
});
}).observes('appId'),
fbAsyncInit: function() {
var _this = this;
FB.init({
appId: this.get('appId'),
status: true,
cookie: true,
xfbml: true
});
this.set('FBloading', true);
FB.Event.subscribe('auth.authResponseChange', function(response) {
return _this.updateFBUser(response);
});
return FB.getLoginStatus(function(response) {
return _this.updateFBUser(response);
});
},
updateFBUser: function(response) {
console.log("Facebook.updateFBUser: Start");
var _this = this;
if (response.status === 'connected') {
//console.log(_this);
return FB.api('/me', function(user) {
var FBUser;
FBUser = user;
FBUser.accessToken = response.authResponse.accessToken;
if (_this.get('fetchPicture')) {
return FB.api('/me/picture', function(path) {
FBUser.picture = path;
_this.set('FBUser', FBUser);
return _this.set('FBloading', false);
});
} else {
_this.set('FBUser', FBUser);
return _this.set('FBloading', false);
}
});
} else {
this.set('FBUser', false);
return this.set('FBloading', false);
}
}//updateFBUser
});
Update :
Adding following observer in my LoginController, I am able to capture the App.FBUser update event(it is update after getting response from FB; as indicated by the blog).
From this observer method, when I try to 'transitionTo' my index route I get following error
Uncaught TypeError: Object data-size has no method 'transitionTo'. Following is the code
App.LoginController = Ember.Controller.extend({
onSuccess: (function(){
var self = this;
/*
//tried all these method to redirect but error is the same
var attemptedTransition = this.get('attemptedTransition');
attemptedTransition.retry();
*/
/*
//tried all these method to redirect but error is the same
var router = this.get('target.router');
router.transitionTo('index');
*/
//tried all these method to redirect but error is the same
this.transitionToRoute('index');
}).observes('App.FBUser')
});
Index Route
App.AuthenticatedRoute = Ember.Route.extend({
beforeModel: function(transition){
var self = this;
if(!App.FBUser){
self.redirectToLogin(transition);
}
},
redirectToLogin: function(transition){
var loginController = this.controllerFor('login');
loginController.set('attemptedTransition', transition);
this.transitionTo('login');
}
});
I am not able to get my head around it.
Any help is greatly appreciated. Thanks
How can I access this object in my Route.beforeModel() hook.
Depending on what route's beforModel hook you are talking about, this is how you could do it:
App.SomeRoute = Ember.Route.extend({
beforeModel: function(transition) {
if (!Ember.isNone(App.FBUser)) {
// calling 'transitionTo' aborts the transition, redirects to 'index'
this.transitionTo('index');
}
}
});
Update in response to your last comment
The addon you are using is slightly outdated and the proposed implementation method for the mixin in your application will not work with the current version of ember:
App = Ember.Application.create(Ember.Facebook)
App.set('appId', 'yourfacebookappid');
starting from version 1.0.0-rc3 of ember you should rather do it like this:
App = Ember.Application.creatWithMixins(Ember.Facebook);
App.set('appId', 'yourfacebookappid');
After that you should be able to have access to the App.FBUser object as mentioned above.
Update 2
If you want to be able to be notified when some events happend, like login, logout etc. you should (as the Author of the addon states on it's blog post) override the updateFBUser method and do in there your transitions.
Since the addon is trough the mixin available in our App namespace you should be able to do the following:
App = Ember.Application.creatWithMixins(Ember.Facebook, {
updateFBUser: function() {
this._super();
// we are calling super to let the addon
// do it's work but at the same time we get
// notified that something happened, so do at this
// point your transition
}
});
Hope it helps.
As per Issue 1 adding
attributeBindings: [],
to:
return Ember.FacebookView = Ember.View.extend({
solved the issue.

Categories

Resources