Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 530 lines (442 sloc) 18.275 kb
f0c67e3 @bnoguchi Initial commit
authored
1 mongoose-auth
2 =============
3
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
4 User authentication plugin for mongoose node.js orm.
5
6 mongoose-auth enables you to support authorization in any number of ways
7 via authorization strategies.
8
9 An authorization strategy is how you authorize your user. Currently
10 mongoose-auth supports the following authorization strategies:
11
12 - `password`
13 - `facebook`
38e41b7 @bnoguchi Finished Twitter module integration.
authored
14 - `twitter`
ff2176e @bnoguchi Updated the list of supported auth strategies.
authored
15 - `github`
16 - `instagram`
1ef2fcd @connor add google as a provider in the README
connor authored
17 - `google`
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
18
c437169 @bnoguchi A bunch of commits, working with everyauth and cleaning up old API
authored
19 mongoose-auth does 3 things:
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
20
21 1. Schema decoration
8604ca5 @bnoguchi Updated README
authored
22 2. (optional) Drop in routing for
9a24187 @bnoguchi Updated README formatting
authored
23 [connect](https://github.com/senchalabs/connect) apps.
c437169 @bnoguchi A bunch of commits, working with everyauth and cleaning up old API
authored
24 3. (optional) Dynamic helpers for
9a24187 @bnoguchi Updated README formatting
authored
25 [express](https://github.com/visionmedia/express) apps.
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
26
b66ce18 @bnoguchi Updated README to incorporate everyauth relationship.
authored
27 It integrates the [everyauth](https://github.com/bnoguchi/everyauth) module
28 to help it take care of the routing and helpers. everyauth is a general
29 purpose npm module for authentication & authorization that can be used
30 independently of mongoose.
31
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
32 ## Schema Decoration
33
34 As you add successive authorization strategies, mongoose-auth at a bare
35 minimum augments your schema with typed attributes corresponding to parameters
36 related to your chosen authorization strategies. For example, if facebook is
37 one of your authorization strategies, then it will add attributes to your
38 User Schema such as 'fb.id' and 'fb.email'.
39
40 To decorate your schema:
5198fde @bnoguchi Full JS syntax highlighting in README
authored
41
4a9ad2d @bnoguchi JS syntax highlighting test in the README
authored
42 ```javascript
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
43 var mongoose = require('mongoose')
44 , Schema = mongoose.Schema
45 , mongooseAuth = require('mongoose-auth');
46
47 var UserSchema = new Schema({});
c437169 @bnoguchi A bunch of commits, working with everyauth and cleaning up old API
authored
48 UserSchema.plugin(mongooseAuth, {
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
49 facebook: true
50 });
4a9ad2d @bnoguchi JS syntax highlighting test in the README
authored
51 ```
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
52
8604ca5 @bnoguchi Updated README
authored
53 ## Beyond Schema Decoration: Routing
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
54
55 Applications require more than just User Schema augmentation in order
56 to implement a complete authorization strategy. Applications also need
57 routes exposing the one or more steps involved for a given authorization
58 strategy. Moreover, applications each handle in their own unique way how
59 they want to respond to successful or failed logins (in addition to logout
60 handling). If you are not using a
bc16cdc @bnoguchi More README formatting
authored
61 [connect](https://github.com/senchalabs/connect), then you will have to
4c16178 @bnoguchi Removed the connect-auth and routes for facebook module, since that n…
authored
62 set all of this up yourself. In this case, mongoose-auth *only* provides
4fa454c @bnoguchi Modified the README to take into account my modified thinking around …
authored
63 you with Schema decoration.
64
65 But, if you are building your app on top of
bc16cdc @bnoguchi More README formatting
authored
66 [connect](https://github.com/senchalabs/connect), then mongoose-auth
8604ca5 @bnoguchi Updated README
authored
67 provides drop in solutions for you. Here is how you can get access
430b29a @bnoguchi Improved the README
authored
68 to the routing that mongoose-auth provides. Not the "STEP X: ..."
69 comments:
8604ca5 @bnoguchi Updated README
authored
70
5198fde @bnoguchi Full JS syntax highlighting in README
authored
71 ```javascript
8604ca5 @bnoguchi Updated README
authored
72 var mongoose = require('mongoose')
73 , Schema = mongoose.Schema
74 , mongooseAuth = require('mongoose-auth');
75
76 var UserSchema = new Schema({})
77 , User;
430b29a @bnoguchi Improved the README
authored
78
79 // STEP 1: Schema Decoration and Configuration for the Routing
8604ca5 @bnoguchi Updated README
authored
80 UserSchema.plugin(mongooseAuth, {
1c640bd @bnoguchi Updated README to reflect new req.user and user helpers
authored
81 // Here, we attach your User model to every module
82 everymodule: {
83 everyauth: {
84 User: function () {
85 return User;
86 }
87 }
88 }
89
90 , facebook: {
8604ca5 @bnoguchi Updated README
authored
91 everyauth: {
92 myHostname: 'http://localhost:3000'
430b29a @bnoguchi Improved the README
authored
93 , appId: 'YOUR APP ID HERE'
94 , appSecret: 'YOUR APP SECRET HERE'
8604ca5 @bnoguchi Updated README
authored
95 , redirectPath: '/'
96 }
97 }
98 });
430b29a @bnoguchi Improved the README
authored
99
8604ca5 @bnoguchi Updated README
authored
100 mongoose.model('User', UserSchema);
101
102 mongoose.connect('mongodb://localhost/example');
103
104 User = mongoose.model('User');
105
106 var app = express.createServer(
107 express.bodyParser()
108 , express.static(__dirname + "/public")
109 , express.cookieParser()
110 , express.session({ secret: 'esoognom'})
430b29a @bnoguchi Improved the README
authored
111
112 // STEP 2: Add in the Routing
8604ca5 @bnoguchi Updated README
authored
113 , mongooseAuth.middleware()
8c960d3 @bnoguchi Added note to express integration instructions so people avoid using …
authored
114
115 // IMPORTANT!!!!!!! Do not add app.router, to your middleware chain
116 // explicitly, or you will run into problems accessing `req.user`
117 // i.e., do not use app.use(app.router). Let express do this for you
118 // automatically for you upon your first app.get or app.post.
8604ca5 @bnoguchi Updated README
authored
119 );
430b29a @bnoguchi Improved the README
authored
120
f63326e @bnoguchi Updated the README to include information about (1) ServerRequest con…
authored
121 // STEP 3: Add in Dynamic View Helpers (only if you are using express)
8604ca5 @bnoguchi Updated README
authored
122 mongooseAuth.helpExpress(app);
123
124 app.listen(3000);
5198fde @bnoguchi Full JS syntax highlighting in README
authored
125 ```
8604ca5 @bnoguchi Updated README
authored
126
f63326e @bnoguchi Updated the README to include information about (1) ServerRequest con…
authored
127 ## View Helpers and Convenience Methods & Getters
128
129 In "Step 3" of the last code sample, we are adding dynamic view helpers, for if
130 you are using the [Express](https://github.com/visionmedia/express) web framework.
131 This automically gives you access to a convenient `everyauth` local variable from
132 your view, so you do not have to pass `req` as a local to your view:
133
134 - `everyauth.loggedIn` - a Boolean getter that tells you if the request is by a logged in user
1c640bd @bnoguchi Updated README to reflect new req.user and user helpers
authored
135 - `everyauth.user` - the mongoose User document associated with the session
f63326e @bnoguchi Updated the README to include information about (1) ServerRequest con…
authored
136 - `everyauth.facebook` - The is equivalent to what is stored at `req.session.auth.facebook`,
137 so you can do things like ...
138 - `everyauth.facebook.user` - returns the user json provided from the OAuth provider.
139 - `everyauth.facebook.accessToken` - returns the access_token provided from the OAuth provider
140 for authorized API calls on behalf of the user.
141 - And you also get this pattern for other modules - e.g., `everyauth.twitter.user`,
142 `everyauth.github.user`, etc.
143
1c640bd @bnoguchi Updated README to reflect new req.user and user helpers
authored
144 You also get access to the view helper
145
146 - `user` - the same as `everyauth.user` above
147
148 As an example of how you would use these, consider the following `./views/user.jade` jade template:
149
150 .user-id
151 .label User Id
152 .value #{user.id}
153 .facebook-id
154 .label User Facebook Id
155 .value #{everyauth.facebook.user.id}
156
f63326e @bnoguchi Updated the README to include information about (1) ServerRequest con…
authored
157 The "STEP 2: Add in the Routing" step in the last code sample also provides convenience methods on the
158 `ServerRequest` instance `req`. From any scope that has access to `req`, you get the following
159 convenience getter and method:
160
161 - `req.loggedIn` - a Boolean getter that tells you if the request is by a logged in user
1c640bd @bnoguchi Updated README to reflect new req.user and user helpers
authored
162 - `req.user` - the mongoose User document associated with the session
f63326e @bnoguchi Updated the README to include information about (1) ServerRequest con…
authored
163 - `req.logout()` - clears the sesion of your auth data
164
8604ca5 @bnoguchi Updated README
authored
165 ## Using Multiple Authorization Strategies at Once
166
167 You can also use multiple authorization strategies in the same application.
168 Here is an example, using 5 authorization strategies:
169
5198fde @bnoguchi Full JS syntax highlighting in README
authored
170 ```javascript
8604ca5 @bnoguchi Updated README
authored
171 // A configuration file for holding all of your
172 // 3rd party OAuth credentials
173 var conf = require('./conf');
174 UserSchema.plugin(mongooseAuth, {
1c640bd @bnoguchi Updated README to reflect new req.user and user helpers
authored
175 // Here, we attach your User model to every module
176 everymodule: {
177 everyauth: {
178 User: function () {
179 return User;
180 }
181 }
182 }
183 , facebook: {
8604ca5 @bnoguchi Updated README
authored
184 everyauth: {
185 myHostname: 'http://localhost:3000'
186 , appId: conf.fb.appId
187 , appSecret: conf.fb.appSecret
188 , redirectPath: '/'
189 }
190 }
191 , twitter: {
192 everyauth: {
193 myHostname: 'http://localhost:3000'
194 , consumerKey: conf.twit.consumerKey
195 , consumerSecret: conf.twit.consumerSecret
196 , redirectPath: '/'
197 }
198 }
199 , password: {
200 everyauth: {
201 getLoginPath: '/login'
202 , postLoginPath: '/login'
203 , loginView: 'login.jade'
204 , getRegisterPath: '/register'
205 , postRegisterPath: '/register'
206 , registerView: 'register.jade'
c378fb7 @bnoguchi Upgraded to new everyauth's Promise changes.
authored
207 , loginSuccessRedirect: '/'
208 , registerSuccessRedirect: '/'
8604ca5 @bnoguchi Updated README
authored
209 }
210 }
211 , github: {
212 everyauth: {
213 myHostname: 'http://localhost:3000'
214 , appId: conf.github.appId
215 , appSecret: conf.github.appSecret
216 , redirectPath: '/'
217 }
218 }
219 , instagram: {
220 everyauth: {
221 myHostname: 'http://localhost:3000'
222 , appId: conf.instagram.clientId
223 , appSecret: conf.instagram.clientSecret
224 , redirectPath: '/'
225 }
226 }
227 });
5198fde @bnoguchi Full JS syntax highlighting in README
authored
228 ```
e0e5163 @bnoguchi Added instructions for running the example to the README.
authored
229
230 ## Example
231
232 There is an example app located in [./example](https://github.com/bnoguchi/mongoose-auth/tree/master/example).
233 To run it:
234
235 $ cd example
236 $ node server.js
237
ee2bb5f @bnoguchi Updated formatting of README.
authored
238 Then navigate to [http://localhost:3000/](http://localhost:3000)
e0e5163 @bnoguchi Added instructions for running the example to the README.
authored
239
86865bf @bnoguchi Updated README to include information about configuring login to be '…
authored
240 ## Recipe 1: Linking Multiple Account Logins Together
64ccf86 @bnoguchi Updated README to include recipe for linking multiple logins under th…
authored
241
242 A common recipe is allowing a user to login via multiple accounts *and* to link those accounts under one user
243 document.
244
245 This can be done in the following way:
246
247 The real magic lies with https://github.com/bnoguchi/everyauth/, and it should be more obvious once
248 I document everyauth more and document mongoose-auth's relationship to everyauth.
249
250 In `everyauth`'s design, every auth module is defined as a set of steps, which are exposed in such a way for
251 you to over-ride. The step that is of particular interest for this scenario is the `findOrCreateUser` step
252 required by every `everyauth` module. `mongoose-auth` defines a default version of this `findOrCreateUser`
253 step for each `everyauth` auth module it supports (You can find these default definitions in
254 "lib/modules/#{moduleName}/everyauth.js" -- e.g., see
255 [.lib/modules/facebook/everyauth.js](https://github.com/bnoguchi/mongoose-auth/tree/master/lib/modules/facebook/everyauth.js)).
256
257 So for example, this is how you would over-ride the default `findOrCreateUser` step for the
258 facebook module if you are using both the facebook and password module:
259
260 ```javascript
261 UserSchema.plugin(mongooseAuth, {
262 facebook: {
263 everyauth: {
264 myHostname: ...
265 , ...
266 , findOrCreateUser: function (session, accessTok, accessTokExtra, fbUser) {
c378fb7 @bnoguchi Upgraded to new everyauth's Promise changes.
authored
267 var promise = this.Promise()
64ccf86 @bnoguchi Updated README to include recipe for linking multiple logins under th…
authored
268 , User = this.User()();
269 User.findById(session.auth.userId, function (err, user) {
270 if (err) return promise.fail(err);
271 if (!user) {
272 User.where('password.login', fbUser.email).findOne( function (err, user) {
273 if (err) return promise.fail(err);
274 if (!user) {
275 User.createWithFB(fbUser, accessTok, accessTokExtra.expires, function (err, createdUser) {
276 if (err) return promise.fail(err);
277 return promise.fulfill(createdUser);
278 });
279 } else {
280 assignFbDataToUser(user, accessTok, accessTokExtra, fbUser);
281 user.save( function (err, user) {
282 if (err) return promise.fail(err);
283 promise.fulfill(user);
284 });
285 }
286 });
287 } else {
288 assignFbDataToUser(user, accessTok, accessTokExtra, fbUser);
289
290 // Save the new data to the user doc in the db
291 user.save( function (err, user) {
292 if (err) return promise.fail(err);
293 promise.fuilfill(user);
294 });
295 }
296 });
297 });
298 return promise; // Make sure to return the promise that promises the user
299 }
300 }
301 });
302
303 // Assign all properties - see lib/modules/facebook/schema.js for details
304 function assignFbDataToUser (user, accessTok, accessTokExtra, fbUser) {
305 user.fb.accessToken = accessTok;
306 user.fb.expires = accessTokExtra.expires;
307 user.fb.id = fbUser.id;
308 user.fb.name.first = fbUser.first_name;
309 // etc. more assigning...
310 }
311 ```
312
6e35842 @bnoguchi README formatting
authored
313 As this is a common recipe, I plan on adding support for this into `everyauth` and `mongoose-auth`, so it's more drop-in, and developers do not have to add this custom code themselves. The intent is for common things like this to be invisible to the developer, so it just *works* *like* *magic*. So, in the near future, you won't have to over-ride the findOrCreateUser step every time you want this feature. This will be coming soon.
64ccf86 @bnoguchi Updated README to include recipe for linking multiple logins under th…
authored
314
86865bf @bnoguchi Updated README to include information about configuring login to be '…
authored
315 ## Recipe 2: Configuring Email or Phone to be your Login for the Password Module
316
317 By default, `everyauth` and therefore `mongoose-auth` use the attribute `login` as the default attribute used for logging in
318 with the password module.
319
320 However, the need can arise to use a different attribute (such as email) that implies a different schema (use `email: String` instead of `login: String`)
321 in addition to different validation assumptions (email validations are more strict that login handle validations).
322
323 Luckily, `mongoose-auth` provide support for this out of the box. All you need to do is (look for the line labeled "THIS NEXT LINE IS THE ONLY ADDITION"):
324
325 ```javascript
326 UserSchema.plugin(mongooseAuth, {
327 // Here, we attach your User model to every module
328 everymodule: {
329 everyauth: {
330 User: function () {
331 return User;
332 }
333 }
334 }
335 , password: {
336 // THIS NEXT LINE IS THE ONLY ADDITION
337 loginWith: 'email' // Or loginWith: 'phone'
338
339 , everyauth: {
340 getLoginPath: '/login'
341 , postLoginPath: '/login'
342 , loginView: 'login.jade'
343 , getRegisterPath: '/register'
344 , postRegisterPath: '/register'
345 , registerView: 'register.jade'
346 , loginSuccessRedirect: '/'
347 , registerSuccessRedirect: '/'
348 }
349 }
350 });
351 ```
3abe37c @bnoguchi Added in `extraParams` support to mongoose-auth
authored
352
86865bf @bnoguchi Updated README to include information about configuring login to be '…
authored
353 Automatically, `mongoose-auth` will use an `email` String attribute in your User schema
354 instead of the default `login` String attribute. Moreover, it will automatically add in
355 validation checks to make sure that the email is valid before registering a user through
356 the registration process of the password module.
357
6cc0c20 @bnoguchi README formatting
authored
358 ## Recipe 3: Extra password registration data besides login + password
3abe37c @bnoguchi Added in `extraParams` support to mongoose-auth
authored
359
360 Sometimes your registration will ask for more information from the user besides the login and password.
361
af71156 @bnoguchi Fixed mistake in README wrt specifying extraParams for the password m…
authored
362 For this particular scenario, you can configure `extraParams`.
3abe37c @bnoguchi Added in `extraParams` support to mongoose-auth
authored
363
364 ```javascript
365 UserSchema.plugin(mongooseAuth, {
366 // Here, we attach your User model to every module
367 everymodule: {
368 everyauth: {
369 User: function () {
370 return User;
371 }
372 }
373 }
374 , password: {
375 extraParams: {
376 phone: String
377 , name: {
378 first: String
379 , last: String
380 }
381 }
382
383 , everyauth: {
384 getLoginPath: '/login'
385 , postLoginPath: '/login'
386 , loginView: 'login.jade'
387 , getRegisterPath: '/register'
388 , postRegisterPath: '/register'
389 , registerView: 'register.jade'
390 , loginSuccessRedirect: '/'
391 , registerSuccessRedirect: '/'
392 }
393 }
394 });
395 ```
64ccf86 @bnoguchi Updated README to include recipe for linking multiple logins under th…
authored
396
af71156 @bnoguchi Fixed mistake in README wrt specifying extraParams for the password m…
authored
397 What this effectively does is:
398
399 1. Adds `phone`, `name.first`, and `name.last` as attributes to your `UserSchema`.
400 2. Automatically extracts the registration parameters after a visitor submits the registration
401 form and saves them to a new `User` document.
402 The registration form `<input>` `name`s should be, e.g., in the example above: 'phone',
403 'name[first]', and 'name[last]'.
404
405 Please see [./example/server.js](https://github.com/bnoguchi/mongoose-auth/tree/master/example/server.js#L45)
406 for a live example.
407
fde6ab1 @bnoguchi Updated README to include information about adding custom attributes …
authored
408 ## Recipe 4: Adding more attributes to your schema
409
410 This one ha come up enough that it is here as a recipe, even though it is not specific to `mongoose-auth`. Suppose
411 you want to add a special attribute such as `roles: [String]` to your UserSchema. This is something that you can do
412 using just `mongoose`
413
414 ```javascript
415 var UserSchema = new mongoose.Schema({
416 roles: [String]
417 , // other custom attributes
418 });
419
420 UserSchema.plugin(mongooseAuth, {
421 // mongooseAuth *adds* other attributes to your UserSchema
422 // depending on the auth modules you choose.
423 });
424 ```
425
6721fa3 @bnoguchi Added new Recipe for modifying password login authentication
authored
426 ## Recipe 5: Customizing how you do password login authentication
427
428 Currently, `mongoose-auth` does password authentication by login and password. Suppose you also want to authenticate
429 by checking against an additional parameter, like `active`, which is a Boolean attribute on your UserSchema that
430 indicates whether this user has been activated or not. Then you can modify the `authenticate` everyauth step in the
431 following way:
432
433 ```javascript
434 var UserSchema = new Schema({
435 active: Boolean
436 }), User;
437 UserSchema.plugin(mongooseAuth, {
438 everymodule: {
439 everyauth: {
440 User: function () {
441 return User;
442 }
443 }
444 }
445 , password: {
446 loginWith: 'email'
447 , everyauth: {
448 getLoginPath: '/login'
449 , postLoginPath: '/login'
450 , loginView: 'login.jade'
451 , getRegisterPath: '/register'
452 , postRegisterPath: '/register'
453 , registerView: 'register.jade'
454 , loginSuccessRedirect: '/'
455 , registerSuccessRedirect: '/'
456
457 // WHAT YOU ADD IS THE FOLLOWING:
458 // The logic is adapted from the default authenticate
459 // implementation in lib/modules/password/everyauth.js
460 , authenticate: function (login, password) {
461 var promise
462 , errors = [];
463 if (!login) errors.push('Missing login.');
464 if (!password) errors.push('Missing password.');
465 if (errors.length) return errors;
466
467 promise = this.Promise();
468 this.User()().authenticate(login, password, function (err, user) {
469 if (err) {
470 errors.push(err.message || err);
471 return promise.fulfill(errors);
472 }
473 if (!user) {
474 errors.push('Failed login.');
475 return promise.fulfill(errors);
476 }
477
478 // The following block is the new code
479 if (!user.active) {
480 errors.push('You are not yet activated.');
481 return promise.fulfill(errors);
482 }
483
484 promise.fulfill(user);
485 });
486 return promise;
487 }
488 }
489 }
490 });
491 mongoose.model('User', UserSchema);
492
493 User = mongoose.model('User');
494 ```
495
02e8127 @niftylettuce Added recipe for customizing logout handler
niftylettuce authored
496 ## Recipe 6: Customizing logout handler
497
498 This is a copy of instructions from `everyauth` and applied to `mongoose-auth`:
499
500 ```javascript
501 // ...
502 UserSchema.plugin(mongooseAuth, {
503 everymodule: {
504 everyauth: {
505 User: function () {
506 return User;
507 },
508 handleLogout: function(req, res) {
509 // Put your extra logic here
510 req.logout(); // The logout method is added for you by everyauth, too
511 // And/or put your extra logic here
512 res.writeHead(303, { 'Location': this.logoutRedirectPath() });
513 res.end();
514 }
515 }
516 }
517 // ...
518 });
519 // ...
520 ```
521
522
523
e0e5163 @bnoguchi Added instructions for running the example to the README.
authored
524 ### License
525 MIT License
526
527 ---
528 ### Author
529 Brian Noguchi
Something went wrong with that request. Please try again.