Permalink
Browse files

initial import

  • Loading branch information...
matb33 committed May 15, 2012
0 parents commit d47131bc55649e595fa522a316c942a802f789bd
@@ -0,0 +1 @@
+local
@@ -0,0 +1,5 @@
+# Meteor packages used by this project, one per line.
+#
+# 'meteor add' and 'meteor remove' will edit this file for you,
+# but you can also edit it by hand.
+
@@ -0,0 +1,7 @@
+# UserAuth example project for Meteor JS
+
+## Dependencies
+
+- bcrypt for Node.js:
+ cd /usr/lib/meteor/lib
+ npm install bcrypt
@@ -0,0 +1,41 @@
+/**
+* jQuery Cookie plugin
+*
+* Copyright (c) 2010 Klaus Hartl (stilbuero.de)
+* Dual licensed under the MIT and GPL licenses:
+* http://www.opensource.org/licenses/mit-license.php
+* http://www.gnu.org/licenses/gpl.html
+*
+*/
+jQuery.cookie = function (key, value, options) {
+
+ // key and at least value given, set cookie...
+ if (arguments.length > 1 && String(value) !== "[object Object]") {
+ options = jQuery.extend({}, options);
+
+ if (value === null || value === undefined) {
+ options.expires = -1;
+ }
+
+ if (typeof options.expires === 'number') {
+ var days = options.expires, t = options.expires = new Date();
+ t.setDate(t.getDate() + days);
+ }
+
+ value = String(value);
+
+ return (document.cookie = [
+ encodeURIComponent(key), '=',
+ options.raw ? value : encodeURIComponent(value),
+ options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
+ options.path ? '; path=' + options.path : '',
+ options.domain ? '; domain=' + options.domain : '',
+ options.secure ? '; secure' : ''
+ ].join(''));
+ }
+
+ // key and possibly options given, get cookie...
+ options = value || {};
+ var result, decode = options.raw ? function (s) { return s; } : decodeURIComponent;
+ return (result = new RegExp('(?:^|; )' + encodeURIComponent(key) + '=([^;]*)').exec(document.cookie)) ? decode(result[1]) : null;
+};
@@ -0,0 +1,47 @@
+/*******************************************
+* RPC helper callback
+*******************************************/
+
+/**
+ Assumes result is formed of an object with
+ at minimum two properties:
+ 1) result.success (boolean)
+ 2) result.reason (string)
+*/
+
+var RPC = function () {
+ var args = arguments;
+ var callback = args[args.length - 1];
+
+ if (typeof callback === "function") {
+ args[args.length - 1] = function (error, result) {
+ if (!error) {
+ callback.call(this, result);
+ } else {
+ console.log("RPC error:", error);
+ }
+ };
+ }
+
+ Meteor.call.apply(this, args);
+};
+
+/*******************************************
+* Session token management
+*******************************************/
+
+var initializeSessionToken = function () {
+ Session.set("token", $.cookie("X_SESSION_TOKEN") || "unknown");
+};
+
+var rememberSessionToken = function (sessionToken) {
+ sessionToken = sessionToken || "";
+ sessionToken = sessionToken === "" ? "unknown" : sessionToken;
+ $.cookie("X_SESSION_TOKEN", sessionToken);
+ Session.set("token", sessionToken);
+};
+
+var forgetSessionToken = function () {
+ $.cookie("X_SESSION_TOKEN", "unknown");
+ Session.set("token", "unknown");
+}
No changes.
@@ -0,0 +1,53 @@
+<head>
+ <title>User auth example for Meteor JS</title>
+</head>
+
+<body>
+ {{> main}}
+</body>
+
+<template name="main">
+ <h1>User auth example in Meteor</h1>
+ <section class="authentication">
+ <h2>Authentication</h2>
+ {{#if is_authenticated}}
+ <button name="logout">Logout</button>
+ {{else}}
+ <label>Username: <input name="username" /></label>
+ <label>Password: <input name="password" type="password" /></label>
+ <button name="login">Login</button>
+ {{/if}}
+ </section>
+ <section class="users">
+ <h2>Users</h2>
+ <ul>
+ {{#each users}}
+ {{> user_row}}
+ {{/each}}
+ </ul>
+ </section>
+ <section class="records">
+ <h2>Records</h2>
+ <ul>
+ {{#each records}}
+ {{> record_row}}
+ {{/each}}
+ </ul>
+ <h3>Add record</h3>
+ <label>Title: <input name="new-record-title" /></label>
+ <label>Is private: <input type="checkbox" name="new-record-is-private" /></label>
+ <button name="add-new-record">add</button>
+ </section>
+</template>
+
+<template name="user_row">
+ <li>
+ <strong>{{name}}</strong> <strike>{{username}} {{password_hash}}</strike>
+ </li>
+</template>
+
+<template name="record_row">
+ <li>
+ <strong>{{title}}</strong>, owned by <strong>{{owner_name}}</strong>. Is private? <strong>{{is_private}}</strong>
+ </li>
+</template>
@@ -0,0 +1,89 @@
+/*******************************************
+* RPC calls
+*******************************************/
+
+var login = function (username, password) {
+ RPC("login", username, password, function (result) {
+ var sessionToken = result.sessionToken;
+ rememberSessionToken(sessionToken);
+
+ if (sessionToken) {
+ console.log("Password verified!");
+ } else {
+ console.log("Could not log you in with that username and/or password combination.");
+ }
+ });
+};
+
+var logout = function () {
+ RPC("logout", Session.get("token"), function (result) {
+ if (result.success === true) {
+ forgetSessionToken();
+ console.log("logout success");
+ } else {
+ console.log("logout failed:", result.reason);
+ }
+ });
+};
+
+var addRecord = function (title, is_private) {
+ RPC("addRecord", title, is_private, Session.get("token"), function (result) {
+ if (result.success === true) {
+ console.log("addRecord success");
+ } else {
+ console.log("addRecord failed:", result.reason);
+ }
+ });
+};
+
+/*******************************************
+* TEMPLATE: main
+*******************************************/
+
+Template.main.users = function () {
+ return Users.find();
+};
+
+Template.main.records = function () {
+ return Records.find();
+};
+
+Template.main.is_authenticated = function () {
+ return Session.get("token") !== "unknown";
+};
+
+Template.main.events = {
+ "click button[name='login']": function (evt) {
+ login(
+ $("input[name='username']").val(),
+ $("input[name='password']").val()
+ );
+ },
+ "click button[name='logout']": function (evt) {
+ logout();
+ },
+ "click button[name='add-new-record']": function (evt) {
+ addRecord(
+ $("input[name='new-record-title']").val(),
+ $("input[name='new-record-is-private']").is(":checked")
+ );
+ }
+};
+
+/*******************************************
+* TEMPLATE: record_row
+*******************************************/
+
+Template.record_row.owner_name = function () {
+ var user = Users.findOne({_id: this.user_id});
+ return user ? user.name : "N/A";
+};
+
+/*******************************************
+* Initialize
+*******************************************/
+
+Meteor.subscribe("users");
+Meteor.subscribe("records");
+
+initializeSessionToken();
@@ -0,0 +1,79 @@
+/*******************************************
+* Authentication methods
+*******************************************/
+
+var getUserBySessionToken = function (sessionToken) {
+ var user;
+
+ if (isSessionTokenValid(sessionToken)) {
+ user = Users.findOne({session_token: sessionToken});
+ if (user) {
+ return user;
+ }
+ }
+};
+
+var isUserPasswordCorrect = function (user, password) {
+ var bcrypt = __meteor_bootstrap__.require("bcrypt");
+
+ if (user && user.password_hash) {
+ return bcrypt.compareSync(password, user.password_hash);
+ }
+
+ return false;
+};
+
+var getServerKey = function () {
+ // This is meant to be a unique value for your application. Changing this will
+ // make any stored session tokens invalid and force users to re-authenticate.
+ return "552ad4c6f2c87b5ef6c4d29614d95f57";
+}
+
+var generateSignedToken = function () {
+ var randomToken = CryptoJS.SHA256(Math.random().toString()).toString();
+ var signature = CryptoJS.HmacMD5(randomToken, getServerKey()).toString();
+ var signedToken = randomToken + ":" + signature;
+
+ return signedToken;
+};
+
+var isSessionTokenValid = function (sessionToken) {
+ var parts = sessionToken.toString().split(":");
+ var token = parts[0];
+ var signature = parts[1];
+
+ return signature === CryptoJS.HmacMD5(token, getServerKey()).toString();
+};
+
+var getSessionTokenByUser = function (user) {
+ var sessionToken;
+
+ // Generate signed token, but only if one isn't already
+ // stored. This allows the user to maintain multiple
+ // sessions across different computers/browsers
+ if (user.session_token) {
+ sessionToken = user.session_token;
+ } else {
+ sessionToken = generateSignedToken();
+ Users.update(user._id, {$set: {session_token: sessionToken}});
+ }
+
+ return sessionToken;
+};
+
+var getSessionTokenByUsernamePassword = function (username, password) {
+ var user = Users.findOne({username: username});
+
+ if (isUserPasswordCorrect(user, password)) {
+ return getSessionTokenByUser(user);
+ }
+};
+
+var clearUserBySessionToken = function (sessionToken) {
+ var user = getUserBySessionToken(sessionToken);
+
+ if (user) {
+ Users.update(user._id, {$unset: {session_token: 1}});
+ return true;
+ }
+};
@@ -0,0 +1,17 @@
+/**
+ Lock down various collections so as to prevent clients from modifying them
+ directly. We use the methods in server_methods.js to modify collections,
+ which are exposed carefully via Meteor.methods.
+*/
+
+Meteor.startup(function () {
+ // Lock down Users
+ Meteor.default_server.method_handlers["/users/insert"] = function() {};
+ Meteor.default_server.method_handlers["/users/update"] = function() {};
+ Meteor.default_server.method_handlers["/users/remove"] = function() {};
+
+ // Lock down Records
+ Meteor.default_server.method_handlers["/records/insert"] = function() {};
+ Meteor.default_server.method_handlers["/records/update"] = function() {};
+ Meteor.default_server.method_handlers["/records/remove"] = function() {};
+});

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.

Some generated files are not rendered by default. Learn more.

Oops, something went wrong.
Oops, something went wrong.

0 comments on commit d47131b

Please sign in to comment.