mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Forgot password flow (modulo sending email).
Also, url helpers prepared for account validation emails.
This commit is contained in:
@@ -124,4 +124,45 @@
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Sends an email to a user with a link that can be used to reset
|
||||
// their password
|
||||
//
|
||||
// @param options {Object}
|
||||
// - email: (email)
|
||||
// @param callback (optional) {Function(error|undefined)}
|
||||
Meteor.forgotPassword = function(options) {
|
||||
if (!options.email)
|
||||
throw new Error("Must pass options.email");
|
||||
options.baseUrl = window.location.protocol + "//"
|
||||
+ window.location.host + "/";
|
||||
Meteor.call("forgotPassword", options, callback);
|
||||
};
|
||||
|
||||
// Resets a password based on a token originally created by
|
||||
// Meteor.forgotPassword, and then logs in the matching user.
|
||||
//
|
||||
// @param token {String}
|
||||
// @param newPassword {String}
|
||||
// @param callback (optional) {Function(error|undefined)}
|
||||
Meteor.resetPassword = function(token, newPassword, callback) {
|
||||
if (!token)
|
||||
throw new Error("Need to pass options.token");
|
||||
if (!newPassword)
|
||||
throw new Error("Need to pass options.newPassword");
|
||||
|
||||
var verifier = Meteor._srp.generateVerifier(newPassword);
|
||||
Meteor.apply(
|
||||
"resetPassword", [token, verifier], {wait: true},
|
||||
function (error, result) {
|
||||
if (error || !result) {
|
||||
error = error || new Error("No result from call to resetPassword");
|
||||
callback && callback(error);
|
||||
}
|
||||
|
||||
Meteor.accounts.makeClientLoggedIn(result.id, result.token);
|
||||
callback && callback();
|
||||
});
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
@@ -137,6 +137,51 @@
|
||||
var loginToken = Meteor.accounts._loginTokens.insert({userId: userId});
|
||||
this.setUserId(userId);
|
||||
return {token: loginToken, id: userId};
|
||||
},
|
||||
|
||||
forgotPassword: function (options) {
|
||||
var email = options.email;
|
||||
var baseUrl = options.baseUrl;
|
||||
if (!email)
|
||||
throw new Meteor.Error(400, "Need to set options.email");
|
||||
if (!baseUrl)
|
||||
throw new Meteor.Error(400, "Need to set options.baseUrl");
|
||||
|
||||
var user = Meteor.users.findOne({emails: email});
|
||||
if (!user)
|
||||
throw new Meteor.Error(403, "User not found");
|
||||
|
||||
var token = Meteor.uuid();
|
||||
var creationTime = +(new Date);
|
||||
Meteor.users.update(user._id, {$set: {
|
||||
"services.password.reset": {
|
||||
token: token,
|
||||
creationTime: creationTime
|
||||
}
|
||||
}});
|
||||
|
||||
// XXX definitely *not* the final form!
|
||||
Meteor.mail.send(email, Meteor.accounts.urls.resetPassword(baseUrl, token));
|
||||
},
|
||||
|
||||
resetPassword: function (token, newVerifier) {
|
||||
if (!token)
|
||||
throw new Meteor.Error(400, "Need to pass token");
|
||||
if (!newVerifier)
|
||||
throw new Meteor.Error(400, "Need to pass newVerifier");
|
||||
|
||||
var user = Meteor.users.findOne({"services.password.reset.token": token});
|
||||
if (!user)
|
||||
throw new Meteor.Error(403, "Reset password link expired");
|
||||
|
||||
Meteor.users.update({_id: user._id}, {
|
||||
$set: {'services.password.srp': newVerifier},
|
||||
$unset: {'services.password.reset': 1}
|
||||
});
|
||||
|
||||
var loginToken = Meteor.accounts._loginTokens.insert({userId: user._id});
|
||||
this.setUserId(user._id);
|
||||
return {token: loginToken, id: user._id};
|
||||
}
|
||||
});
|
||||
|
||||
@@ -199,3 +244,10 @@
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
|
||||
Meteor.mail = {};
|
||||
Meteor.mail.send = function() {
|
||||
console.log("Send mail:");
|
||||
console.log(arguments);
|
||||
};
|
||||
|
||||
@@ -37,7 +37,10 @@
|
||||
<span class="hline"> </span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<div class="login-password-form">
|
||||
{{#if isForgotPasswordFlow}}
|
||||
{{> forgotPasswordForm}}
|
||||
{{else}}
|
||||
<div class="login-form login-password-form">
|
||||
<label id="login-username-label" for="login-username">Username</label>
|
||||
<input id="login-username"/>
|
||||
|
||||
@@ -46,6 +49,7 @@
|
||||
|
||||
{{> loginButtonsServicesRowDynamicPart}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<div class="login-button" id="login-buttons-{{name}}">
|
||||
<div class="login-image" id="login-buttons-image-{{name}}"></div>
|
||||
@@ -62,11 +66,9 @@
|
||||
<input id="login-password-again" type="password"/>
|
||||
{{/if}}
|
||||
|
||||
{{#if errorMessage}}
|
||||
<div class="error-message">{{errorMessage}}</div>
|
||||
{{/if}}
|
||||
{{> loginButtonsMessages}}
|
||||
|
||||
<div class="login-button" id="login-buttons-password">
|
||||
<div class="login-button login-button-form-submit" id="login-buttons-password">
|
||||
{{#if inSignupFlow}}
|
||||
Create account
|
||||
{{else}}
|
||||
@@ -74,16 +76,31 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#unless inSignupFlow}}
|
||||
<a id="signup-link">Create account</a>
|
||||
{{/unless}}
|
||||
{{#if inLoginFlow}}
|
||||
<div class="additional-link-container">
|
||||
<a id="signup-link" class="additional-link">Create account</a>
|
||||
</div>
|
||||
|
||||
<div class="additional-link-container">
|
||||
<a id="forgot-password-link" class="additional-link">Forgot password</a>
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
<template name="loginButtonsMessages">
|
||||
{{#if errorMessage}}
|
||||
<div class="message error-message">{{errorMessage}}</div>
|
||||
{{/if}}
|
||||
{{#if infoMessage}}
|
||||
<div class="message info-message">{{infoMessage}}</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
<template name="loginButtonsServicesDropdown">
|
||||
<div class="login-link-and-dropdown-list">
|
||||
<a class="login-link-text">Sign in</a>
|
||||
{{#if dropdownVisible}}
|
||||
<div id="login-dropdown-list">
|
||||
<div id="login-dropdown-list" class="accounts-dialog">
|
||||
<a class="login-close-text">Close</a>
|
||||
<div class="login-close-text-clear"></div>
|
||||
{{> loginButtonsServicesRow}}
|
||||
@@ -91,3 +108,39 @@
|
||||
{{/if}}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="forgotPasswordForm">
|
||||
<div class="login-form">
|
||||
<label id="forgot-password-email-label" for="forgot-password-email">Email</label>
|
||||
<input id="forgot-password-email"/>
|
||||
|
||||
{{> loginButtonsMessages}}
|
||||
|
||||
<div class="login-button login-button-form-submit" id="login-buttons-forgot-password">
|
||||
Reset password
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template name="resetPasswordForm">
|
||||
{{#if inResetPasswordFlow}}
|
||||
<div class="hide-background"></div>
|
||||
|
||||
<div id="reset-password-form" class="accounts-dialog">
|
||||
<label id="reset-password-new-password-label" for="reset-password-new-password">
|
||||
New password
|
||||
</label>
|
||||
<input id="reset-password-new-password"/>
|
||||
|
||||
{{> loginButtonsMessages}}
|
||||
|
||||
<div class="login-button login-button-form-submit" id="reset-password-button">
|
||||
Reset password
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
<body>
|
||||
{{> resetPasswordForm}}
|
||||
</body>
|
||||
|
||||
@@ -5,12 +5,21 @@
|
||||
|
||||
var DROPDOWN_VISIBLE_KEY = 'Meteor.loginButtons.dropdownVisible';
|
||||
var IN_SIGNUP_FLOW_KEY = 'Meteor.loginButtons.inSignupFlow';
|
||||
var IN_FORGOT_PASSWORD_FLOW_KEY = 'Meteor.loginButtons.inForgotPasswordFlow';
|
||||
var ERROR_MESSAGE_KEY = 'Meteor.loginButtons.errorMessage';
|
||||
var INFO_MESSAGE_KEY = 'Meteor.loginButtons.infoMessage';
|
||||
var RESET_PASSWORD_TOKEN_KEY = 'Meteor.loginButtons.resetPasswordToken';
|
||||
|
||||
var resetSession = function () {
|
||||
Session.set(IN_SIGNUP_FLOW_KEY, false);
|
||||
Session.set(IN_FORGOT_PASSWORD_FLOW_KEY, false);
|
||||
Session.set(DROPDOWN_VISIBLE_KEY, false);
|
||||
resetMessages();
|
||||
};
|
||||
|
||||
var resetMessages = function () {
|
||||
Session.set(ERROR_MESSAGE_KEY, null);
|
||||
Session.set(INFO_MESSAGE_KEY, null);
|
||||
};
|
||||
|
||||
|
||||
@@ -89,16 +98,18 @@
|
||||
loginOrSignup();
|
||||
},
|
||||
'click #signup-link': function () {
|
||||
Session.set(ERROR_MESSAGE_KEY, null);
|
||||
resetMessages();
|
||||
Session.set(IN_SIGNUP_FLOW_KEY, true);
|
||||
Session.set(IN_FORGOT_PASSWORD_FLOW_KEY, false);
|
||||
},
|
||||
'click #forgot-password-link': function () {
|
||||
resetMessages();
|
||||
Session.set(IN_SIGNUP_FLOW_KEY, false);
|
||||
Session.set(IN_FORGOT_PASSWORD_FLOW_KEY, true);
|
||||
},
|
||||
'keypress #login-username,#login-password,#login-password-again': function (event) {
|
||||
if (event.keyCode === 13)
|
||||
loginOrSignup();
|
||||
},
|
||||
'keypress #login-password-again': function (event) {
|
||||
if (event.keyCode === 13)
|
||||
loginOrSignup();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,13 +125,29 @@
|
||||
return getLoginServices().length > 1;
|
||||
};
|
||||
|
||||
Template.loginButtonsServicesRow.isForgotPasswordFlow = function () {
|
||||
return Session.get(IN_FORGOT_PASSWORD_FLOW_KEY);
|
||||
};
|
||||
|
||||
//
|
||||
// loginButtonsMessage template
|
||||
//
|
||||
|
||||
Template.loginButtonsMessages.errorMessage = function () {
|
||||
return Session.get(ERROR_MESSAGE_KEY);
|
||||
};
|
||||
|
||||
Template.loginButtonsMessages.infoMessage = function () {
|
||||
return Session.get(INFO_MESSAGE_KEY);
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// loginButtonsServicesRowDynamicPart template
|
||||
//
|
||||
|
||||
Template.loginButtonsServicesRowDynamicPart.errorMessage = function () {
|
||||
return Session.get(ERROR_MESSAGE_KEY);
|
||||
Template.loginButtonsServicesRowDynamicPart.inLoginFlow = function () {
|
||||
return !Session.get(IN_SIGNUP_FLOW_KEY) && !Session.get(IN_FORGOT_PASSWORD_FLOW_KEY);
|
||||
};
|
||||
|
||||
Template.loginButtonsServicesRowDynamicPart.inSignupFlow = function () {
|
||||
@@ -128,6 +155,36 @@
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// forgotPasswordForm template
|
||||
//
|
||||
Template.forgotPasswordForm.events = {
|
||||
'keypress #forgot-password-email': function (event) {
|
||||
if (event.keyCode === 13)
|
||||
forgotPassword();
|
||||
},
|
||||
'click #login-buttons-forgot-password': function () {
|
||||
forgotPassword();
|
||||
}
|
||||
};
|
||||
|
||||
var forgotPassword = function () {
|
||||
resetMessages();
|
||||
|
||||
var email = document.getElementById("forgot-password-email").value;
|
||||
if (email.indexOf('@') !== -1) {
|
||||
Meteor.forgotPassword({email: email}, function (error) {
|
||||
if (error)
|
||||
Session.set(ERROR_MESSAGE_KEY, error.reason);
|
||||
else
|
||||
Session.set(INFO_MESSAGE_KEY, "Email sent");
|
||||
});
|
||||
} else {
|
||||
Session.set(ERROR_MESSAGE_KEY, "Invalid email");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// loginButtonsServicesDropdown template
|
||||
//
|
||||
@@ -161,11 +218,53 @@
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// resetPasswordForm template
|
||||
//
|
||||
|
||||
Template.resetPasswordForm.events = {
|
||||
'click #reset-password-button': function () {
|
||||
resetPassword();
|
||||
},
|
||||
'keypress #reset-password-new-password': function (event) {
|
||||
if (event.keyCode === 13)
|
||||
resetPassword();
|
||||
}
|
||||
};
|
||||
|
||||
var resetPassword = function () {
|
||||
resetMessages();
|
||||
var newPassword = document.getElementById('reset-password-new-password').value;
|
||||
if (!validatePassword(newPassword))
|
||||
return;
|
||||
|
||||
Meteor.resetPassword(
|
||||
Session.get(RESET_PASSWORD_TOKEN_KEY), newPassword,
|
||||
function (error) {
|
||||
if (error) {
|
||||
Session.set(ERROR_MESSAGE_KEY, error.reason);
|
||||
} else {
|
||||
Session.set(RESET_PASSWORD_TOKEN_KEY, null);
|
||||
Meteor.accounts._preventAutoLogin = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Template.resetPasswordForm.inResetPasswordFlow = function () {
|
||||
return Session.get(RESET_PASSWORD_TOKEN_KEY);
|
||||
};
|
||||
|
||||
if (Meteor.accounts._resetPasswordToken) {
|
||||
Session.set(RESET_PASSWORD_TOKEN_KEY, Meteor.accounts._resetPasswordToken);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// helpers
|
||||
//
|
||||
|
||||
var login = function () {
|
||||
resetMessages();
|
||||
var username = document.getElementById('login-username').value;
|
||||
var password = document.getElementById('login-password').value;
|
||||
|
||||
@@ -177,25 +276,26 @@
|
||||
};
|
||||
|
||||
var signup = function () {
|
||||
resetMessages();
|
||||
var username = document.getElementById('login-username').value;
|
||||
var password = document.getElementById('login-password').value;
|
||||
var passwordAgain = document.getElementById('login-password-again').value;
|
||||
|
||||
// XXX these will become configurable, and will be validated on
|
||||
// the server as well.
|
||||
if (username.length < 3) {
|
||||
Session.set(ERROR_MESSAGE_KEY, "Username must be at least 3 characters long");
|
||||
} else if (password.length < 6) {
|
||||
Session.set(ERROR_MESSAGE_KEY, "Password must be at least 6 characters long");
|
||||
} else if (password !== passwordAgain) {
|
||||
if (!validateUsername(username) || !validatePassword(password))
|
||||
return;
|
||||
|
||||
if (password !== passwordAgain) {
|
||||
Session.set(ERROR_MESSAGE_KEY, "Passwords don't match");
|
||||
} else {
|
||||
Meteor.createUser({username: username, password: password}, function (error) {
|
||||
if (error) {
|
||||
Session.set(ERROR_MESSAGE_KEY, error.reason);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Meteor.createUser({username: username, password: password}, function (error) {
|
||||
if (error) {
|
||||
Session.set(ERROR_MESSAGE_KEY, error.reason);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var loginOrSignup = function () {
|
||||
@@ -223,4 +323,24 @@
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
// XXX improve these? should this be in accounts-passwords instead?
|
||||
var validateUsername = function (username) {
|
||||
if (username.length >= 3) {
|
||||
return true;
|
||||
} else {
|
||||
Session.set(ERROR_MESSAGE_KEY, "Username must be at least 3 characters long");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
var validatePassword = function (password) {
|
||||
if (password.length >= 6) {
|
||||
return true;
|
||||
} else {
|
||||
Session.set(ERROR_MESSAGE_KEY, "Password must be at least 6 characters long");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
#login-buttons .login-button {
|
||||
@login-buttons-accounts-dialog-width: 158px;
|
||||
|
||||
#login-buttons .login-button, .accounts-dialog .login-button {
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
padding: 1px 4px;
|
||||
@@ -43,7 +45,7 @@
|
||||
margin-left: 5px; /* so that other elements aren't too close */
|
||||
}
|
||||
|
||||
#login-dropdown-list .login-button {
|
||||
.accounts-dialog .login-button {
|
||||
width: 158px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
@@ -80,24 +82,13 @@
|
||||
clear: right;
|
||||
}
|
||||
|
||||
#login-dropdown-list {
|
||||
position: absolute;
|
||||
@login-buttons-accounts-dialog-padding-left: 8px;
|
||||
|
||||
.accounts-dialog {
|
||||
border: 1px solid black;
|
||||
z-index: 1000;
|
||||
width: 167px;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
background: white;
|
||||
|
||||
margin-top: -5px;
|
||||
padding-top: 4px; /* = border-width - margin-top */
|
||||
|
||||
margin-right: -8px;
|
||||
padding-right: 8px; /* = -margin-right */
|
||||
|
||||
padding-left: 8px;
|
||||
padding-bottom: 8px;
|
||||
|
||||
-moz-box-shadow: -2px 3px 3px 1px rgba(0, 0, 0, 0.3);
|
||||
-webkit-box-shadow: -2px 3px 3px 1px rgba(0, 0, 0, 0.3);
|
||||
box-shadow: -2px 3px 3px 1px rgba(0, 0, 0, 0.3);
|
||||
@@ -105,6 +96,23 @@
|
||||
-moz-border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
|
||||
margin-top: -5px;
|
||||
padding-top: 4px; /* = border-width - margin-top */
|
||||
|
||||
margin-right: -8px;
|
||||
padding-right: 8px; /* = -margin-right */
|
||||
|
||||
padding-left: @login-buttons-accounts-dialog-padding-left;
|
||||
padding-bottom: 8px;
|
||||
|
||||
width: 167px;
|
||||
}
|
||||
|
||||
#login-dropdown-list {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
#login-buttons .hline {
|
||||
@@ -123,26 +131,66 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#login-buttons label {
|
||||
.accounts-dialog label {
|
||||
font-weight: bold;
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
#login-buttons .login-password-form input {
|
||||
.accounts-dialog input {
|
||||
width: 162px;
|
||||
}
|
||||
|
||||
#login-buttons #login-buttons-password {
|
||||
.accounts-dialog .login-button-form-submit {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
#login-buttons .error-message {
|
||||
color: red;
|
||||
.accounts-dialog .message {
|
||||
font-size: 80%;
|
||||
margin-top: 2px;
|
||||
}
|
||||
|
||||
.accounts-dialog .error-message {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.accounts-dialog .info-message {
|
||||
color: green;
|
||||
}
|
||||
|
||||
#login-buttons .additional-link {
|
||||
font-size: 60%;
|
||||
}
|
||||
|
||||
#login-buttons #signup-link {
|
||||
float: right;
|
||||
font-size: 70%;
|
||||
}
|
||||
}
|
||||
|
||||
#login-buttons #forgot-password-link {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#reset-password-form {
|
||||
z-index: 1000;
|
||||
position: fixed;
|
||||
|
||||
left: 50%;
|
||||
margin-left: -(@login-buttons-accounts-dialog-width
|
||||
+ @login-buttons-accounts-dialog-padding-left) / 2;
|
||||
|
||||
top: 50%;
|
||||
margin-top: -40px; /* = approximately -height/2, though height can change */
|
||||
}
|
||||
|
||||
.hide-background {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 999;
|
||||
|
||||
/* XXX consider replacing with DXImageTransform */
|
||||
background-color: rgb(0.2, 0.2, 0.2); /* fallback for IE7-8 */
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
@@ -3,10 +3,11 @@ Package.describe({
|
||||
});
|
||||
|
||||
Package.on_use(function (api) {
|
||||
api.use(['accounts', 'underscore', 'liveui', 'templating'], 'client');
|
||||
api.use(['accounts-urls', 'accounts', 'underscore', 'liveui', 'templating'], 'client');
|
||||
api.use('less', 'server');
|
||||
|
||||
api.add_files([
|
||||
'login_buttons.css',
|
||||
'login_buttons.less',
|
||||
'login_buttons_images.css',
|
||||
'login_buttons.html',
|
||||
'login_buttons.js'], 'client');
|
||||
|
||||
9
packages/accounts-urls/package.js
Normal file
9
packages/accounts-urls/package.js
Normal file
@@ -0,0 +1,9 @@
|
||||
Package.describe({
|
||||
summary: "Generate and consume reset password and validate account URLs",
|
||||
internal: true
|
||||
});
|
||||
|
||||
Package.on_use(function (api) {
|
||||
api.add_files('url_client.js', 'client');
|
||||
api.add_files('url_server.js', 'server');
|
||||
});
|
||||
26
packages/accounts-urls/url_client.js
Normal file
26
packages/accounts-urls/url_client.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// reads a reset password token from the url's hash fragment, if it's there. if so
|
||||
// prevent automatically logging in since it could be confusing to be logged in as user
|
||||
// A while resetting password for user B
|
||||
//
|
||||
// reset password urls use hash fragments instead of url paths/query
|
||||
// strings so that the reset password token is not sent over the wire
|
||||
// on the http request
|
||||
(function () {
|
||||
if (!Meteor.accounts)
|
||||
Meteor.accounts = {};
|
||||
|
||||
var match;
|
||||
match = window.location.hash.match(/^\#\?reset-password\/(.*)$/);
|
||||
if (match) {
|
||||
Meteor.accounts._preventAutoLogin = true;
|
||||
Meteor.accounts._resetPasswordToken = match[1];
|
||||
window.location.hash = '';
|
||||
}
|
||||
|
||||
match = window.location.hash.match(/^\#\?validate-user\/(.*)$/);
|
||||
if (match) {
|
||||
Meteor.accounts._preventAutoLogin = true;
|
||||
Meteor.accounts._validateUserToken = match[1];
|
||||
window.location.hash = '';
|
||||
}
|
||||
})();
|
||||
13
packages/accounts-urls/url_server.js
Normal file
13
packages/accounts-urls/url_server.js
Normal file
@@ -0,0 +1,13 @@
|
||||
if (!Meteor.accounts)
|
||||
Meteor.accounts = {};
|
||||
|
||||
if (!Meteor.accounts.urls)
|
||||
Meteor.accounts.urls = {};
|
||||
|
||||
Meteor.accounts.urls.resetPassword = function (baseUrl, token) {
|
||||
return baseUrl + '#?reset-password/' + token;
|
||||
};
|
||||
|
||||
Meteor.accounts.urls.validateUser = function (baseUrl, token) {
|
||||
return baseUrl + '#?validate-user/' + token;
|
||||
};
|
||||
@@ -66,23 +66,28 @@ Meteor.loginWithToken = function (token, errorCallback) {
|
||||
});
|
||||
};
|
||||
|
||||
// Immediately try to log in via local storage, so that any DDP
|
||||
// messages are sent after we have established our user account
|
||||
var token = Meteor.accounts.storedLoginToken();
|
||||
if (token) {
|
||||
// On startup, optimistically present us as logged in while the
|
||||
// request is in flight. This reduces page flicker on startup.
|
||||
var userId = Meteor.accounts.storedUserId();
|
||||
userId && Meteor.default_connection.setUserId(userId);
|
||||
Meteor.loginWithToken(token, function () {
|
||||
Meteor.accounts.makeClientLoggedOut();
|
||||
});
|
||||
if (!Meteor.accounts._preventAutoLogin) {
|
||||
// Immediately try to log in via local storage, so that any DDP
|
||||
// messages are sent after we have established our user account
|
||||
var token = Meteor.accounts.storedLoginToken();
|
||||
if (token) {
|
||||
// On startup, optimistically present us as logged in while the
|
||||
// request is in flight. This reduces page flicker on startup.
|
||||
var userId = Meteor.accounts.storedUserId();
|
||||
userId && Meteor.default_connection.setUserId(userId);
|
||||
Meteor.loginWithToken(token, function () {
|
||||
Meteor.accounts.makeClientLoggedOut();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Poll local storage every 3 seconds to login if someone logged in in
|
||||
// another tab
|
||||
Meteor.accounts._lastLoginTokenWhenPolled = token;
|
||||
setInterval(function() {
|
||||
if (Meteor.accounts._preventAutoLogin)
|
||||
return;
|
||||
|
||||
var currentLoginToken = Meteor.accounts.storedLoginToken();
|
||||
|
||||
// != instead of !== just to make sure undefined and null are treated the same
|
||||
@@ -94,4 +99,3 @@ setInterval(function() {
|
||||
}
|
||||
Meteor.accounts._lastLoginTokenWhenPolled = currentLoginToken;
|
||||
}, 3000);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ Package.describe({
|
||||
Package.on_use(function (api) {
|
||||
api.use('underscore', 'server');
|
||||
api.use('localstorage-polyfill', 'client');
|
||||
api.use('accounts-urls', 'client');
|
||||
|
||||
// need this because of the Meteor.users collection but in the future
|
||||
// we'd probably want to abstract this away
|
||||
|
||||
Reference in New Issue
Block a user