diff --git a/app.js b/app.js index 7625775811..6e6ac1757b 100755 --- a/app.js +++ b/app.js @@ -149,6 +149,8 @@ app.get('/auth/tumblr/callback', passport.authorize('tumblr', { failureRedirect: res.redirect('/api/tumblr'); }); +app.post('/auth/browserid', passport.authenticate('browserid', { successRedirect: '/', failureRedirect: '/login' })); + /** * Start Express server. */ diff --git a/config/passport.js b/config/passport.js index 2983b30462..0a619139e4 100755 --- a/config/passport.js +++ b/config/passport.js @@ -6,6 +6,7 @@ var FacebookStrategy = require('passport-facebook').Strategy; var TwitterStrategy = require('passport-twitter').Strategy; var GitHubStrategy = require('passport-github').Strategy; var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy; +var BrowserIDStrategy = require('passport-browserid').Strategy; var User = require('../models/User'); var secrets = require('./secrets'); var _ = require('underscore'); @@ -205,6 +206,52 @@ passport.use(new GoogleStrategy(secrets.google, function(req, accessToken, refre } })); +/** + * Sign in with BrowserID/Persona + */ +passport.use(new BrowserIDStrategy(secrets.persona, function(req, email, done) { + if (req.user) { + User.findOne({ browserid: email }, function(err, existingUser) { + if (existingUser) { + req.flash('errors', { msg: 'There is already a user linked to Persona using this email address. Sign in with that account or delete it, then link it with your current account.' }); + done(err); + } else { + if (req.user.email === email) { + User.findById(req.user.id, function(err, user) { + user.browserid = email; + user.save(function(err) { + req.flash('info', { msg: 'Persona account has been linked.' }); + done(err, user); + }); + }); + } + } + }); + } else { + User.findOne({ $or: [{email: email }, { browserid: email }]}, function(err, existingUser) { + if (existingUser) { + if (!existingUser.browserid) { + existingUser.browserid = email; + existingUser.save(function(err) { + done(err, user); + }) + } + else { + return done(null, existingUser); + } + } + else { + var user = new User(); + user.email = email; + user.browserid = email; + user.save(function(err) { + done(err, user); + }); + } + }); + } +})); + passport.use('tumblr', new OAuthStrategy({ requestTokenURL: 'http://www.tumblr.com/oauth/request_token', accessTokenURL: 'http://www.tumblr.com/oauth/access_token', diff --git a/config/secrets.js b/config/secrets.js index f4bea75a8d..58266945b7 100644 --- a/config/secrets.js +++ b/config/secrets.js @@ -45,6 +45,12 @@ module.exports = { passReqToCallback: true }, + persona: { + audience: 'http://localhost:3000', //replace with your app URL or it will fail + assertionField: 'assertion', //do not modify + passReqToCallback: true //do not modify + }, + steam: { apiKey: 'Your Steam API Key' }, diff --git a/models/User.js b/models/User.js index e8203720ae..f23a73fbd1 100644 --- a/models/User.js +++ b/models/User.js @@ -10,6 +10,7 @@ var userSchema = new mongoose.Schema({ twitter: { type: String, unique: true, sparse: true }, google: { type: String, unique: true, sparse: true }, github: { type: String, unique: true, sparse: true }, + browserid: { type: String, unique: true, sparse: true }, tokens: Array, profile: { diff --git a/package.json b/package.json index a2cc257081..ff3dcd6592 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,10 @@ { "name": "hackathon-starter", "version": "0.0.0", + "repository": { + "type": "git", + "url": "https://github.com/sahat/hackathon-starter" + }, "dependencies": { "async": "~0.2.10", "bcrypt-nodejs": "~0.0.3", @@ -23,6 +27,7 @@ "passport-local": "~0.1.6", "passport-oauth": "~1.0.0", "passport-twitter": "~1.0.2", + "passport-browserid": "~0.1.4", "request": "~2.33.0", "sendgrid": "~0.4.6", "tumblr.js": "~0.0.4", diff --git a/public/css/styles.less b/public/css/styles.less index 8e105d8a32..8ee538065a 100644 --- a/public/css/styles.less +++ b/public/css/styles.less @@ -93,6 +93,16 @@ body { } } +.btn-browserid { + color: #fff; + background: #64a8d9; + border: 1px solid rgba(0, 0, 0, 0.07); + + &:hover { + color: #fff; + } +} + // Extra space between font-awesome icons and text [class^="fa-"], [class*="fa-"] { diff --git a/public/img/persona_logo_glyph.png b/public/img/persona_logo_glyph.png new file mode 100644 index 0000000000..bde2215b01 Binary files /dev/null and b/public/img/persona_logo_glyph.png differ diff --git a/public/js/main.js b/public/js/main.js index c7e75730af..5cda198072 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1,5 +1,20 @@ $(document).ready(function() { - - // Place JavaScript code here... - + $("#browserid").click(function() { + console.log('clicked on persona login'); + navigator.id.get(function(assertion) { + if (assertion) { + $('input[name="assertion"]').val(assertion); + $('form[action="/auth/browserid"]').submit(); + } else { + console.log('bad assertion'); + location.reload(); + } + }); + }); + + $("#logout").click(function() { + console.log('logout'); + navigator.id.logout(); + }) }); + diff --git a/views/account/login.jade b/views/account/login.jade index 803e0b1738..5a666f0360 100644 --- a/views/account/login.jade +++ b/views/account/login.jade @@ -2,6 +2,10 @@ extends ../layout block content .col-sm-8.col-sm-offset-2 + form(method='POST', action='/auth/browserid') + input(type='hidden', name='_csrf', value=token) + input(type='hidden', name='assertion') + form(method='POST') input(type='hidden', name='_csrf', value=token) legend Sign In @@ -19,6 +23,9 @@ block content a.btn.btn-google-plus(href='/auth/google') i.fa.fa-google-plus | Google + a.btn.btn-browserid(id="browserid",href="#") + img(src="/img/persona_logo_glyph.png") + | Persona .form-group label.control-label(for='email') Email input.form-control(type='text', name='email', id='email', placeholder='Email', autofocus=true) diff --git a/views/account/profile.jade b/views/account/profile.jade index b52afc1dcb..bb8d4f5491 100644 --- a/views/account/profile.jade +++ b/views/account/profile.jade @@ -74,6 +74,10 @@ block content .page-header h3 Linked Accounts + form.form-horizontal(action='/auth/browserid', method='POST') + input(type='hidden', name='_csrf', value=token) + input(type='hidden', name='assertion') + if user.google p: a.text-danger(href='/account/unlink/google') Unlink your Google account else @@ -93,3 +97,9 @@ block content p: a.text-danger(href='/account/unlink/github') Unlink your GitHub account else p: a(href='/auth/github') Link your GitHub account + + if user.browserid + p: a.text-danger(href='/account/unlink/browserid') Unlink your Persona account + else + p: a(id="browserid", href='#') Link your Persona account + diff --git a/views/layout.jade b/views/layout.jade index fa454565b1..9820f4105c 100644 --- a/views/layout.jade +++ b/views/layout.jade @@ -9,6 +9,7 @@ html title #{title} - Starter Template for Bootstrap != css('styles') != js('application') + script(src="https://login.persona.org/include.js") body #wrap include partials/navigation diff --git a/views/partials/navigation.jade b/views/partials/navigation.jade index 6bd3b6885c..8ef2042511 100644 --- a/views/partials/navigation.jade +++ b/views/partials/navigation.jade @@ -33,4 +33,4 @@ ul.dropdown-menu li: a(href='/account') My Account li.divider - li: a(href='/logout') Logout \ No newline at end of file + li: a(id='logout',href='/logout') Logout \ No newline at end of file