How should I store current user details in EmberJS? - javascript

I have an EmberJS application generated using ember-cli. I'm currently using simple-auth with a custom authenticator.
In the authenticator, when the user logs in I want to save his details so that I can use it later.
I have the following code:
authenticate: function(options) {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject){
API.user.login(options.username, options.password, true).done(function(data) {
// #TODO: Save current user
resolve(data.id);
}).fail(function() {
reject();
});
});
},
User data is available in the variable data.user.
I tried using Ember.set('App.currentUser', data.user); but it's not working. What should I do?

I think it works easiest to use an initializer. Theres several ways you can resolve the user, I think it is easiest if you pass the user_email alongside the grant token from the API
//initializers/session-user.js
import Ember from "ember";
import Session from "simple-auth/session";
export function initialize(container) {
Session.reopen({
setCurrentUser: function() {
var accessToken = this.get('access_token');
var self = this;
if (!Ember.isEmpty(accessToken)) {
return container.lookup('store:main').find('user', {
email: self.get('user_email')
}).then(function (users){
self.set('currentUser', users.get('firstObject'));
});
}
}.observes('access_token')
});
}
export default {
name: 'session-user',
before: 'simple-auth',
initialize: initialize
};
Check this thread for where the idea of this came from: http://discuss.emberjs.com/t/best-practice-for-loading-and-persisting-current-user-in-an-authenticated-system/6987
And if you are using simple-auth > 0.8.0-beta.1 you will need to adjust the initializer

I ended up creating a custom Sessions controller and setting the current user object there, and then creating an alias from the application controller.
Something like what's in this article.

Related

How to create custom Registration and Login API using Strapi?

I am using strapi to create APIs.
I want to implement my own Registration API and Login API.
I checked the documentation of strapi but i am not finding any custom API for this.
can any one help me on this?
Same answer, but in more detail:
Strapi creates an Auth controller automatically for you and you can overwrite its behavior.
Copy the function(s) you need (e.g. register) from this file:
node_modules/strapi-plugin-users-permissions/controllers/Auth.js
to:
your_project_root/extensions/users-permissions/controllers/Auth.js
Now you can overwrite the behavior, e.g. pass a custom field inside the registration process {"myCustomField": "hello world"} and log it to the console:
async register(ctx) {
...
...
// log the custom field
console.log(params.myCustomField)
// do something with it, e.g. check whether the value already exists
// in another content type
const itExists = await strapi.query('some-content-type').findOne({
fieldName: params.myCustomField
});
if (!itExists) {
return ctx.badRequest(...)
} else {
console.log('check success')
}
}
Actually, strapi creates an Auth controller to handle these requests. You can just change them to fit in your need.
The path to the controller is:
plugins/users-permissions/controllers/Auth.js
in order to create custom users-permissons apis on server side you have to create
src/extensions/users-permissions/strapi-server.js
and in that file can write or override existing user-permissions plugin apis
here is the example for users/me
const _ = require('lodash');
module.exports = (plugin) => {
const getController = name => {
return strapi.plugins['users-permissions'].controller(name);
};
// Create the new controller
plugin.controllers.user.me = async (ctx) => {
const user = ctx.state.user;
// User has to be logged in to update themselves
if (!user) {
return ctx.unauthorized();
}
console.log('calling about meeeeeeeeeee------')
return;
};
// Add the custom route
plugin.routes['content-api'].routes.unshift({
method: 'GET',
path: '/users/me',
handler: 'user.me',
config: {
prefix: '',
}
});
return plugin;
};

Getting Facebook Avatar in Meteor when Autopublish is removed

Currently when auto publish is removed, only {{currentUser.profile.name}} works.I'm trying to get {{currentUser.profile.first_name}} and the avatar from Facebook but have not been able to do so. Here is my code...
On the Server side:
Meteor.publish('userData', function() {
if(!this.userId) return null;
return Meteor.users.find(this.userId, {fields: {
'services.facebook': 1
}});
});
On Iron Router:
Router.configure({
waitOn: function() {
return Meteor.subscribe('userData');
}
});
From my understanding, I see that Meteor is publishing all the userData and then subscribing to it via Iron Router. What I don't understand is why this is not working -- as I think {{currentUser.profile.first_name}} should work but isn't.
Like Richard suggests, when a user is created, you can copy the services document to the profile doc.
Accounts.onCreateUser(function(options, user) {
// We still want the default hook's 'profile' behavior.
if (options.profile) {
user.profile = options.profile;
user.profile.memberSince = new Date();
// Copy data from Facebook to user object
user.profile.facebookId = user.services.facebook.id;
user.profile.firstName = user.services.facebook.first_name;
user.profile.email = user.services.facebook.email;
user.profile.link = user.services.facebook.link;
}
return user;
});
Your publication to get their first name and Facebook ID would look like this...
/* ============== Single User Data =============== */
Meteor.publish('singleUser', function(id) {
check(id, String);
return Meteor.users.find(id,
{fields: {'profile.facebookId': 1, 'profile.name': 1, 'profile.firstName': 1, 'profile.link': 1}});
});
You can access a user's Facebook avatar with a template helper function...
Template.profileView.helpers({
userPicHelper: function() {
if (this.profile) {
var id = this.profile.facebookId;
var img = 'http://graph.facebook.com/' + id + '/picture?type=square&height=160&width=160';
return img;
}
}
});
In your template, you can then use the following helper (provided you are wrapping this in a block that contains user data):
<img src="{{userPicHelper}}" alt="" />
I believe you're trying to access the first_name field from the services subdocument. It should be {{currentUser.services.facebook.first_name}}
If you want to transfer first_name to the profile subdocument, you can have the following event handler:
Accounts.onCreateUser(function(options, user) {
// ... some checks here to detect Facebook login
user.profile.firstName = user.services.facebook.first_name;
user.profile.lastName = user.services.facebook.last_name;
});

Meteor Routing, Pub/Sub

I'm trying to make a publishment statement to publish
ONLY the author(OP)'s profile avatar. I am thinking of grabbing the _id of the page. And from that page, I will grab the userId which is the author's _id and try to show the profile.
However, I have been very unsuccessful, and currently, I am using the following. Publishing EVERY user's profile avatar.
Publications.js
//Need to filter this to show only OP.
Meteor.publish("userPostAvatar", function() {
return Meteor.users.find( {} ,
{
fields: {'profile.avatar': 1}
})
});
Meteor.publish('singlePost', function(id) {
check(id, String);
return Posts.find(id);
});
Router.js
Router.route('/posts/:_id', {
name: 'postPage',
waitOn: function() {
return [
Meteor.subscribe('singlePost', this.params._id),
Meteor.subscribe('userStatus'),
Meteor.subscribe('userPostAvatar')
];
},
data: function() {
return Posts.findOne({_id:this.params._id});
}
});
You can do a simple join in the userPostAvatar publish function like this:
Meteor.publish('userPostAvatar', function(postId) {
check(postId, String);
var post = Posts.findOne(postId);
return Meteor.users.find(post.authorId, {fields: {profile: 1}});
});
This assumes posts have an authorId field - adjust as needed for your use case. Note three important things:
You will need to subscribe with this.params._id just as you did for singlePost.
The join is non-reactive. If the author changes, the avatar will not be republished. Given the general nature of posts I assume this isn't a problem.
I didn't publish the nested field profile.avatar on purpose because doing so can cause weird behavior on the client. See this question for more details.
I believe you can achieve this within the iron:router data context, by finding the post, associated author (whatever the field is), and then the subsequent user avatar. You can return an object to the iron:router data context. Then you can access post and avatar in the template as variables (so you might need to adjust the template output a little).
Publications.js
Meteor.publish("userPostAvatar", function() {
return Meteor.users.findOne( {} ,
{
fields: {'profile.avatar': 1}
})
});
Meteor.publish('singlePost', function(id) {
check(id, String);
return Posts.find(id);
});
Router.js
Router.route('/posts/:_id', {
name: 'postPage',
waitOn: function() {
return [
Meteor.subscribe('singlePost', this.params._id),
Meteor.subscribe('userStatus'),
Meteor.subscribe('userPostAvatar')
];
},
data: function() {
var post = Posts.findOne({_id: this.params._id});
var avatar = Users.findOne(post.authorId).profile.avatar;
return {
post: post,
avatar: avatar
};
}
});
Two problems with this method are that you could achieve the same thing with template helpers, and the user publication hasn't been limited to one user (I'm unsure how to do this unless we know the authorId within the waitOn, although maybe you could try moving the logic to there instead of the data context as my example shows).

Ember CLI + Ember Data + Simple Auth: authorize gets not called

i am using Ember CLI + Ember Data + Simple Auth. The authenticator is working fine. But when im am doing a Rest Call with Ember Data Rest Adapter this.store.findAll("user"); the authorize function in my custom authorizer don't gets called.
The Rest API Endpoint is on an other domain, so i added the url to the crossOriginWhitelist in my environment.js.
environment.js:
module.exports = function(environment) {
var ENV = {
// some configuration
};
ENV['simple-auth'] = {
crossOriginWhitelist: ['http://api.xxxx.com'],
authorizer: 'authorizer:xxxx',
routeAfterAuthentication: 'dashboard',
};
return ENV;
};
authorizer
import Ember from 'ember';
import Base from 'simple-auth/authorizers/base';
var XXXXAuthorizer = Base.extend({
authorize: function(jqXHR, requestOptions) {
// Some Code, gets not called, damn it :(
}
});
export default {
name: 'authorization',
before: 'simple-auth',
initialize: function(container) {
container.register('authorizer:xxxx', XXXXAuthorizer);
}
};
index.html
....
<script>
window.XXXXWebclientENV = {{ENV}};
window.ENV = window.MyAppENV;
window.EmberENV = window.XXXXWebclientENV.EmberENV;
</script>
<script>
window.XXXXWebclient = require('xxxx-webclient/app')['default'].create(XXXXWebclientENV.APP);
</script>
....
Thanks for help :)
I had a similar problem. For me it was the crossOriginWhitelist config.
I set it like this:
// config/environment.js
ENV['simple-auth'] = {
crossOriginWhitelist: ['*'] // <-- Make sure it's an array, not a string
};
to see if I could get it working (I could), then I could narrow it down to figure out exactly what URL I should use to enforce the restriction (port number and hostname etc).
Don't leave it like that though!
You should actually figure out what URL works for the whitelist, and use that.
I am facing the same issue. I have same setup but the authorize function is not being called. May be you can try by adding the port number in your crossOriginWhiteList url.
I am adding window.ENV = window.MyAppENV line in new initializer which runs before simple-auth. You have added that in index file and may be that is the reason why simple-auth is not able to read your configuration.
Does the other configuration routeAfterAuthentication: 'dashboard', works properly? If not then this might be the reason. Try adding new initializer like
export default {
name: 'simple-auth-config',
before: 'simple-auth',
initialize: function() {
window.ENV = window.MyAppNameENV;
}
};

Ember: store not available in Application.initializer

I have implemented an authentication based on this article
I thus create an App.Session object at application initialization:
Ember.Application.initializer({
name: 'session',
initialize: function (container, application) {
App.Session = Ember.Object.extend({
init: function () {
this._super();
this.set('authToken', $.cookie('auth_token'));
this.set('authAccountId', $.cookie('auth_accountId'));
this.set('authAccountLanguage', $.cookie('auth_accountLanguage'));
},
authAccountIdChanged: function () {
var authAccountId = this.get('authAccountId');
$.cookie('auth_accountId', authAccountId);
//Load the actual account record from the server if the authAccountId is set
//Used to have for example the full name or other properties of the account
if (!Ember.isEmpty(authAccountId)) {
this.set('authAccount', this.store.find('account', authAccountId));
}
}.observes('authAccountId'),
...
I have an observer on authAccountId; thus each time the accountId (the id of the logged in user) is changed, I want to retrieve all details of that user (full name, preferences, etc.).
Before Ember data version 1.0.0, I was using:
this.set('authAccount', App.Account.find(authAccountId));
And this worked. Now I use: this.set('authAccount', this.store.find('account', authAccountId));
And I receive the error: Uncaught TypeError: Cannot call method 'find' of undefined. Also in debugger, this.get('store') results in undefined. I have the impression that the store is not available in Application.initializer. Can somebody help resolving this issue ?
You can use container.lookup('store:main') this will return the store registered in container:
var store = container.lookup('store:main');
this.set('authAccount', store.find('account', authAccountId));

Categories

Resources