/
auth.js
165 lines (154 loc) · 5.59 KB
/
auth.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
/*!
* Copyright(c) 2010 Ciaran Jessup <ciaranj@gmail.com>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var connect= require('connect');
var fs= require('fs');
var StrategyExecutor= require('./strategyExecutor');
/**
* Construct the authentication middleware.
* Construction can take 2 forms:
* auth(<Strategy>()/[<Strategy>()]) - A single configured strategy, or array of strategies.
* auth({strategies:<Strategy>()/[<Strategy>()...]}) - More powerful variant that allows for passing in other configuration options, none yet defined.
*/
Auth= module.exports = function(optionsOrStrategy) {
var strategies;
var options;
if( !optionsOrStrategy ) throw new Error("You must specify at least one strategy to use the authentication middleware, even if it is anonymous.");
// Constructor form 1
if( Array.isArray(optionsOrStrategy) || ( optionsOrStrategy.authenticate !== undefined &&
optionsOrStrategy.strategies === undefined ) ) {
strategies= Array.isArray(optionsOrStrategy) ? optionsOrStrategy : [optionsOrStrategy];
options= {};
}
else {
options= optionsOrStrategy
strategies= Array.isArray(optionsOrStrategy.strategies) ? optionsOrStrategy.strategies : [optionsOrStrategy.strategies];
}
var isAuthenticated = function( request, scope ) {
if( scope === undefined ) {
return request.getAuthDetails().user !== undefined;
}
else {
return request.getAuthDetails().scopedUsers[scope].user= undefined;
}
};
var defaultGetPasswordForUser= function(username, password, callback) {
callback( new Error("No mechanism specified for retrieving user passwords.") );
};
var server= connect.createServer(
function auth(req, res, next) {
if( req.session ) {
// Setup the utility methods
req.authenticate = function(strategy, opts, callback) {
var strategy, opts, callback;
var scope;
//ughhh pull this rubbish somewhere tidy...
if( strategy && opts && callback ) {
var type= typeof strategy;
if( strategy.constructor != Array ) {
strategy= [strategy]
}
scope= opts.scope;
}
else if( strategy && opts ) {
callback= opts;
var type= typeof strategy;
if( strategy.constructor == Array ) {
// do nothing
}
else if( type == 'string' ) {
strategy= [strategy];
}
else if( type == 'object') {
scope= strategy.scope
strategy= undefined;
}
}
else if( strategy ) {
callback= strategy;
strategy= undefined;
//TODO: DEFINE DEFAULT STRATEGY(IES)
}
if( isAuthenticated(req, scope) ) callback(null, true);
else strategyExecutor.authenticate(strategy, scope, req, res, function(error, executionResult){
if(error) callback(error); // I really wish the plugins code logged errors..
else {
if( !executionResult.user ) callback(null,false)
else {
if( scope === undefined) {
req.getAuthDetails().user= executionResult.user;
}
else {
req.getAuthDetails().scopedUsers[scope].user= executionResult.user;
}
callback(null, true)
}
}
});
};
req.getAuthDetails= function() {
return req.session.auth;
};
req.isAuthenticated= function(scope) {
return isAuthenticated( req, scope );
};
req.isUnAuthenticated= function(scope) {
return !isAuthenticated( req, scope );
};
req.logout= function(scope) {
if( scope === undefined) {
req.getAuthDetails().user= undefined;
req.getAuthDetails().scopedUsers= {};
}
else {
req.getAuthDetails().scopedUsers[scope].user= undefined;
}
}
// Create the auth holder
if( ! req.getAuthDetails() ) {
var auth= { user: undefined };
req.session.auth= auth;
//TODO: move this to a prototype or somesuch
req.session.scope= function(scope) {
return auth.scopedUsers[scope];
};
}
}
next();
});
// Some strategies require routes to be defined, so give them a chance to do so.
for(var i=0;i< strategies.length; i++ ) {
if( strategies[i].setupRoutes ) {
strategies[i].setupRoutes(server);
}
}
var strategyExecutor = new StrategyExecutor( strategies )
return server;
};
/**
* Auto-load bundled strategies with getters.
*/
var STRATEGY_EXCLUSIONS= {"base.js" : true,
"base64.js" : true};
function augmentAuthWithStrategy(filename, path) {
if (/\.js$/.test(filename) && !STRATEGY_EXCLUSIONS[filename]) {
var name = filename.substr(0, filename.lastIndexOf('.'));
var camelCaseName= name.charAt(0).toUpperCase() + name.substr(1).toLowerCase();
Object.defineProperty(Auth, camelCaseName, {
get: function() {
return require('./' + path+ '/' + name);
},
enumerable:true});
}
}
//TODO: Meh could make this recurse neatly over directories, but I'm lazy.
fs.readdirSync(__dirname + '/auth.strategies').forEach(function(filename){
augmentAuthWithStrategy(filename, '/auth.strategies')
});
fs.readdirSync(__dirname + '/auth.strategies/http').forEach(function(filename){
augmentAuthWithStrategy(filename, '/auth.strategies/http')
});