Skip to content

Commit

Permalink
annotate all code with better explanations of what's what.
Browse files Browse the repository at this point in the history
  • Loading branch information
lloyd committed Sep 17, 2011
1 parent 5a01844 commit 08d5549
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 18 deletions.
7 changes: 7 additions & 0 deletions server/db.js
@@ -1,3 +1,10 @@
// db.js is a tiny persistence layer for myfavoritebeer that uses
// mongodb and the mongodb client library.
//
// This implementation is really not the point of the myfavoritebeer
// example code and is just provided for completeness (the point is
// how you can do authentication with browserid).

const
url = require('url'),
mongodb = require('mongodb');
Expand Down
40 changes: 30 additions & 10 deletions server/main.js
Expand Up @@ -24,6 +24,8 @@ const PORT = process.env.PORT || 0;
// as our external address ('audience' to which assertions will be set)
// if no 'Host' header is present on incoming login requests.
var localHostname = undefined;

// create a webserver using the express framework
var app = express.createServer();

// do some logging
Expand Down Expand Up @@ -53,16 +55,17 @@ app.use(function (req, res, next) {
return next();
});

// The next three functions contain some fancy logic to make it so
// we can run multiple different versions of myfavoritebeer on the
// same server, each which uses a different browserid server
// (dev/beta/prod):
function determineEnvironment(req) {
if (req.headers['host'] === 'myfavoritebeer.org') return 'prod';
else if (req.headers['host'] === 'beta.myfavoritebeer.org') return 'beta';
else if (req.headers['host'] === 'dev.myfavoritebeer.org') return 'dev';
else return 'local';
}

// some fancy logic to make it so we can run multiple different
// versions of myfavoritebeer on the same server, each which uses
// a different browserid server (dev/beta/prod):
function determineBrowserIDURL(req) {
// first defer to the environment
if (process.env.BROWSERID_URL) return process.env.BROWSERID_URL;
Expand All @@ -86,15 +89,24 @@ app.use(postprocess.middleware(function(req, body) {
}));


// and now for the wsapi api
// /api/whoami is an API that returns the authentication status of the current session.
// it returns a JSON encoded string containing the currently authenticated user's email
// if someone is logged in, otherwise it returns null.
app.get("/api/whoami", function (req, res) {
if (req.session && typeof req.session.email === 'string') return res.json(req.session.email);
return res.json(null);
});


// /api/login is an API which authenticates the current session. The client includes
// an assertion in the post body (returned by browserid's navigator.id.getVerifiedEmail()).
// if the assertion is valid an (encrypted) cookie is set to start the user's session.
// returns a json encoded email if the session is successfully authenticated, otherwise
// null.
app.post("/api/login", function (req, res) {
// req.body.assertion contains an assertion we should
// verify, we'll use the browserid verification console
// To verify the assertion we initiate a POST request to the browserid verifier service.
// If we didn't want to rely on this service, it's possible to implement verification
// in a library and to do it ourselves.
var vreq = https.request({
host: determineBrowserIDHost(req),
path: "/verify",
Expand Down Expand Up @@ -122,8 +134,10 @@ app.post("/api/login", function (req, res) {
});
});
vreq.setHeader('Content-Type', 'application/x-www-form-urlencoded');
// the *audience* depends on how the client reaches us. We'll just
// use the hostname out of the request

// An "audience" argument is embedded in the assertion and must match our hostname.
// Because this one server runs on multiple different domain names we just use
// the host parameter out of the request.
var audience = req.headers['host'] ? req.headers['host'] : localHostname;
var data = querystring.stringify({
assertion: req.body.assertion,
Expand All @@ -135,13 +149,17 @@ app.post("/api/login", function (req, res) {
console.log("verifying assertion!");
});

// /api/logout clears the session cookie, effectively terminating the current session.
app.post("/api/logout", function (req, res) {
req.session.email = null;
res.json(true);
});

// /api/get requires an authenticated session, and accesses the current user's favorite
// beer out of the database.
app.get("/api/get", function (req, res) {
var email;

if (req.session && typeof req.session.email === 'string') email = req.session.email;

if (!email) {
Expand All @@ -162,6 +180,8 @@ app.get("/api/get", function (req, res) {
});
});

// /api/set requires an authenticated session, and sets the current user's favorite
// beer in the database.
app.post("/api/set", function (req, res) {
var email = req.session.email;

Expand Down Expand Up @@ -192,7 +212,7 @@ app.post("/api/set", function (req, res) {
});
});

// serve static resources
// Tell express from where it should serve static resources
app.use(express.static(path.join(path.dirname(__dirname), "static")));

// connect up the database!
Expand All @@ -202,7 +222,7 @@ db.connect(function(err) {
process.exit(1);
}

// start listening for connections
// once connected to the database, start listening for connections
app.listen(PORT, IP_ADDRESS, function () {
var address = app.address();
localHostname = address.address + ':' + address.port
Expand Down
23 changes: 15 additions & 8 deletions static/js/main.js
@@ -1,13 +1,12 @@
// Add these in for IE that does not have dev tools open
window.console = window.console || {};
console.log = console.log || function() {};

function setSessions(val) {
if (navigator.id) {
navigator.id.sessions = val ? val : [ ];
}
}

// when the user is found to be logged in we'll update the UI, fetch and
// display the user's favorite beer from the server, and set up handlers to
// wait for user input (specifying their favorite beer).
function loggedIn(email, immediate) {
setSessions([ { email: email } ]);

Expand Down Expand Up @@ -41,7 +40,7 @@ function loggedIn(email, immediate) {
url: '/api/set',
data: { beer: $("input").val() },
success: function(res, status, xhr) {
console.log("successfully set beer:", res);
// noop
}
});
$("#content input").fadeOut(200).fadeIn(400);
Expand All @@ -53,7 +52,6 @@ function loggedIn(email, immediate) {
type: 'GET',
url: '/api/get',
success: function(res, status, xhr) {
console.log("successfully got beer:", res);
$("input").val(res);
}
});
Expand All @@ -65,21 +63,26 @@ function loggedIn(email, immediate) {
$("<img>").attr('src', iurl).appendTo($("header .picture"));
}

// when the user clicks logout, we'll make a call to the server to clear
// our current session.
function logout(event) {
event.preventDefault();
$.ajax({
type: 'POST',
url: '/api/logout',
success: function() {
document.location = '/';
// and then redraw the UI.
loggedOut();
}
});
}


// when no user is logged in, we'll display a "sign-in" button
// which will call into browserid when clicked.
function loggedOut() {
setSessions();
$('.intro').fadeIn(300);
$("header .picture").empty();
var l = $("header .login").removeClass('clickable');
l.html('<img src="i/sign_in_blue.png" alt="Sign in">')
.show().click(function() {
Expand All @@ -88,6 +91,8 @@ function loggedOut() {
}).addClass("clickable");
}

// a handler that is passed an assertion after the user logs in via the
// browserid dialog
function gotVerifiedEmail(assertion) {
// got an assertion, now send it up to the server for verification
$.ajax({
Expand All @@ -114,6 +119,8 @@ if (document.addEventListener) {
document.addEventListener("logout", logout, false);
}

// at startup let's check to see whether we're authenticated to
// myfavoritebeer (have existing cookie), and update the UI accordingly
$(function() {
$.get('/api/whoami', function (res) {
if (res === null) loggedOut();
Expand Down

0 comments on commit 08d5549

Please sign in to comment.