-
Notifications
You must be signed in to change notification settings - Fork 9
/
strategy.js
144 lines (127 loc) · 4.26 KB
/
strategy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/**
* Module dependencies.
*/
var passport = require('passport-strategy')
, BrowserID = require('browserid-local-verify')
, util = require('util');
/**
* `Strategy` constructor.
*
* The BrowserID authentication strategy authenticates requests using the
* navigator.id JavaScript API and the BrowserID protocol to verify assertions.
*
* BrowserID provides a federated and decentralized universal login system for
* the web, based on email addresses as an identity token. Authenticating in
* this this manner involves a sequence of events, including prompting the user,
* via their user agent, for an assertion of email address ownership. Once this
* assertion is obtained, it can be verified and the user can be authenticated.
*
* Applications must supply a `verify` callback which accepts an `email`
* address, and then calls the `done` callback supplying a `user`, which should
* be set to `false` if the credentials are not valid. If an exception occured,
* `err` should be set.
*
* Options:
* - `audience` the website requesting and verifying an identity assertion
* - `assertionField` field name where the assertion is found, defaults to 'assertion'
* - `passReqToCallback` when `true`, `req` is the first argument to the verify callback (default: `false`)
*
* Examples:
*
* passport.use(new BrowserIDStrategy({
* audience: 'http://www.example.com'
* },
* function(email, done) {
* User.findByEmail(email, function (err, user) {
* done(err, user);
* });
* }
* ));
*
* @param {Object} options
* @param {Function} verify
* @api public
*/
function Strategy(options, verify) {
if (!verify) throw new TypeError('BrowserIDStrategy requires a verify callback');
if (!options.audience) throw new TypeError('BrowserIDStrategy requires an audience option');
passport.Strategy.call(this);
this.name = 'browserid';
this._verify = verify;
var opts = {};
if (options.trustedIssuers) { opts.trustedIssuers = options.trustedIssuers; }
if (options.httpRequest) { opts.httpRequest = options.httpRequest; }
this._browserID = new BrowserID(opts);
this._audience = options.audience;
this._assertionField = options.assertionField || 'assertion';
this._passReqToCallback = options.passReqToCallback;
}
/**
* Inherit from `passport.Strategy`.
*/
util.inherits(Strategy, passport.Strategy);
/**
* Authenticate request by verifying a BrowserID assertion.
*
* @param {Object} req
* @api protected
*/
Strategy.prototype.authenticate = function(req) {
var self = this;
if (!req.body || !req.body[this._assertionField]) {
return this.fail({ message: 'Missing assertion' }, 400);
}
// Get the Backed Identity Assertion from the request. This is a sequence
// (typically just one) of Identity Certificates followed by an
// Identity Assertion.
var assertion = req.body[this._assertionField];
this._browserID.verify({
assertion: assertion,
audience: this._audience
}, function(err, result) {
if (err) {
var status;
if (typeof err == 'string') {
// HACK: `browserid-local-verify` yeilds all errors as string, so we need
// to inspect the message to determine useful status.
if (err.indexOf('audience mismatch') == 0 ||
err.indexOf('expired') == 0) {
status = 403;
}
err = new Error(err);
}
if (status) {
return self.fail({ message: err.message }, status);
}
return self.error(err);
}
function done(err, user, info) {
if (err) { return self.error(err); }
if (!user) { return self.fail(info); }
self.success(user, info);
}
try {
if (self._passReqToCallback) {
var arity = self._verify.length;
if (arity == 4) {
self._verify(req, result.email, result.issuer, done);
} else {
self._verify(req, result.email, done);
}
} else {
var arity = self._verify.length;
if (arity == 3) {
self._verify(result.email, result.issuer, done);
} else {
self._verify(result.email, done);
}
}
} catch (ex) {
return self.error(ex);
}
});
}
/**
* Expose `Strategy`.
*/
module.exports = Strategy;