This repository was archived by the owner on Feb 18, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathstrategy.js
184 lines (155 loc) · 6.51 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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
var util = require('util');
var _ = require('underscore');
var crypto = require('crypto');
var base58 = require('bs58');
var Strategy = require('passport-strategy');
var TokenStore = require('passwordless-tokenstore');
var deliveries = require(__dirname + '/deliveries.js');
function PasswordlessStrategy(options, verify) {
this.name = 'passwordless';
//bind passwordless to the current instance to provide multiple strategies at the same time
this.options = options ? util._extend(options) : {};
this._verify = verify;
//check if dynamicConfig should not be used
// initialize only single passwordless instance with one configuration
if (!this.options.dynamicConfig) {
this.initPasswordless();
}
Strategy.call(this);
};
util.inherits(PasswordlessStrategy, Strategy);
//checkup required parameters
PasswordlessStrategy.prototype.checkOptions = function() {
//ensure that a store was given (existing store or path to store lib)
if (!this.options.store) {
throw new Error('Store parameter is missing! Please specify a valid passwordless datastore! (https://passwordless.net/plugins)');
}
//check if the store was set or if an allready existing passwordless store was applied
if (!this.options.store.initialized) {
if (!this.options.store.config) {
throw new Error('Store parameter is missing! Please specify a passwordless datastore parameters!');
}
//check if the store variable is a string and try to load the required dataStore
if (typeof this.options.store.path === 'string') {
try {
this.options.store.lib = require(this.options.store.path);
} catch(ex) {
throw new Error('Passwordless datastore not found! Please specify a valid passwordless datastore!');
}
//initialize new data store
this.options.store = new this.options.store.lib(this.options.store.config);
this.options.store.initialized = true;
} else {
throw new Error('Please specify a valid dataStore path to load the store!');
}
}
//check for a valid delivery (a function or a described object for predefined ones)
if (!this.options.delivery || (!util.isFunction(this.options.delivery) && (!this.options.delivery.type || !deliveries[this.options.delivery.type]))) {
throw new Error('Delivery parameter is missing or invalid! Please specify a valid delivery! ' +
'The delivery must be a functions or one of the following specified deliveries + : ' +
Object.keys(deliveries).join(', '));
}
//check if the delivery is not a function and set it with the predefined function
if (!util.isFunction(this.options.delivery)) {
this.options.delivery = deliveries[this.options.delivery.type](this.options.delivery);
}
if (!util.isFunction(this.options.access)) {
this.options.access = function(user, callback) {
callback(null, user);
};
}
};
//Initialize passwordless
PasswordlessStrategy.prototype.initPasswordless = function() {
this.checkOptions(this.options);
this.passwordless = new (require('passwordless').Passwordless)();
//initialize the token store
this.passwordless.init(this.options.store, {
allowTokenReuse : !!this.options.allowTokenReuse
});
//initialize the delivery
var that = this;
this.passwordless.addDelivery(this.options.delivery, {
ttl : this.options.tokenLifeTime,
tokenAlgorithm : this.options.maxTokenLength ? function() {
var buf = crypto.randomBytes(that.options.maxTokenLength);
return base58.encode(buf);
} : this.options.tokenAlgorithm
});
};
//Passport authentication function
PasswordlessStrategy.prototype.authenticate = function(req, options) {
//merge configiration options with the applied options and check if all was set right
this.options = _.extend(this.options, options);
//initialize passwordless with the current options
if (!!this.options.dynamicConfig) {
this.initPasswordless();
}
//get request parameters to check the authentication state
var user = this.getParam(req.query, this.options.userField, 'user');
if (!user) { user = this.getParam(req.body, this.options.userField, 'user'); }
var token = this.getParam(req.query, this.options.tokenField, 'token');
var uid = this.getParam(req.query, this.options.uidField, 'uid');
//if a token and a uid was specified, verify the token
//if only a user was specified, generate a token and send it
//else send an error to specifiy valid values
if (token && uid) {
this.verifyToken(req, token, uid);
} else if (user) {
this.useDelivery(req);
} else {
return this.error('Could not authenticate! Please specify a user id for the specified delivery (' + options.delivery.type + ') or specify a valid token and uid!');
}
};
//Get a parameter value from an object.
PasswordlessStrategy.prototype.getParam = function(obj, field, defaultField) {
return obj[field || defaultField];
};
//Use the specified delivery to genrate and send a token.
PasswordlessStrategy.prototype.useDelivery = function(req) {
var that = this;
//request a passwordlesstoken
this.passwordless.requestToken(function(user, delivery, callback, req) {
// usually you would want something like:
that.options.access(user, function(err, user) {
if (user) {
callback(err, user);
} else {
callback('This user is not allowed to request a token!', null);
}
});
}, this.options)(req, {}, function(err) {
if (err) {
that.error(err);
} else {
that.pass();
}
});
};
//Use the a sended token to checkup validity.
PasswordlessStrategy.prototype.verifyToken = function(req, token, uid) {
var that = this;
//test the specified token and uid
this.passwordless._tokenStore.authenticate(token, uid.toString(), function(error, valid, referrer) {
if (error) {
that.error(error);
} else if (valid) {
//if the token and uid combination was valid, verify the user
that._verify(req, uid, function(err, user, info) {
if (err) { return that.error(err); }
if (!user) { return that.fail(info); }
//if no token reuse is allowed, invalidate the token after the first authentication
if (!that.options.allowTokenReuse) {
that.passwordless._tokenStore.invalidateUser(uid.toString(), function() {
that.success(user, info);
});
} else {
that.success(user, info);
}
});
} else {
that.error('Invalid token and user id combination!');
}
});
};
module.exports = PasswordlessStrategy;