I might have this pretty close but I'm lacking the knowledge to fix this last issue.
I wanted to use a custom authentication system instead of using accounts-ui so I could track some additional details about each user.
Everything worked great until I get to the resetPassword part. If a user submits their email address in the forgotPassword form, the email is received. But when you click the reset password link in the email it does not display the resetPassword template.
This is on SO here:
Meteor account email verify fails two ways
And the iron-router github issue tracker here (which has the most fixes though is more focused on the enrollmentemail than resetPassword which I'm assuming should be very similar):
Iron-router swallows Accounts.sendEnrollmentEmail
If I understand correctly from the iron-router issue tracker above, iron-router doesn't (or didn't and maybe still doesn't) support hashbang urls like that being sent in the reset password email. A URL like:
http://localhost:3000/#/reset-password/T4rPxcVNWKwBONHSRajSk7dNZvM_YRxTLyzxZVv5SuU
Meteor was then updated so that meteor accounts-base strips out everything after the # and stores them in variables in the Accounts namespace.
While I think I understand all of that, now the question is why I can't get the suggestions in the issue tracker to work for my reset password code. I'm using everything that is in the custom auth system by Julien Le Coupanec and then I've done the following from the issue tracker:
router.js
Router.map(function() {
this.route('invList', {path: '/'});
this.route('resetPassword', {
controller: 'AccountController',
path: '/reset-password/:token',
action: 'resetPassword'
});
});
AccountController = RouteController.extend({
resetPassword: function () {
Accounts.resetPassword(this.params.token, function () {
Router.go('/reset-password');
});
}
});
overrideaccounts.js in /server
(function () {
"use strict";
Accounts.urls.resetPassword = function (token) {
return Meteor.absoluteUrl('reset-password/' + token);
};
Accounts.urls.verifyEmail = function (token) {
return Meteor.absoluteUrl('verify-email/' + token);
};
Accounts.urls.enrollAccount = function (token) {
return Meteor.absoluteUrl('enroll-account/' + token);
};
})();
I'm wondering if the issues isn't related to either bad routing on my part (likely since I don't have my head wrapped around it well yet), if I put "server code" as is listed in the issue track in the right place, or if the session related code below is what is causing the resetPassword template to not display. Or something else that I'm missing of course.
main.js
//forgotPassword helper and event handler
Template.main.helpers({
showForgotPassword: function() {
return Session.get('showForgotPassword');
},
resetPassword: function(){
return Session.get('resetPassword');
}
});
After spending many hours on what I thought would be a really simple authentication system, I'm still at a loss. Appreciate any advice!
Don't struggle with hacking the hash and iron router, just back to Meteor original design flow.
When user click the verify link in email, it lead back to "/" (home), so just did this:
Template.home.created = function() {
if (Accounts._verifyEmailToken) {
Accounts.verifyEmail(Accounts._verifyEmailToken, function(err){
if (err != null) {
// handle the error
} else {
// do what you want, maybe redirec to some route show verify successful message
}
});
}
};
I did this and verify email right, same way worked for enroll, reset password...
Related
Here's the rundown:
I'm trying to run Stripe API on my Meteor app asynchronously
Long story short, everything works (i.e. subscription and charge is/are created normally and shows up in my Stripe dashboard)
When errors occur, the errors throw normally and show on client via user friendly alerts
I have a problem when there is a success and customer subscription is created, the result is not present in client and instead always returns as an error, despite it being a successful process
Here's what my method looks like on the server:
createCustomer: function(token, email, plan){
try{
let createCustomer = Meteor.wrapAsync(stripe.customers.create);
let result = createCustomer({
source: token,
email: email,
plan: plan
});
let subscription = {
customer: result.id,
sub: result.subscriptions.data[0].id,
plan: result.subscriptions.data[0].plan.name
};
Meteor.users.update({_id: Meteor.userId()}, {$set: subscription});
} catch(error){
if(error.code === "incorrect_cvc"){
throw new Meteor.Error("incorrect_cvc", error.message);
}
// More of such errors follows
}
}
Here's what it looks like on the client:
Stripe.card.createToken({
number: number,
cvc: cvc,
exp_month: exp,
exp_year: exp_year,
address_zip: zip,
address_country: country
}, function(status, response){
if(response.error){
console.log("Make sure all fields are filled before submitting order.");
} else{
let token = response.id;
Meteor.call("createCustomer", token, email, plan, function(error, result){
if(result){
console.log("Congratulations, everything worked!");
} else{
if(error.error === "incorrect_cvc"){
console.log("oops, the CSV is incorrect");
}
// More of such errors follow..
}
})
}
});
So, everything works in terms of when there is a real error, it throws fine on server + client. When user uses card, the charges are created and subscription is always created. HOWEVER, when there is a success and everything clicking fine, I still receive an error on client via callback and the result is never true or triggered. No idea why.
Not 100% up on Meteor, but it looks to me like your createCustomer method doesn't actually return anything, so the result from your (err, result) might never have anything in it?
As was mentioned in the comments, you might want to separate out the steps and wrap each in its own try-catch set so you can better isolate the issue.
Also, I feel like you could probably generalize your server-side error code to something like:
throw new Meteor.Error(error.error, error.message);
And I might even be tempted to do something like this, at least during testing/development - that way you can actually console.log() the original error in the browser:
throw new Meteor.Error(error.error, error.message, JSON.stringify(error));
i store email using session .set('email','email name') but when i reload page that time this session email is become undefined. i use Session.get('email') to get user email.
Router.route('profile', {
path: '/profile',
data: function() {
$("body").removeClass('home');
this.render('profile');
setTimeout(function(){
$('#username').html(Session.get('first_name'));
$('#profile_username').html(Session.get('first_name'));
$('#setting_name').val(Session.get('first_name'));
$('#setting_username').val(Session.get('first_name'));
$('#setting_email').val(Session.get('email'));
$('#user_id').val(Session.get('id'));
$('.setting_day').val(Session.get('day'));
$('.setting_month').val(Session.get('month'));
$('.setting_year').val(Session.get('year'));
if(Session.get('image')!= ''){
$('.user_profile_image').attr("src",Session.get('image'));
}
if(Session.get('gender') == 0){
$('#user_gender').html('Male');
}else{
$('#user_gender').html('Female');
}
$('#day').html(Session.get('day'));
$('#month').html(Session.get('month'));
$('#year').html(Session.get('year'));
},100);
},
onBeforeAction: function () {
alert(Session.get('email'));
if(Session.get('email')){
this.next();
}else {
this.redirect('/');
}
}
});
install persistent package Session. Your session variables will persist across routes also. You need to configure it via Meteor settings. so don't forget include the settings when you run project.
u2622:persistent-session
When you reload the page page in meteor all the client side reactive things reinitialize So if you want to keep the email when page refresh then you have to send it on the server and then you can fetch this when according to your need. You can save it into a collection and then fetch from a meteor call or publish-subscribe according to your need.
When you refresh the page you are no longer in the same session so what you describe is the expected and correct default behavior. There is a package (I don't know it's name right now, but should be easy to find on atmospherejs) which gives you Session.setPersistent(...). I think this is what you are looking for.
I am trying to create a file upload feature in Meteor where a logged in user is able to upload a file to the server under a directory named after their username. I have the basics working but when I take it a step further by checking the logged in user ID, things start breaking. Specifically:
WebApp.connectHandlers.use('/upload/', function(req, res) {
if (this.userId) {
// Do cool stuff.
} else {
res.writeHead(500, {"content-type":"text/html"});
res.end("this.userId = " + this.userId); // End the response.
}
});
Result:
this.userId = undefined
And...
WebApp.connectHandlers.use('/upload/', function(req, res) {
if (Meteor.userId()) {
// Do cool stuff.
} else {
res.writeHead(500, {"content-type":"text/html"});
res.end("Meteor.userId() = " + Meteor.userId()); // End the response.
}
});
Result:
Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.
at Object.Meteor.userId (packages/accounts-base/accounts_server.js:19:1)
at Object.Package [as handle] (packages/cool_package/upload.js:34:1)
at next (/Users/me/.meteor/packages/webapp/.1.2.0.19shc3d++os+web.browser+web.cordova/npm/node_modules/connect/lib/proto.js:190:15)
at Function.app.handle (/Users/me/.meteor/packages/webapp/.1.2.0.19shc3d++os+web.browser+web.cordova/npm/node_modules/connect/lib/proto.js:198:3)
at Object.fn [as handle] (/Users/me/.meteor/packages/webapp/.1.2.0.19shc3d++os+web.browser+web.cordova/npm/node_modules/connect/lib/proto.js:74:14)
at next (/Users/me/.meteor/packages/webapp/.1.2.0.19shc3d++os+web.browser+web.cordova/npm/node_modules/connect/lib/proto.js:190:15)
at Object.WebAppInternals.staticFilesMiddleware (packages/webapp/webapp_server.js:331:1)
at packages/webapp/webapp_server.js:625:1
The code above is included in a Meteor package I'm developing. The package.js file specifies that the code should run on the server:
api.add_files("upload.js", "server");
So my questions are:
What is the correct way to check the logged in user ID and username?
Can this code be moved to an Iron Router route instead?
It looks like the line
WebApp.connectHandlers.use('/upload/', function(req, res) {
Is Express.js or similar code -- if so, you have broken out of the Meteor frameowrk providing your own REST services etc. If that is the case you also have to provide your own user management and authentication scheme for incoming REST calls, just as you would in any other bare-bones REST applications
I've a meteor app which uses Neo4j as a database with neo4jreactivity driver. Since I'm not using Mongo, Meteor.loginWithPassword(email, password, function(err) {...}) doesn't work. My question is:
How do I define custom authentication rule to login to the app?
kind of like:
customLogin(email, password, function() {...});
You can use the Accounts.registerLoginHandler method to accomplish this. This function allows developers to add custom authentication methods. Check out https://meteorhacks.com/extending-meteor-accounts.html for a great article with more details.
You likely want to continue to use loginWithPassword, and register a loginHandler similar to the one in Meteor's accounts-password package (see
Meteor's implementation ), with the call to Meteor.users.findOne(selector) replaced with a database lookup in Neo4j.
If you want to use a custom login method, your code might look something like the code from here (modified for the purposes of this question). Note that this code is not complete, nor is it a secure means of authenticating:
// client-side
// This function can be called to log in your users, and will
// trigger the following
Meteor.loginWithNeo4j = function(email, password, callback) {
//create a login request with the email and password passed in
var loginRequest = {email: email, password: password};
//send the login request
Accounts.callLoginMethod({
methodArguments: [loginRequest],
userCallback: callback
});
};
// server-side
Accounts.registerLoginHandler(function(loginRequest) {
// loginRequest is a JS object that will have properties
// "email" and "password as passed in the client code
// -- here you can write code to fetch the user's ID from the database
// take a look at https://github.com/meteor/meteor/blob/devel/packages/accounts-password/password_server.js#L61
// to see how meteor handles password checking
return {
userId: userId
}
});
The accounts package in general has a lot of dependencies on MongoDB, but you should be able to piece together various methods from the package to get auth to work.
To fetch user's object use:
Meteor.neo4j.query('MATCH (a:Player {_id: "kE3ypH4Um"}) RETURN a').get().a[0]
/* or by email */
Meteor.neo4j.query('MATCH (a:Player {email: "name#domain.com"}) RETURN a').get().a[0]
Also see updated driver API
I am using $firebaseSimpleLogin to log into Firebase using email/password.
It is working rather well when I log in using email/password, I could see sessionkey being saved automatically as a cookie.
However, would like to remember the log in such that user only have to log in once.
So I included {rememberMe: true} during auth.
How do I check if the session is still alive at the beginning of the page being loaded?
From your question, I assume you're using Angular JS.
You can execute a run block on your main module, which is run everytime the page is loaded. I don't know much about Angularfire, this is the code I'm using on a hack day project to check auth and redirect to the login page if needed.
FirebaseRef is a wrapper that points to my Firebase instance.
This also makes sure that the currentUser object is available in all scopes.
var minib = angular.module('minib', ['ngRoute', 'firebase']);
minib.run(function($rootScope, $location, $firebaseSimpleLogin, firebaseRef) {
$rootScope.auth = $firebaseSimpleLogin(firebaseRef());
$rootScope.auth.$getCurrentUser().then(function(user) {
if (user) {
$rootScope.currentUser = user;
} else {
$location.path('/login');
}
});
});