├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ └── node.yml ├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── SPONSORS.md ├── etc └── jsdoc.json ├── lib ├── authenticator.js ├── errors │ └── authenticationerror.js ├── framework │ └── connect.js ├── http │ └── request.js ├── index.js ├── middleware │ ├── authenticate.js │ └── initialize.js ├── sessionmanager.js └── strategies │ └── session.js ├── package.json ├── sponsors ├── auth0-dark.png ├── auth0.png ├── descope-dark.svg ├── descope.svg ├── fusionauth.png ├── fusionauth.svg ├── loginradius.png ├── snyk.png ├── stytch-dark.png ├── stytch.png └── workos.png └── test ├── authenticator.framework.test.js ├── authenticator.middleware.test.js ├── authenticator.test.js ├── bootstrap └── node.js ├── http └── request.test.js ├── middleware ├── authenticate.error.callback.test.js ├── authenticate.error.test.js ├── authenticate.fail.callback.test.js ├── authenticate.fail.flash.test.js ├── authenticate.fail.message.test.js ├── authenticate.fail.multi.test.js ├── authenticate.fail.test.js ├── authenticate.pass.test.js ├── authenticate.redirect.test.js ├── authenticate.success.callback.test.js ├── authenticate.success.flash.test.js ├── authenticate.success.info.test.js ├── authenticate.success.message.test.js ├── authenticate.success.multi.test.js ├── authenticate.success.test.js ├── authenticate.test.js └── initialize.test.js ├── package.test.js └── strategies ├── session.pause.test.js └── session.test.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: jaredhanson 2 | patreon: jaredhanson 3 | ko_fi: jaredhanson 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ** READ THIS FIRST! ** 2 | 3 | #### Are you looking for help? 4 | 5 | Reminder: The issue tracker is not a support forum. 6 | 7 | Issues should only be filed in this project once they are able to be reproduced 8 | and confirmed as a flaw in the software or incorrect information in associated 9 | documention. 10 | 11 | If you are encountering problems integrating this module into your application, 12 | please post a question on the [discussion forum](https://github.com/passport/discuss) 13 | rather than filing an issue. 14 | 15 | #### Is this a security issue? 16 | 17 | Do not open issues that might have security implications. Potential security 18 | vulnerabilities should be reported privately to jaredhanson@gmail.com. Once any 19 | vulerabilities have been repaired, the details will be disclosed publicly in a 20 | responsible manner. This also allows time for coordinating with affected parties 21 | in order to mitigate negative consequences. 22 | 23 | 24 | If neither of the above two scenarios apply to your situation, you should open 25 | an issue. Delete this paragraph and the text above, and fill in the information 26 | requested below. 27 | 28 | 29 | 30 | 31 | 32 | 33 | ### Expected behavior 34 | 35 | 36 | 37 | ### Actual behavior 38 | 39 | 40 | 41 | ### Steps to reproduce 42 | 43 | 44 | 45 | ```js 46 | // Format code using Markdown code blocks 47 | ``` 48 | 49 | ### Environment 50 | 51 | * Operating System: 52 | * Node version: 53 | * passport version: 54 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ** READ THIS FIRST! ** 2 | 3 | #### Are you implementing a new feature? 4 | 5 | Requests for new features should first be discussed on the [developer forum](https://github.com/passport/develop). 6 | This allows the community to gather feedback and assess whether or not there is 7 | an existing way to achieve the desired functionality. 8 | 9 | If it is determined that a new feature needs to be implemented, include a link 10 | to the relevant discussion along with the pull request. 11 | 12 | #### Is this a security patch? 13 | 14 | Do not open pull requests that might have security implications. Potential 15 | security vulnerabilities should be reported privately to jaredhanson@gmail.com. 16 | Once any vulerabilities have been repaired, the details will be disclosed 17 | publicly in a responsible manner. This also allows time for coordinating with 18 | affected parties in order to mitigate negative consequences. 19 | 20 | 21 | If neither of the above two scenarios apply to your situation, you should open 22 | a pull request. Delete this paragraph and the text above, and fill in the 23 | information requested below. 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | ### Checklist 33 | 34 | 35 | 36 | 37 | - [ ] I have read the [CONTRIBUTING](https://github.com/jaredhanson/passport/blob/master/CONTRIBUTING.md) guidelines. 38 | - [ ] I have added test cases which verify the correct operation of this feature or patch. 39 | - [ ] I have added documentation pertaining to this feature or patch. 40 | - [ ] The automated test suite (`$ make test`) executes successfully. 41 | - [ ] The automated code linting (`$ make lint`) executes successfully. 42 | -------------------------------------------------------------------------------- /.github/workflows/node.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: 20 | - '17' 21 | - '16' 22 | - '14' 23 | - '12' 24 | - '10' 25 | - '8' 26 | - '6' 27 | - '4' 28 | # - '3' # io.js 29 | # - '2' # io.js 30 | # - '1' # io.js 31 | - '0.12' 32 | - '0.10' 33 | # - '0.8' 34 | 35 | steps: 36 | - uses: actions/checkout@v2 37 | - name: Use Node.js ${{ matrix.node-version }} 38 | uses: actions/setup-node@v2 39 | with: 40 | node-version: ${{ matrix.node-version }} 41 | - run: npm install 42 | - run: npm test 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docs/ 2 | reports/ 3 | 4 | # Mac OS X 5 | .DS_Store 6 | 7 | # Node.js 8 | node_modules 9 | npm-debug.log 10 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "bitwise": true, 4 | "camelcase": true, 5 | "curly": true, 6 | "forin": true, 7 | "immed": true, 8 | "latedef": true, 9 | "newcap": true, 10 | "noarg": true, 11 | "noempty": true, 12 | "nonew": true, 13 | "quotmark": "single", 14 | "undef": true, 15 | "unused": true, 16 | "trailing": true, 17 | "laxcomma": true 18 | } 19 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | CONTRIBUTING.md 2 | Makefile 3 | SPONSORS.md 4 | docs/ 5 | examples/ 6 | reports/ 7 | test/ 8 | 9 | .github/ 10 | .jshintrc 11 | .travis.yml 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: "node_js" 2 | node_js: 3 | - "13" 4 | - "12" 5 | - "11" 6 | - "10" 7 | - "9" 8 | - "8" 9 | - "7" 10 | - "6" 11 | - "5" 12 | - "4" 13 | - "3" # io.js 14 | - "2" # io.js 15 | - "1" # io.js 16 | - "0.12" 17 | - "0.10" 18 | # - "0.8" 19 | 20 | 21 | before_install: 22 | - "npm install make-node@0.3.x -g" 23 | - "preinstall-compat" 24 | 25 | script: 26 | - "make test-cov" 27 | 28 | after_success: 29 | - "make report-cov" 30 | 31 | sudo: false 32 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [Unreleased] 8 | 9 | ## [0.7.0] - 2023-11-27 10 | ### Changed 11 | - Set `req.authInfo` by default when using the `assignProperty` option to 12 | `authenticate()` middleware. This makes the behavior the same as when not using 13 | the option, and can be disabled by setting `authInfo` option to `false`. 14 | 15 | ## [0.6.0] - 2022-05-20 16 | ### Added 17 | - `authenticate()`, `req#login`, and `req#logout` accept a 18 | `keepSessionInfo: true` option to keep session information after regenerating 19 | the session. 20 | 21 | ### Changed 22 | 23 | - `req#login()` and `req#logout()` regenerate the the session and clear session 24 | information by default. 25 | - `req#logout()` is now an asynchronous function and requires a callback 26 | function as the last argument. 27 | 28 | ### Security 29 | 30 | - Improved robustness against session fixation attacks in cases where there is 31 | physical access to the same system or the application is susceptible to 32 | cross-site scripting (XSS). 33 | 34 | ## [0.5.3] - 2022-05-16 35 | ### Fixed 36 | 37 | - `initialize()` middleware extends request with `login()`, `logIn()`, 38 | `logout()`, `logOut()`, `isAuthenticated()`, and `isUnauthenticated()` functions 39 | again, reverting change from 0.5.1. 40 | 41 | ## [0.5.2] - 2021-12-16 42 | ### Fixed 43 | - Introduced a compatibility layer for strategies that depend directly on 44 | `passport@0.4.x` or earlier (such as `passport-azure-ad`), which were 45 | broken by the removal of private variables in `passport@0.5.1`. 46 | 47 | ## [0.5.1] - 2021-12-15 48 | ### Added 49 | - Informative error message in session strategy if session support is not 50 | available. 51 | 52 | ### Changed 53 | 54 | - `authenticate()` middleware, rather than `initialize()` middleware, extends 55 | request with `login()`, `logIn()`, `logout()`, `logOut()`, `isAuthenticated()`, 56 | and `isUnauthenticated()` functions. 57 | 58 | ## [0.5.0] - 2021-09-23 59 | ### Changed 60 | 61 | - `initialize()` middleware extends request with `login()`, `logIn()`, 62 | `logout()`, `logOut()`, `isAuthenticated()`, and `isUnauthenticated()` 63 | functions. 64 | 65 | ### Removed 66 | 67 | - `login()`, `logIn()`, `logout()`, `logOut()`, `isAuthenticated()`, and 68 | `isUnauthenticated()` functions no longer added to `http.IncomingMessage.prototype`. 69 | 70 | ### Fixed 71 | 72 | - `userProperty` option to `initialize()` middleware only affects the current 73 | request, rather than all requests processed via singleton Passport instance, 74 | eliminating a race condition in situations where `initialize()` middleware is 75 | used multiple times in an application with `userProperty` set to different 76 | values. 77 | 78 | [Unreleased]: https://github.com/jaredhanson/passport/compare/v0.6.0...HEAD 79 | [0.6.0]: https://github.com/jaredhanson/passport/compare/v0.5.3...v0.6.0 80 | [0.5.3]: https://github.com/jaredhanson/passport/compare/v0.5.2...v0.5.3 81 | [0.5.2]: https://github.com/jaredhanson/passport/compare/v0.5.1...v0.5.2 82 | [0.5.1]: https://github.com/jaredhanson/passport/compare/v0.5.0...v0.5.1 83 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | ### Tests 4 | 5 | The test suite is located in the `test/` directory. All new features are 6 | expected to have corresponding test cases with complete code coverage. Patches 7 | that increase test coverage are happily accepted. 8 | 9 | Ensure that the test suite passes by executing: 10 | 11 | ```bash 12 | $ make test 13 | ``` 14 | 15 | Coverage reports can be generated and viewed by executing: 16 | 17 | ```bash 18 | $ make test-cov 19 | $ make view-cov 20 | ``` 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2011-2021 Jared Hanson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include node_modules/make-node/main.mk 2 | 3 | 4 | SOURCES = lib/*.js lib/**/*.js 5 | TESTS = test/*.test.js test/**/*.test.js 6 | 7 | LCOVFILE = ./reports/coverage/lcov.info 8 | 9 | MOCHAFLAGS = --require ./test/bootstrap/node 10 | 11 | 12 | view-docs: 13 | open ./docs/index.html 14 | 15 | view-cov: 16 | open ./reports/coverage/lcov-report/index.html 17 | 18 | clean: clean-docs clean-cov 19 | -rm -r $(REPORTSDIR) 20 | 21 | clobber: clean 22 | -rm -r node_modules 23 | 24 | html: 25 | jsdoc -c etc/jsdoc.json -d ./doc $(SOURCES) 26 | 27 | 28 | .PHONY: clean clobber 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![passport banner](http://cdn.auth0.com/img/passport-banner-github.png)](http://passportjs.org) 2 | 3 | # Passport 4 | 5 | Passport is [Express](http://expressjs.com/)-compatible authentication 6 | middleware for [Node.js](http://nodejs.org/). 7 | 8 | Passport's sole purpose is to authenticate requests, which it does through an 9 | extensible set of plugins known as _strategies_. Passport does not mount 10 | routes or assume any particular database schema, which maximizes flexibility and 11 | allows application-level decisions to be made by the developer. The API is 12 | simple: you provide Passport a request to authenticate, and Passport provides 13 | hooks for controlling what occurs when authentication succeeds or fails. 14 | 15 | --- 16 | 17 |
18 | Sponsors 19 |
20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |

29 | Simple Authentication 30 |
31 | Make login our problem. Not yours. 32 |

33 |
34 |

Auth0 by Okta provides a simple and customizable login page to authenticate your users. You can dynamically add new capabilities to it - including social login, multi-factor authentication, or passkeys - without making changes to your app’s code.

35 |

We help protect your app and your users from attacks - defending your application from bot attacks and detecting runtime anomalies based on suspicious IPs, breached credentials, user context, and more.

36 |
37 |
38 | 39 |
40 | 41 | 42 |

43 | Your app, enterprise-ready. 44 |
45 | Start selling to enterprise customers with just a few lines of code. Add Single Sign-On (and more) in minutes instead of months. 46 |

47 |
48 |
49 |
50 | 51 |
52 | 53 | 54 | 55 | 56 | 57 | 58 |

59 | Drag and drop your auth 60 |
61 | Add authentication and user management to your consumer and business apps with a few lines of code. 62 |

63 |
64 |
65 |
66 | 67 |
68 | 69 | 70 |

71 | Auth. Built for Devs, by Devs 72 |
73 | Add login, registration, SSO, MFA, and a bazillion other features to your app in minutes. Integrates with any codebase and installs on any server, anywhere in the world. 74 |

75 |
76 |
77 |
78 | 79 |
80 | 81 | 82 | 83 | 84 | 85 | 86 |

87 | API-first AuthN, AuthZ, and Fraud Prevention 88 |
89 | The most powerful identity platform built for developers. Easily build and secure a modern auth flow with user & org management, multi-tenant SSO, MFA, RBAC, device fingerprinting, and more. 90 |

91 |
92 |
93 |
94 | 95 | --- 96 | 97 | Status: 98 | [![Build](https://travis-ci.org/jaredhanson/passport.svg?branch=master)](https://travis-ci.org/jaredhanson/passport) 99 | [![Coverage](https://coveralls.io/repos/jaredhanson/passport/badge.svg?branch=master)](https://coveralls.io/r/jaredhanson/passport) 100 | [![Dependencies](https://david-dm.org/jaredhanson/passport.svg)](https://david-dm.org/jaredhanson/passport) 101 | 102 | 103 | ## Install 104 | 105 | ``` 106 | $ npm install passport 107 | ``` 108 | 109 | ## Usage 110 | 111 | #### Strategies 112 | 113 | Passport uses the concept of strategies to authenticate requests. Strategies 114 | can range from verifying username and password credentials, delegated 115 | authentication using [OAuth](http://oauth.net/) (for example, via [Facebook](http://www.facebook.com/) 116 | or [Twitter](http://twitter.com/)), or federated authentication using [OpenID](http://openid.net/). 117 | 118 | Before authenticating requests, the strategy (or strategies) used by an 119 | application must be configured. 120 | 121 | ```javascript 122 | passport.use(new LocalStrategy( 123 | function(username, password, done) { 124 | User.findOne({ username: username }, function (err, user) { 125 | if (err) { return done(err); } 126 | if (!user) { return done(null, false); } 127 | if (!user.verifyPassword(password)) { return done(null, false); } 128 | return done(null, user); 129 | }); 130 | } 131 | )); 132 | ``` 133 | 134 | There are 480+ strategies. Find the ones you want at: [passportjs.org](http://passportjs.org) 135 | 136 | #### Sessions 137 | 138 | Passport will maintain persistent login sessions. In order for persistent 139 | sessions to work, the authenticated user must be serialized to the session, and 140 | deserialized when subsequent requests are made. 141 | 142 | Passport does not impose any restrictions on how your user records are stored. 143 | Instead, you provide functions to Passport which implements the necessary 144 | serialization and deserialization logic. In a typical application, this will be 145 | as simple as serializing the user ID, and finding the user by ID when 146 | deserializing. 147 | 148 | ```javascript 149 | passport.serializeUser(function(user, done) { 150 | done(null, user.id); 151 | }); 152 | 153 | passport.deserializeUser(function(id, done) { 154 | User.findById(id, function (err, user) { 155 | done(err, user); 156 | }); 157 | }); 158 | ``` 159 | 160 | #### Middleware 161 | 162 | To use Passport in an [Express](http://expressjs.com/) or 163 | [Connect](http://senchalabs.github.com/connect/)-based application, configure it 164 | with the required `passport.initialize()` middleware. If your application uses 165 | persistent login sessions (recommended, but not required), `passport.session()` 166 | middleware must also be used. 167 | 168 | ```javascript 169 | var app = express(); 170 | app.use(require('serve-static')(__dirname + '/../../public')); 171 | app.use(require('cookie-parser')()); 172 | app.use(require('body-parser').urlencoded({ extended: true })); 173 | app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true })); 174 | app.use(passport.initialize()); 175 | app.use(passport.session()); 176 | ``` 177 | 178 | #### Authenticate Requests 179 | 180 | Passport provides an `authenticate()` function, which is used as route 181 | middleware to authenticate requests. 182 | 183 | ```javascript 184 | app.post('/login', 185 | passport.authenticate('local', { failureRedirect: '/login' }), 186 | function(req, res) { 187 | res.redirect('/'); 188 | }); 189 | ``` 190 | 191 | ## Strategies 192 | 193 | Passport has a comprehensive set of **over 480** authentication strategies 194 | covering social networking, enterprise integration, API services, and more. 195 | 196 | ## Search all strategies 197 | 198 | There is a **Strategy Search** at [passportjs.org](http://passportjs.org) 199 | 200 | The following table lists commonly used strategies: 201 | 202 | |Strategy | Protocol |Developer | 203 | |---------------------------------------------------------------|--------------------------|------------------------------------------------| 204 | |[Local](https://github.com/jaredhanson/passport-local) | HTML form |[Jared Hanson](https://github.com/jaredhanson) | 205 | |[OpenID](https://github.com/jaredhanson/passport-openid) | OpenID |[Jared Hanson](https://github.com/jaredhanson) | 206 | |[BrowserID](https://github.com/jaredhanson/passport-browserid) | BrowserID |[Jared Hanson](https://github.com/jaredhanson) | 207 | |[Facebook](https://github.com/jaredhanson/passport-facebook) | OAuth 2.0 |[Jared Hanson](https://github.com/jaredhanson) | 208 | |[Google](https://github.com/jaredhanson/passport-google) | OpenID |[Jared Hanson](https://github.com/jaredhanson) | 209 | |[Google](https://github.com/jaredhanson/passport-google-oauth) | OAuth / OAuth 2.0 |[Jared Hanson](https://github.com/jaredhanson) | 210 | |[Twitter](https://github.com/jaredhanson/passport-twitter) | OAuth |[Jared Hanson](https://github.com/jaredhanson) | 211 | |[Azure Active Directory](https://github.com/AzureAD/passport-azure-ad) | OAuth 2.0 / OpenID / SAML |[Azure](https://github.com/azuread) | 212 | 213 | ## Examples 214 | 215 | - For a complete, working example, refer to the [example](https://github.com/passport/express-4.x-local-example) 216 | that uses [passport-local](https://github.com/jaredhanson/passport-local). 217 | - **Local Strategy**: Refer to the following tutorials for setting up user authentication via LocalStrategy (`passport-local`): 218 | - Mongo 219 | - Express v3x - [Tutorial](http://mherman.org/blog/2016/09/25/node-passport-and-postgres/#.V-govpMrJE5) / [working example](https://github.com/mjhea0/passport-local-knex) 220 | - Express v4x - [Tutorial](http://mherman.org/blog/2015/01/31/local-authentication-with-passport-and-express-4/) / [working example](https://github.com/mjhea0/passport-local-express4) 221 | - Postgres 222 | - [Tutorial](http://mherman.org/blog/2015/01/31/local-authentication-with-passport-and-express-4/) / [working example](https://github.com/mjhea0/passport-local-express4) 223 | - **Social Authentication**: Refer to the following tutorials for setting up various social authentication strategies: 224 | - Express v3x - [Tutorial](http://mherman.org/blog/2013/11/10/social-authentication-with-passport-dot-js/) / [working example](https://github.com/mjhea0/passport-examples) 225 | - Express v4x - [Tutorial](http://mherman.org/blog/2015/09/26/social-authentication-in-node-dot-js-with-passport) / [working example](https://github.com/mjhea0/passport-social-auth) 226 | 227 | ## Related Modules 228 | 229 | - [Locomotive](https://github.com/jaredhanson/locomotive) — Powerful MVC web framework 230 | - [OAuthorize](https://github.com/jaredhanson/oauthorize) — OAuth service provider toolkit 231 | - [OAuth2orize](https://github.com/jaredhanson/oauth2orize) — OAuth 2.0 authorization server toolkit 232 | - [connect-ensure-login](https://github.com/jaredhanson/connect-ensure-login) — middleware to ensure login sessions 233 | 234 | The [modules](https://github.com/jaredhanson/passport/wiki/Modules) page on the 235 | [wiki](https://github.com/jaredhanson/passport/wiki) lists other useful modules 236 | that build upon or integrate with Passport. 237 | 238 | ## License 239 | 240 | [The MIT License](http://opensource.org/licenses/MIT) 241 | 242 | Copyright (c) 2011-2021 Jared Hanson <[https://www.jaredhanson.me/](https://www.jaredhanson.me/)> 243 | -------------------------------------------------------------------------------- /SPONSORS.md: -------------------------------------------------------------------------------- 1 | ## Gold Sponsors 2 | 3 | [![WorkOS](https://raw.githubusercontent.com/jaredhanson/passport/master/sponsors/workos.png)](https://workos.com/) 4 |
5 | [![Snyk](https://raw.githubusercontent.com/jaredhanson/passport/master/sponsors/snyk.png)](https://snyk.io/) 6 | 7 | ## Sponsors 8 | 9 | - [CodePilot.ai](https://codepilot.ai/) 10 | - [Jeremy Combs](https://github.com/jmcombs) 11 | - [Gadget](https://gadget.dev/) 12 | - Kelly Burke 13 | - [Matt Miller](https://mmiller.me/) 14 | 15 | ## Past Sponsors 16 | 17 | - [LoginRadius](https://www.loginradius.com/) 18 | -------------------------------------------------------------------------------- /etc/jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["plugins/markdown"] 3 | } 4 | -------------------------------------------------------------------------------- /lib/authenticator.js: -------------------------------------------------------------------------------- 1 | // Module dependencies. 2 | var SessionStrategy = require('./strategies/session') 3 | , SessionManager = require('./sessionmanager'); 4 | 5 | 6 | /** 7 | * Create a new `Authenticator` object. 8 | * 9 | * @public 10 | * @class 11 | */ 12 | function Authenticator() { 13 | this._key = 'passport'; 14 | this._strategies = {}; 15 | this._serializers = []; 16 | this._deserializers = []; 17 | this._infoTransformers = []; 18 | this._framework = null; 19 | 20 | this.init(); 21 | } 22 | 23 | /** 24 | * Initialize authenticator. 25 | * 26 | * Initializes the `Authenticator` instance by creating the default `{@link SessionManager}`, 27 | * {@link Authenticator#use `use()`}'ing the default `{@link SessionStrategy}`, and 28 | * adapting it to work as {@link https://github.com/senchalabs/connect#readme Connect}-style 29 | * middleware, which is also compatible with {@link https://expressjs.com/ Express}. 30 | * 31 | * @private 32 | */ 33 | Authenticator.prototype.init = function() { 34 | this.framework(require('./framework/connect')()); 35 | this.use(new SessionStrategy({ key: this._key }, this.deserializeUser.bind(this))); 36 | this._sm = new SessionManager({ key: this._key }, this.serializeUser.bind(this)); 37 | }; 38 | 39 | /** 40 | * Register a strategy for later use when authenticating requests. The name 41 | * with which the strategy is registered is passed to {@link Authenticator#authenticate `authenticate()`}. 42 | * 43 | * @public 44 | * @param {string} [name=strategy.name] - Name of the strategy. When specified, 45 | * this value overrides the strategy's name. 46 | * @param {Strategy} strategy - Authentication strategy. 47 | * @returns {this} 48 | * 49 | * @example Register strategy. 50 | * passport.use(new GoogleStrategy(...)); 51 | * 52 | * @example Register strategy and override name. 53 | * passport.use('password', new LocalStrategy(function(username, password, cb) { 54 | * // ... 55 | * })); 56 | */ 57 | Authenticator.prototype.use = function(name, strategy) { 58 | if (!strategy) { 59 | strategy = name; 60 | name = strategy.name; 61 | } 62 | if (!name) { throw new Error('Authentication strategies must have a name'); } 63 | 64 | this._strategies[name] = strategy; 65 | return this; 66 | }; 67 | 68 | /** 69 | * Deregister a strategy that was previously registered with the given name. 70 | * 71 | * In a typical application, the necessary authentication strategies are 72 | * registered when initializing the app and, once registered, are always 73 | * available. As such, it is typically not necessary to call this function. 74 | * 75 | * @public 76 | * @param {string} name - Name of the strategy. 77 | * @returns {this} 78 | * 79 | * @example 80 | * passport.unuse('acme'); 81 | */ 82 | Authenticator.prototype.unuse = function(name) { 83 | delete this._strategies[name]; 84 | return this; 85 | }; 86 | 87 | /** 88 | * Adapt this `Authenticator` to work with a specific framework. 89 | * 90 | * By default, Passport works as {@link https://github.com/senchalabs/connect#readme Connect}-style 91 | * middleware, which makes it compatible with {@link https://expressjs.com/ Express}. 92 | * For any app built using Express, there is no need to call this function. 93 | * 94 | * @public 95 | * @param {Object} fw 96 | * @returns {this} 97 | */ 98 | Authenticator.prototype.framework = function(fw) { 99 | this._framework = fw; 100 | return this; 101 | }; 102 | 103 | /** 104 | * Create initialization middleware. 105 | * 106 | * Returns middleware that initializes Passport to authenticate requests. 107 | * 108 | * As of v0.6.x, it is typically no longer necessary to use this middleware. It 109 | * exists for compatiblity with apps built using previous versions of Passport, 110 | * in which this middleware was necessary. 111 | * 112 | * The primary exception to the above guidance is when using strategies that 113 | * depend directly on `passport@0.4.x` or earlier. These earlier versions of 114 | * Passport monkeypatch Node.js `http.IncomingMessage` in a way that expects 115 | * certain Passport-specific properties to be available. This middleware 116 | * provides a compatibility layer for this situation. 117 | * 118 | * @public 119 | * @param {Object} [options] 120 | * @param {string} [options.userProperty='user'] - Determines what property on 121 | * `req` will be set to the authenticated user object. 122 | * @param {boolean} [options.compat=true] - When `true`, enables a compatibility 123 | * layer for packages that depend on `passport@0.4.x` or earlier. 124 | * @returns {function} 125 | * 126 | * @example 127 | * app.use(passport.initialize()); 128 | */ 129 | Authenticator.prototype.initialize = function(options) { 130 | options = options || {}; 131 | return this._framework.initialize(this, options); 132 | }; 133 | 134 | /** 135 | * Create authentication middleware. 136 | * 137 | * Returns middleware that authenticates the request by applying the given 138 | * strategy (or strategies). 139 | * 140 | * Examples: 141 | * 142 | * passport.authenticate('local', function(err, user) { 143 | * if (!user) { return res.redirect('/login'); } 144 | * res.end('Authenticated!'); 145 | * })(req, res); 146 | * 147 | * @public 148 | * @param {string|string[]|Strategy} strategy 149 | * @param {Object} [options] 150 | * @param {boolean} [options.session=true] 151 | * @param {boolean} [options.keepSessionInfo=false] 152 | * @param {string} [options.failureRedirect] 153 | * @param {boolean|string|Object} [options.failureFlash=false] 154 | * @param {boolean|string} [options.failureMessage=false] 155 | * @param {boolean|string|Object} [options.successFlash=false] 156 | * @param {string} [options.successReturnToOrRedirect] 157 | * @param {string} [options.successRedirect] 158 | * @param {boolean|string} [options.successMessage=false] 159 | * @param {boolean} [options.failWithError=false] 160 | * @param {string} [options.assignProperty] 161 | * @param {boolean} [options.authInfo=true] 162 | * @param {function} [callback] 163 | * @returns {function} 164 | * 165 | * @example Authenticate username and password submitted via HTML form. 166 | * app.get('/login/password', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' })); 167 | * 168 | * @example Authenticate bearer token used to access an API resource. 169 | * app.get('/api/resource', passport.authenticate('bearer', { session: false })); 170 | */ 171 | Authenticator.prototype.authenticate = function(strategy, options, callback) { 172 | return this._framework.authenticate(this, strategy, options, callback); 173 | }; 174 | 175 | /** 176 | * Create third-party service authorization middleware. 177 | * 178 | * Returns middleware that will authorize a connection to a third-party service. 179 | * 180 | * This middleware is identical to using {@link Authenticator#authenticate `authenticate()`} 181 | * middleware with the `assignProperty` option set to `'account'`. This is 182 | * useful when a user is already authenticated (for example, using a username 183 | * and password) and they want to connect their account with a third-party 184 | * service. 185 | * 186 | * In this scenario, the user's third-party account will be set at 187 | * `req.account`, and the existing `req.user` and login session data will be 188 | * be left unmodified. A route handler can then link the third-party account to 189 | * the existing local account. 190 | * 191 | * All arguments to this function behave identically to those accepted by 192 | * `{@link Authenticator#authenticate}`. 193 | * 194 | * @public 195 | * @param {string|string[]|Strategy} strategy 196 | * @param {Object} [options] 197 | * @param {function} [callback] 198 | * @returns {function} 199 | * 200 | * @example 201 | * app.get('/oauth/callback/twitter', passport.authorize('twitter')); 202 | */ 203 | Authenticator.prototype.authorize = function(strategy, options, callback) { 204 | options = options || {}; 205 | options.assignProperty = 'account'; 206 | 207 | var fn = this._framework.authorize || this._framework.authenticate; 208 | return fn(this, strategy, options, callback); 209 | }; 210 | 211 | /** 212 | * Middleware that will restore login state from a session. 213 | * 214 | * Web applications typically use sessions to maintain login state between 215 | * requests. For example, a user will authenticate by entering credentials into 216 | * a form which is submitted to the server. If the credentials are valid, a 217 | * login session is established by setting a cookie containing a session 218 | * identifier in the user's web browser. The web browser will send this cookie 219 | * in subsequent requests to the server, allowing a session to be maintained. 220 | * 221 | * If sessions are being utilized, and a login session has been established, 222 | * this middleware will populate `req.user` with the current user. 223 | * 224 | * Note that sessions are not strictly required for Passport to operate. 225 | * However, as a general rule, most web applications will make use of sessions. 226 | * An exception to this rule would be an API server, which expects each HTTP 227 | * request to provide credentials in an Authorization header. 228 | * 229 | * Examples: 230 | * 231 | * app.use(connect.cookieParser()); 232 | * app.use(connect.session({ secret: 'keyboard cat' })); 233 | * app.use(passport.initialize()); 234 | * app.use(passport.session()); 235 | * 236 | * Options: 237 | * - `pauseStream` Pause the request stream before deserializing the user 238 | * object from the session. Defaults to _false_. Should 239 | * be set to true in cases where middleware consuming the 240 | * request body is configured after passport and the 241 | * deserializeUser method is asynchronous. 242 | * 243 | * @param {Object} options 244 | * @return {Function} middleware 245 | * @api public 246 | */ 247 | Authenticator.prototype.session = function(options) { 248 | return this.authenticate('session', options); 249 | }; 250 | 251 | // TODO: Make session manager pluggable 252 | /* 253 | Authenticator.prototype.sessionManager = function(mgr) { 254 | this._sm = mgr; 255 | return this; 256 | } 257 | */ 258 | 259 | /** 260 | * Registers a function used to serialize user objects into the session. 261 | * 262 | * Examples: 263 | * 264 | * passport.serializeUser(function(user, done) { 265 | * done(null, user.id); 266 | * }); 267 | * 268 | * @api public 269 | */ 270 | Authenticator.prototype.serializeUser = function(fn, req, done) { 271 | if (typeof fn === 'function') { 272 | return this._serializers.push(fn); 273 | } 274 | 275 | // private implementation that traverses the chain of serializers, attempting 276 | // to serialize a user 277 | var user = fn; 278 | 279 | // For backwards compatibility 280 | if (typeof req === 'function') { 281 | done = req; 282 | req = undefined; 283 | } 284 | 285 | var stack = this._serializers; 286 | (function pass(i, err, obj) { 287 | // serializers use 'pass' as an error to skip processing 288 | if ('pass' === err) { 289 | err = undefined; 290 | } 291 | // an error or serialized object was obtained, done 292 | if (err || obj || obj === 0) { return done(err, obj); } 293 | 294 | var layer = stack[i]; 295 | if (!layer) { 296 | return done(new Error('Failed to serialize user into session')); 297 | } 298 | 299 | 300 | function serialized(e, o) { 301 | pass(i + 1, e, o); 302 | } 303 | 304 | try { 305 | var arity = layer.length; 306 | if (arity == 3) { 307 | layer(req, user, serialized); 308 | } else { 309 | layer(user, serialized); 310 | } 311 | } catch(e) { 312 | return done(e); 313 | } 314 | })(0); 315 | }; 316 | 317 | /** 318 | * Registers a function used to deserialize user objects out of the session. 319 | * 320 | * Examples: 321 | * 322 | * passport.deserializeUser(function(id, done) { 323 | * User.findById(id, function (err, user) { 324 | * done(err, user); 325 | * }); 326 | * }); 327 | * 328 | * @api public 329 | */ 330 | Authenticator.prototype.deserializeUser = function(fn, req, done) { 331 | if (typeof fn === 'function') { 332 | return this._deserializers.push(fn); 333 | } 334 | 335 | // private implementation that traverses the chain of deserializers, 336 | // attempting to deserialize a user 337 | var obj = fn; 338 | 339 | // For backwards compatibility 340 | if (typeof req === 'function') { 341 | done = req; 342 | req = undefined; 343 | } 344 | 345 | var stack = this._deserializers; 346 | (function pass(i, err, user) { 347 | // deserializers use 'pass' as an error to skip processing 348 | if ('pass' === err) { 349 | err = undefined; 350 | } 351 | // an error or deserialized user was obtained, done 352 | if (err || user) { return done(err, user); } 353 | // a valid user existed when establishing the session, but that user has 354 | // since been removed 355 | if (user === null || user === false) { return done(null, false); } 356 | 357 | var layer = stack[i]; 358 | if (!layer) { 359 | return done(new Error('Failed to deserialize user out of session')); 360 | } 361 | 362 | 363 | function deserialized(e, u) { 364 | pass(i + 1, e, u); 365 | } 366 | 367 | try { 368 | var arity = layer.length; 369 | if (arity == 3) { 370 | layer(req, obj, deserialized); 371 | } else { 372 | layer(obj, deserialized); 373 | } 374 | } catch(e) { 375 | return done(e); 376 | } 377 | })(0); 378 | }; 379 | 380 | /** 381 | * Registers a function used to transform auth info. 382 | * 383 | * In some circumstances authorization details are contained in authentication 384 | * credentials or loaded as part of verification. 385 | * 386 | * For example, when using bearer tokens for API authentication, the tokens may 387 | * encode (either directly or indirectly in a database), details such as scope 388 | * of access or the client to which the token was issued. 389 | * 390 | * Such authorization details should be enforced separately from authentication. 391 | * Because Passport deals only with the latter, this is the responsiblity of 392 | * middleware or routes further along the chain. However, it is not optimal to 393 | * decode the same data or execute the same database query later. To avoid 394 | * this, Passport accepts optional `info` along with the authenticated `user` 395 | * in a strategy's `success()` action. This info is set at `req.authInfo`, 396 | * where said later middlware or routes can access it. 397 | * 398 | * Optionally, applications can register transforms to proccess this info, 399 | * which take effect prior to `req.authInfo` being set. This is useful, for 400 | * example, when the info contains a client ID. The transform can load the 401 | * client from the database and include the instance in the transformed info, 402 | * allowing the full set of client properties to be convieniently accessed. 403 | * 404 | * If no transforms are registered, `info` supplied by the strategy will be left 405 | * unmodified. 406 | * 407 | * Examples: 408 | * 409 | * passport.transformAuthInfo(function(info, done) { 410 | * Client.findById(info.clientID, function (err, client) { 411 | * info.client = client; 412 | * done(err, info); 413 | * }); 414 | * }); 415 | * 416 | * @api public 417 | */ 418 | Authenticator.prototype.transformAuthInfo = function(fn, req, done) { 419 | if (typeof fn === 'function') { 420 | return this._infoTransformers.push(fn); 421 | } 422 | 423 | // private implementation that traverses the chain of transformers, 424 | // attempting to transform auth info 425 | var info = fn; 426 | 427 | // For backwards compatibility 428 | if (typeof req === 'function') { 429 | done = req; 430 | req = undefined; 431 | } 432 | 433 | var stack = this._infoTransformers; 434 | (function pass(i, err, tinfo) { 435 | // transformers use 'pass' as an error to skip processing 436 | if ('pass' === err) { 437 | err = undefined; 438 | } 439 | // an error or transformed info was obtained, done 440 | if (err || tinfo) { return done(err, tinfo); } 441 | 442 | var layer = stack[i]; 443 | if (!layer) { 444 | // if no transformers are registered (or they all pass), the default 445 | // behavior is to use the un-transformed info as-is 446 | return done(null, info); 447 | } 448 | 449 | 450 | function transformed(e, t) { 451 | pass(i + 1, e, t); 452 | } 453 | 454 | try { 455 | var arity = layer.length; 456 | if (arity == 1) { 457 | // sync 458 | var t = layer(info); 459 | transformed(null, t); 460 | } else if (arity == 3) { 461 | layer(req, info, transformed); 462 | } else { 463 | layer(info, transformed); 464 | } 465 | } catch(e) { 466 | return done(e); 467 | } 468 | })(0); 469 | }; 470 | 471 | /** 472 | * Return strategy with given `name`. 473 | * 474 | * @param {String} name 475 | * @return {Strategy} 476 | * @api private 477 | */ 478 | Authenticator.prototype._strategy = function(name) { 479 | return this._strategies[name]; 480 | }; 481 | 482 | 483 | /** 484 | * Expose `Authenticator`. 485 | */ 486 | module.exports = Authenticator; 487 | -------------------------------------------------------------------------------- /lib/errors/authenticationerror.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `AuthenticationError` error. 3 | * 4 | * @constructor 5 | * @api private 6 | */ 7 | function AuthenticationError(message, status) { 8 | Error.call(this); 9 | Error.captureStackTrace(this, arguments.callee); 10 | this.name = 'AuthenticationError'; 11 | this.message = message; 12 | this.status = status || 401; 13 | } 14 | 15 | // Inherit from `Error`. 16 | AuthenticationError.prototype.__proto__ = Error.prototype; 17 | 18 | 19 | // Expose constructor. 20 | module.exports = AuthenticationError; 21 | -------------------------------------------------------------------------------- /lib/framework/connect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var initialize = require('../middleware/initialize') 5 | , authenticate = require('../middleware/authenticate'); 6 | 7 | /** 8 | * Framework support for Connect/Express. 9 | * 10 | * This module provides support for using Passport with Express. It exposes 11 | * middleware that conform to the `fn(req, res, next)` signature. 12 | * 13 | * @return {Object} 14 | * @api protected 15 | */ 16 | exports = module.exports = function() { 17 | 18 | return { 19 | initialize: initialize, 20 | authenticate: authenticate 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/http/request.js: -------------------------------------------------------------------------------- 1 | var req = exports = module.exports = {}; 2 | 3 | /** 4 | * Initiate a login session for `user`. 5 | * 6 | * Options: 7 | * - `session` Save login state in session, defaults to _true_ 8 | * 9 | * Examples: 10 | * 11 | * req.logIn(user, { session: false }); 12 | * 13 | * req.logIn(user, function(err) { 14 | * if (err) { throw err; } 15 | * // session saved 16 | * }); 17 | * 18 | * @param {User} user 19 | * @param {Object} options 20 | * @param {Function} done 21 | * @api public 22 | */ 23 | req.login = 24 | req.logIn = function(user, options, done) { 25 | if (typeof options == 'function') { 26 | done = options; 27 | options = {}; 28 | } 29 | options = options || {}; 30 | 31 | var property = this._userProperty || 'user'; 32 | var session = (options.session === undefined) ? true : options.session; 33 | 34 | this[property] = user; 35 | if (session && this._sessionManager) { 36 | if (typeof done != 'function') { throw new Error('req#login requires a callback function'); } 37 | 38 | var self = this; 39 | this._sessionManager.logIn(this, user, options, function(err) { 40 | if (err) { self[property] = null; return done(err); } 41 | done(); 42 | }); 43 | } else { 44 | done && done(); 45 | } 46 | }; 47 | 48 | /** 49 | * Terminate an existing login session. 50 | * 51 | * @api public 52 | */ 53 | req.logout = 54 | req.logOut = function(options, done) { 55 | if (typeof options == 'function') { 56 | done = options; 57 | options = {}; 58 | } 59 | options = options || {}; 60 | 61 | var property = this._userProperty || 'user'; 62 | 63 | this[property] = null; 64 | if (this._sessionManager) { 65 | if (typeof done != 'function') { throw new Error('req#logout requires a callback function'); } 66 | 67 | this._sessionManager.logOut(this, options, done); 68 | } else { 69 | done && done(); 70 | } 71 | }; 72 | 73 | /** 74 | * Test if request is authenticated. 75 | * 76 | * @return {Boolean} 77 | * @api public 78 | */ 79 | req.isAuthenticated = function() { 80 | var property = this._userProperty || 'user'; 81 | return (this[property]) ? true : false; 82 | }; 83 | 84 | /** 85 | * Test if request is unauthenticated. 86 | * 87 | * @return {Boolean} 88 | * @api public 89 | */ 90 | req.isUnauthenticated = function() { 91 | return !this.isAuthenticated(); 92 | }; 93 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // Module dependencies. 2 | var Passport = require('./authenticator') 3 | , SessionStrategy = require('./strategies/session'); 4 | 5 | 6 | /** 7 | * Export default singleton. 8 | * 9 | * @api public 10 | */ 11 | exports = module.exports = new Passport(); 12 | 13 | /** 14 | * Expose constructors. 15 | */ 16 | exports.Passport = 17 | exports.Authenticator = Passport; 18 | exports.Strategy = require('passport-strategy'); 19 | 20 | /* 21 | * Expose strategies. 22 | */ 23 | exports.strategies = {}; 24 | exports.strategies.SessionStrategy = SessionStrategy; 25 | -------------------------------------------------------------------------------- /lib/middleware/authenticate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var http = require('http') 5 | , IncomingMessageExt = require('../http/request') 6 | , AuthenticationError = require('../errors/authenticationerror'); 7 | 8 | 9 | /** 10 | * Authenticates requests. 11 | * 12 | * Applies the `name`ed strategy (or strategies) to the incoming request, in 13 | * order to authenticate the request. If authentication is successful, the user 14 | * will be logged in and populated at `req.user` and a session will be 15 | * established by default. If authentication fails, an unauthorized response 16 | * will be sent. 17 | * 18 | * Options: 19 | * - `session` Save login state in session, defaults to _true_ 20 | * - `successRedirect` After successful login, redirect to given URL 21 | * - `successMessage` True to store success message in 22 | * req.session.messages, or a string to use as override 23 | * message for success. 24 | * - `successFlash` True to flash success messages or a string to use as a flash 25 | * message for success (overrides any from the strategy itself). 26 | * - `failureRedirect` After failed login, redirect to given URL 27 | * - `failureMessage` True to store failure message in 28 | * req.session.messages, or a string to use as override 29 | * message for failure. 30 | * - `failureFlash` True to flash failure messages or a string to use as a flash 31 | * message for failures (overrides any from the strategy itself). 32 | * - `assignProperty` Assign the object provided by the verify callback to given property 33 | * 34 | * An optional `callback` can be supplied to allow the application to override 35 | * the default manner in which authentication attempts are handled. The 36 | * callback has the following signature, where `user` will be set to the 37 | * authenticated user on a successful authentication attempt, or `false` 38 | * otherwise. An optional `info` argument will be passed, containing additional 39 | * details provided by the strategy's verify callback - this could be information about 40 | * a successful authentication or a challenge message for a failed authentication. 41 | * An optional `status` argument will be passed when authentication fails - this could 42 | * be a HTTP response code for a remote authentication failure or similar. 43 | * 44 | * app.get('/protected', function(req, res, next) { 45 | * passport.authenticate('local', function(err, user, info, status) { 46 | * if (err) { return next(err) } 47 | * if (!user) { return res.redirect('/signin') } 48 | * res.redirect('/account'); 49 | * })(req, res, next); 50 | * }); 51 | * 52 | * Note that if a callback is supplied, it becomes the application's 53 | * responsibility to log-in the user, establish a session, and otherwise perform 54 | * the desired operations. 55 | * 56 | * Examples: 57 | * 58 | * passport.authenticate('local', { successRedirect: '/', failureRedirect: '/login' }); 59 | * 60 | * passport.authenticate('basic', { session: false }); 61 | * 62 | * passport.authenticate('twitter'); 63 | * 64 | * @param {Strategy|String|Array} name 65 | * @param {Object} options 66 | * @param {Function} callback 67 | * @return {Function} 68 | * @api public 69 | */ 70 | module.exports = function authenticate(passport, name, options, callback) { 71 | if (typeof options == 'function') { 72 | callback = options; 73 | options = {}; 74 | } 75 | options = options || {}; 76 | 77 | var multi = true; 78 | 79 | // Cast `name` to an array, allowing authentication to pass through a chain of 80 | // strategies. The first strategy to succeed, redirect, or error will halt 81 | // the chain. Authentication failures will proceed through each strategy in 82 | // series, ultimately failing if all strategies fail. 83 | // 84 | // This is typically used on API endpoints to allow clients to authenticate 85 | // using their preferred choice of Basic, Digest, token-based schemes, etc. 86 | // It is not feasible to construct a chain of multiple strategies that involve 87 | // redirection (for example both Facebook and Twitter), since the first one to 88 | // redirect will halt the chain. 89 | if (!Array.isArray(name)) { 90 | name = [ name ]; 91 | multi = false; 92 | } 93 | 94 | return function authenticate(req, res, next) { 95 | req.login = 96 | req.logIn = req.logIn || IncomingMessageExt.logIn; 97 | req.logout = 98 | req.logOut = req.logOut || IncomingMessageExt.logOut; 99 | req.isAuthenticated = req.isAuthenticated || IncomingMessageExt.isAuthenticated; 100 | req.isUnauthenticated = req.isUnauthenticated || IncomingMessageExt.isUnauthenticated; 101 | 102 | req._sessionManager = passport._sm; 103 | 104 | // accumulator for failures from each strategy in the chain 105 | var failures = []; 106 | 107 | function allFailed() { 108 | if (callback) { 109 | if (!multi) { 110 | return callback(null, false, failures[0].challenge, failures[0].status); 111 | } else { 112 | var challenges = failures.map(function(f) { return f.challenge; }); 113 | var statuses = failures.map(function(f) { return f.status; }); 114 | return callback(null, false, challenges, statuses); 115 | } 116 | } 117 | 118 | // Strategies are ordered by priority. For the purpose of flashing a 119 | // message, the first failure will be displayed. 120 | var failure = failures[0] || {} 121 | , challenge = failure.challenge || {} 122 | , msg; 123 | 124 | if (options.failureFlash) { 125 | var flash = options.failureFlash; 126 | if (typeof flash == 'string') { 127 | flash = { type: 'error', message: flash }; 128 | } 129 | flash.type = flash.type || 'error'; 130 | 131 | var type = flash.type || challenge.type || 'error'; 132 | msg = flash.message || challenge.message || challenge; 133 | if (typeof msg == 'string') { 134 | req.flash(type, msg); 135 | } 136 | } 137 | if (options.failureMessage) { 138 | msg = options.failureMessage; 139 | if (typeof msg == 'boolean') { 140 | msg = challenge.message || challenge; 141 | } 142 | if (typeof msg == 'string') { 143 | req.session.messages = req.session.messages || []; 144 | req.session.messages.push(msg); 145 | } 146 | } 147 | if (options.failureRedirect) { 148 | return res.redirect(options.failureRedirect); 149 | } 150 | 151 | // When failure handling is not delegated to the application, the default 152 | // is to respond with 401 Unauthorized. Note that the WWW-Authenticate 153 | // header will be set according to the strategies in use (see 154 | // actions#fail). If multiple strategies failed, each of their challenges 155 | // will be included in the response. 156 | var rchallenge = [] 157 | , rstatus, status; 158 | 159 | for (var j = 0, len = failures.length; j < len; j++) { 160 | failure = failures[j]; 161 | challenge = failure.challenge; 162 | status = failure.status; 163 | 164 | rstatus = rstatus || status; 165 | if (typeof challenge == 'string') { 166 | rchallenge.push(challenge); 167 | } 168 | } 169 | 170 | res.statusCode = rstatus || 401; 171 | if (res.statusCode == 401 && rchallenge.length) { 172 | res.setHeader('WWW-Authenticate', rchallenge); 173 | } 174 | if (options.failWithError) { 175 | return next(new AuthenticationError(http.STATUS_CODES[res.statusCode], rstatus)); 176 | } 177 | res.end(http.STATUS_CODES[res.statusCode]); 178 | } 179 | 180 | (function attempt(i) { 181 | var layer = name[i]; 182 | // If no more strategies exist in the chain, authentication has failed. 183 | if (!layer) { return allFailed(); } 184 | 185 | // Get the strategy, which will be used as prototype from which to create 186 | // a new instance. Action functions will then be bound to the strategy 187 | // within the context of the HTTP request/response pair. 188 | var strategy, prototype; 189 | if (typeof layer.authenticate == 'function') { 190 | strategy = layer; 191 | } else { 192 | prototype = passport._strategy(layer); 193 | if (!prototype) { return next(new Error('Unknown authentication strategy "' + layer + '"')); } 194 | 195 | strategy = Object.create(prototype); 196 | } 197 | 198 | 199 | // ----- BEGIN STRATEGY AUGMENTATION ----- 200 | // Augment the new strategy instance with action functions. These action 201 | // functions are bound via closure the the request/response pair. The end 202 | // goal of the strategy is to invoke *one* of these action methods, in 203 | // order to indicate successful or failed authentication, redirect to a 204 | // third-party identity provider, etc. 205 | 206 | /** 207 | * Authenticate `user`, with optional `info`. 208 | * 209 | * Strategies should call this function to successfully authenticate a 210 | * user. `user` should be an object supplied by the application after it 211 | * has been given an opportunity to verify credentials. `info` is an 212 | * optional argument containing additional user information. This is 213 | * useful for third-party authentication strategies to pass profile 214 | * details. 215 | * 216 | * @param {Object} user 217 | * @param {Object} info 218 | * @api public 219 | */ 220 | strategy.success = function(user, info) { 221 | if (callback) { 222 | return callback(null, user, info); 223 | } 224 | 225 | info = info || {}; 226 | var msg; 227 | 228 | if (options.successFlash) { 229 | var flash = options.successFlash; 230 | if (typeof flash == 'string') { 231 | flash = { type: 'success', message: flash }; 232 | } 233 | flash.type = flash.type || 'success'; 234 | 235 | var type = flash.type || info.type || 'success'; 236 | msg = flash.message || info.message || info; 237 | if (typeof msg == 'string') { 238 | req.flash(type, msg); 239 | } 240 | } 241 | if (options.successMessage) { 242 | msg = options.successMessage; 243 | if (typeof msg == 'boolean') { 244 | msg = info.message || info; 245 | } 246 | if (typeof msg == 'string') { 247 | req.session.messages = req.session.messages || []; 248 | req.session.messages.push(msg); 249 | } 250 | } 251 | if (options.assignProperty) { 252 | req[options.assignProperty] = user; 253 | if (options.authInfo !== false) { 254 | passport.transformAuthInfo(info, req, function(err, tinfo) { 255 | if (err) { return next(err); } 256 | req.authInfo = tinfo; 257 | next(); 258 | }); 259 | } else { 260 | next(); 261 | } 262 | return; 263 | } 264 | 265 | req.logIn(user, options, function(err) { 266 | if (err) { return next(err); } 267 | 268 | function complete() { 269 | if (options.successReturnToOrRedirect) { 270 | var url = options.successReturnToOrRedirect; 271 | if (req.session && req.session.returnTo) { 272 | url = req.session.returnTo; 273 | delete req.session.returnTo; 274 | } 275 | return res.redirect(url); 276 | } 277 | if (options.successRedirect) { 278 | return res.redirect(options.successRedirect); 279 | } 280 | next(); 281 | } 282 | 283 | if (options.authInfo !== false) { 284 | passport.transformAuthInfo(info, req, function(err, tinfo) { 285 | if (err) { return next(err); } 286 | req.authInfo = tinfo; 287 | complete(); 288 | }); 289 | } else { 290 | complete(); 291 | } 292 | }); 293 | }; 294 | 295 | /** 296 | * Fail authentication, with optional `challenge` and `status`, defaulting 297 | * to 401. 298 | * 299 | * Strategies should call this function to fail an authentication attempt. 300 | * 301 | * @param {String} challenge 302 | * @param {Number} status 303 | * @api public 304 | */ 305 | strategy.fail = function(challenge, status) { 306 | if (typeof challenge == 'number') { 307 | status = challenge; 308 | challenge = undefined; 309 | } 310 | 311 | // push this failure into the accumulator and attempt authentication 312 | // using the next strategy 313 | failures.push({ challenge: challenge, status: status }); 314 | attempt(i + 1); 315 | }; 316 | 317 | /** 318 | * Redirect to `url` with optional `status`, defaulting to 302. 319 | * 320 | * Strategies should call this function to redirect the user (via their 321 | * user agent) to a third-party website for authentication. 322 | * 323 | * @param {String} url 324 | * @param {Number} status 325 | * @api public 326 | */ 327 | strategy.redirect = function(url, status) { 328 | // NOTE: Do not use `res.redirect` from Express, because it can't decide 329 | // what it wants. 330 | // 331 | // Express 2.x: res.redirect(url, status) 332 | // Express 3.x: res.redirect(status, url) -OR- res.redirect(url, status) 333 | // - as of 3.14.0, deprecated warnings are issued if res.redirect(url, status) 334 | // is used 335 | // Express 4.x: res.redirect(status, url) 336 | // - all versions (as of 4.8.7) continue to accept res.redirect(url, status) 337 | // but issue deprecated versions 338 | 339 | res.statusCode = status || 302; 340 | res.setHeader('Location', url); 341 | res.setHeader('Content-Length', '0'); 342 | res.end(); 343 | }; 344 | 345 | /** 346 | * Pass without making a success or fail decision. 347 | * 348 | * Under most circumstances, Strategies should not need to call this 349 | * function. It exists primarily to allow previous authentication state 350 | * to be restored, for example from an HTTP session. 351 | * 352 | * @api public 353 | */ 354 | strategy.pass = function() { 355 | next(); 356 | }; 357 | 358 | /** 359 | * Internal error while performing authentication. 360 | * 361 | * Strategies should call this function when an internal error occurs 362 | * during the process of performing authentication; for example, if the 363 | * user directory is not available. 364 | * 365 | * @param {Error} err 366 | * @api public 367 | */ 368 | strategy.error = function(err) { 369 | if (callback) { 370 | return callback(err); 371 | } 372 | 373 | next(err); 374 | }; 375 | 376 | // ----- END STRATEGY AUGMENTATION ----- 377 | 378 | strategy.authenticate(req, options); 379 | })(0); // attempt 380 | }; 381 | }; 382 | -------------------------------------------------------------------------------- /lib/middleware/initialize.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var IncomingMessageExt = require('../http/request'); 5 | 6 | 7 | /** 8 | * Passport initialization. 9 | * 10 | * Intializes Passport for incoming requests, allowing authentication strategies 11 | * to be applied. 12 | * 13 | * If sessions are being utilized, applications must set up Passport with 14 | * functions to serialize a user into and out of a session. For example, a 15 | * common pattern is to serialize just the user ID into the session (due to the 16 | * fact that it is desirable to store the minimum amount of data in a session). 17 | * When a subsequent request arrives for the session, the full User object can 18 | * be loaded from the database by ID. 19 | * 20 | * Note that additional middleware is required to persist login state, so we 21 | * must use the `connect.session()` middleware _before_ `passport.initialize()`. 22 | * 23 | * If sessions are being used, this middleware must be in use by the 24 | * Connect/Express application for Passport to operate. If the application is 25 | * entirely stateless (not using sessions), this middleware is not necessary, 26 | * but its use will not have any adverse impact. 27 | * 28 | * Examples: 29 | * 30 | * app.use(connect.cookieParser()); 31 | * app.use(connect.session({ secret: 'keyboard cat' })); 32 | * app.use(passport.initialize()); 33 | * app.use(passport.session()); 34 | * 35 | * passport.serializeUser(function(user, done) { 36 | * done(null, user.id); 37 | * }); 38 | * 39 | * passport.deserializeUser(function(id, done) { 40 | * User.findById(id, function (err, user) { 41 | * done(err, user); 42 | * }); 43 | * }); 44 | * 45 | * @return {Function} 46 | * @api public 47 | */ 48 | module.exports = function initialize(passport, options) { 49 | options = options || {}; 50 | 51 | return function initialize(req, res, next) { 52 | req.login = 53 | req.logIn = req.logIn || IncomingMessageExt.logIn; 54 | req.logout = 55 | req.logOut = req.logOut || IncomingMessageExt.logOut; 56 | req.isAuthenticated = req.isAuthenticated || IncomingMessageExt.isAuthenticated; 57 | req.isUnauthenticated = req.isUnauthenticated || IncomingMessageExt.isUnauthenticated; 58 | 59 | req._sessionManager = passport._sm; 60 | 61 | if (options.userProperty) { 62 | req._userProperty = options.userProperty; 63 | } 64 | 65 | var compat = (options.compat === undefined) ? true : options.compat; 66 | if (compat) { 67 | // `passport@0.5.1` [removed][1] all internal use of `req._passport`. 68 | // From the standpoint of this package, this should have been a 69 | // non-breaking change. However, some strategies (such as `passport-azure-ad`) 70 | // depend directly on `passport@0.4.x` or earlier. `require`-ing earlier 71 | // versions of `passport` has the effect of monkeypatching `http.IncomingMessage` 72 | // with `logIn`, `logOut`, `isAuthenticated` and `isUnauthenticated` 73 | // functions that [expect][2] the `req._passport` property to exist. 74 | // Since pre-existing functions on `req` are given [preference][3], this 75 | // results in [issues][4]. 76 | // 77 | // The changes here restore the expected properties needed when earlier 78 | // versions of `passport` are `require`-ed. This compatibility mode is 79 | // enabled by default, and can be disabld by simply not `use`-ing `passport.initialize()` 80 | // middleware or setting `compat: false` as an option to the middleware. 81 | // 82 | // An alternative approach to addressing this issue would be to not 83 | // preferentially use pre-existing functions on `req`, but rather always 84 | // overwrite `req.logIn`, etc. with the versions of those functions shiped 85 | // with `authenticate()` middleware. This option should be reconsidered 86 | // in a future major version release. 87 | // 88 | // [1]: https://github.com/jaredhanson/passport/pull/875 89 | // [2]: https://github.com/jaredhanson/passport/blob/v0.4.1/lib/http/request.js 90 | // [3]: https://github.com/jaredhanson/passport/blob/v0.5.1/lib/middleware/authenticate.js#L96 91 | // [4]: https://github.com/jaredhanson/passport/issues/877 92 | passport._userProperty = options.userProperty || 'user'; 93 | 94 | req._passport = {}; 95 | req._passport.instance = passport; 96 | } 97 | 98 | next(); 99 | }; 100 | }; 101 | -------------------------------------------------------------------------------- /lib/sessionmanager.js: -------------------------------------------------------------------------------- 1 | var merge = require('utils-merge'); 2 | 3 | function SessionManager(options, serializeUser) { 4 | if (typeof options == 'function') { 5 | serializeUser = options; 6 | options = undefined; 7 | } 8 | options = options || {}; 9 | 10 | this._key = options.key || 'passport'; 11 | this._serializeUser = serializeUser; 12 | } 13 | 14 | SessionManager.prototype.logIn = function(req, user, options, cb) { 15 | if (typeof options == 'function') { 16 | cb = options; 17 | options = {}; 18 | } 19 | options = options || {}; 20 | 21 | if (!req.session) { return cb(new Error('Login sessions require session support. Did you forget to use `express-session` middleware?')); } 22 | 23 | var self = this; 24 | var prevSession = req.session; 25 | 26 | // regenerate the session, which is good practice to help 27 | // guard against forms of session fixation 28 | req.session.regenerate(function(err) { 29 | if (err) { 30 | return cb(err); 31 | } 32 | 33 | self._serializeUser(user, req, function(err, obj) { 34 | if (err) { 35 | return cb(err); 36 | } 37 | if (options.keepSessionInfo) { 38 | merge(req.session, prevSession); 39 | } 40 | if (!req.session[self._key]) { 41 | req.session[self._key] = {}; 42 | } 43 | // store user information in session, typically a user id 44 | req.session[self._key].user = obj; 45 | // save the session before redirection to ensure page 46 | // load does not happen before session is saved 47 | req.session.save(function(err) { 48 | if (err) { 49 | return cb(err); 50 | } 51 | cb(); 52 | }); 53 | }); 54 | }); 55 | } 56 | 57 | SessionManager.prototype.logOut = function(req, options, cb) { 58 | if (typeof options == 'function') { 59 | cb = options; 60 | options = {}; 61 | } 62 | options = options || {}; 63 | 64 | if (!req.session) { return cb(new Error('Login sessions require session support. Did you forget to use `express-session` middleware?')); } 65 | 66 | var self = this; 67 | 68 | // clear the user from the session object and save. 69 | // this will ensure that re-using the old session id 70 | // does not have a logged in user 71 | if (req.session[this._key]) { 72 | delete req.session[this._key].user; 73 | } 74 | var prevSession = req.session; 75 | 76 | req.session.save(function(err) { 77 | if (err) { 78 | return cb(err) 79 | } 80 | 81 | // regenerate the session, which is good practice to help 82 | // guard against forms of session fixation 83 | req.session.regenerate(function(err) { 84 | if (err) { 85 | return cb(err); 86 | } 87 | if (options.keepSessionInfo) { 88 | merge(req.session, prevSession); 89 | } 90 | cb(); 91 | }); 92 | }); 93 | } 94 | 95 | 96 | module.exports = SessionManager; 97 | -------------------------------------------------------------------------------- /lib/strategies/session.js: -------------------------------------------------------------------------------- 1 | // Module dependencies. 2 | var pause = require('pause') 3 | , util = require('util') 4 | , Strategy = require('passport-strategy'); 5 | 6 | 7 | /** 8 | * Create a new `SessionStrategy` object. 9 | * 10 | * An instance of this strategy is automatically used when creating an 11 | * `{@link Authenticator}`. As such, it is typically unnecessary to create an 12 | * instance using this constructor. 13 | * 14 | * @classdesc This `Strategy` authenticates HTTP requests based on the contents 15 | * of session data. 16 | * 17 | * The login session must have been previously initiated, typically upon the 18 | * user interactively logging in using a HTML form. During session initiation, 19 | * the logged-in user's information is persisted to the session so that it can 20 | * be restored on subsequent requests. 21 | * 22 | * Note that this strategy merely restores the authentication state from the 23 | * session, it does not authenticate the session itself. Authenticating the 24 | * underlying session is assumed to have been done by the middleware 25 | * implementing session support. This is typically accomplished by setting a 26 | * signed cookie, and verifying the signature of that cookie on incoming 27 | * requests. 28 | * 29 | * In {@link https://expressjs.com/ Express}-based apps, session support is 30 | * commonly provided by {@link https://github.com/expressjs/session `express-session`} 31 | * or {@link https://github.com/expressjs/cookie-session `cookie-session`}. 32 | * 33 | * @public 34 | * @class 35 | * @augments base.Strategy 36 | * @param {Object} [options] 37 | * @param {string} [options.key='passport'] - Determines what property ("key") on 38 | * the session data where login session data is located. The login 39 | * session is stored and read from `req.session[key]`. 40 | * @param {function} deserializeUser - Function which deserializes user. 41 | */ 42 | function SessionStrategy(options, deserializeUser) { 43 | if (typeof options == 'function') { 44 | deserializeUser = options; 45 | options = undefined; 46 | } 47 | options = options || {}; 48 | 49 | Strategy.call(this); 50 | 51 | /** The name of the strategy, set to `'session'`. 52 | * 53 | * @type {string} 54 | * @readonly 55 | */ 56 | this.name = 'session'; 57 | this._key = options.key || 'passport'; 58 | this._deserializeUser = deserializeUser; 59 | } 60 | 61 | // Inherit from `passport.Strategy`. 62 | util.inherits(SessionStrategy, Strategy); 63 | 64 | /** 65 | * Authenticate request based on current session data. 66 | * 67 | * When login session data is present in the session, that data will be used to 68 | * restore login state across across requests by calling the deserialize user 69 | * function. 70 | * 71 | * If login session data is not present, the request will be passed to the next 72 | * middleware, rather than failing authentication - which is the behavior of 73 | * most other strategies. This deviation allows session authentication to be 74 | * performed at the application-level, rather than the individual route level, 75 | * while allowing both authenticated and unauthenticated requests and rendering 76 | * responses accordingly. Routes that require authentication will need to guard 77 | * that condition. 78 | * 79 | * This function is protected, and should not be called directly. Instead, 80 | * use `passport.authenticate()` middleware and specify the {@link SessionStrategy#name `name`} 81 | * of this strategy and any options. 82 | * 83 | * @protected 84 | * @param {http.IncomingMessage} req - The Node.js {@link https://nodejs.org/api/http.html#class-httpincomingmessage `IncomingMessage`} 85 | * object. 86 | * @param {Object} [options] 87 | * @param {boolean} [options.pauseStream=false] - When `true`, data events on 88 | * the request will be paused, and then resumed after the asynchronous 89 | * `deserializeUser` function has completed. This is only necessary in 90 | * cases where later middleware in the stack are listening for events, 91 | * and ensures that those events are not missed. 92 | * 93 | * @example 94 | * passport.authenticate('session'); 95 | */ 96 | SessionStrategy.prototype.authenticate = function(req, options) { 97 | if (!req.session) { return this.error(new Error('Login sessions require session support. Did you forget to use `express-session` middleware?')); } 98 | options = options || {}; 99 | 100 | var self = this, 101 | su; 102 | if (req.session[this._key]) { 103 | su = req.session[this._key].user; 104 | } 105 | 106 | if (su || su === 0) { 107 | // NOTE: Stream pausing is desirable in the case where later middleware is 108 | // listening for events emitted from request. For discussion on the 109 | // matter, refer to: https://github.com/jaredhanson/passport/pull/106 110 | 111 | var paused = options.pauseStream ? pause(req) : null; 112 | this._deserializeUser(su, req, function(err, user) { 113 | if (err) { return self.error(err); } 114 | if (!user) { 115 | delete req.session[self._key].user; 116 | } else { 117 | var property = req._userProperty || 'user'; 118 | req[property] = user; 119 | } 120 | self.pass(); 121 | if (paused) { 122 | paused.resume(); 123 | } 124 | }); 125 | } else { 126 | self.pass(); 127 | } 128 | }; 129 | 130 | // Export `SessionStrategy`. 131 | module.exports = SessionStrategy; 132 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport", 3 | "version": "0.7.0", 4 | "description": "Simple, unobtrusive authentication for Node.js.", 5 | "keywords": [ 6 | "express", 7 | "connect", 8 | "auth", 9 | "authn", 10 | "authentication" 11 | ], 12 | "author": { 13 | "name": "Jared Hanson", 14 | "email": "jaredhanson@gmail.com", 15 | "url": "https://www.jaredhanson.me/" 16 | }, 17 | "homepage": "https://www.passportjs.org/", 18 | "repository": { 19 | "type": "git", 20 | "url": "git://github.com/jaredhanson/passport.git" 21 | }, 22 | "bugs": { 23 | "url": "https://github.com/jaredhanson/passport/issues" 24 | }, 25 | "funding": { 26 | "type": "github", 27 | "url": "https://github.com/sponsors/jaredhanson" 28 | }, 29 | "license": "MIT", 30 | "licenses": [ 31 | { 32 | "type": "MIT", 33 | "url": "https://opensource.org/licenses/MIT" 34 | } 35 | ], 36 | "main": "./lib", 37 | "dependencies": { 38 | "passport-strategy": "1.x.x", 39 | "pause": "0.0.1", 40 | "utils-merge": "^1.0.1" 41 | }, 42 | "devDependencies": { 43 | "make-node": "0.3.x", 44 | "mocha": "2.x.x", 45 | "chai": "2.x.x", 46 | "chai-connect-middleware": "0.3.x", 47 | "chai-passport-strategy": "0.2.x", 48 | "proxyquire": "1.4.x" 49 | }, 50 | "engines": { 51 | "node": ">= 0.4.0" 52 | }, 53 | "scripts": { 54 | "test": "node_modules/.bin/mocha --reporter spec --require test/bootstrap/node test/*.test.js test/**/*.test.js" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /sponsors/auth0-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredhanson/passport/217018dbc46dcd4118dd6f2c60c8d97010c587f8/sponsors/auth0-dark.png -------------------------------------------------------------------------------- /sponsors/auth0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredhanson/passport/217018dbc46dcd4118dd6f2c60c8d97010c587f8/sponsors/auth0.png -------------------------------------------------------------------------------- /sponsors/descope-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 31 | 35 | 40 | 45 | 49 | 54 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /sponsors/descope.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 63 | 68 | 73 | 77 | 82 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /sponsors/fusionauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredhanson/passport/217018dbc46dcd4118dd6f2c60c8d97010c587f8/sponsors/fusionauth.png -------------------------------------------------------------------------------- /sponsors/fusionauth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml 45 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 65 | 71 | 77 | 83 | 89 | 95 | 101 | 104 | 110 | 116 | 122 | 128 | 134 | 140 | 144 | 150 | 154 | 160 | 171 | -------------------------------------------------------------------------------- /sponsors/loginradius.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredhanson/passport/217018dbc46dcd4118dd6f2c60c8d97010c587f8/sponsors/loginradius.png -------------------------------------------------------------------------------- /sponsors/snyk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredhanson/passport/217018dbc46dcd4118dd6f2c60c8d97010c587f8/sponsors/snyk.png -------------------------------------------------------------------------------- /sponsors/stytch-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredhanson/passport/217018dbc46dcd4118dd6f2c60c8d97010c587f8/sponsors/stytch-dark.png -------------------------------------------------------------------------------- /sponsors/stytch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredhanson/passport/217018dbc46dcd4118dd6f2c60c8d97010c587f8/sponsors/stytch.png -------------------------------------------------------------------------------- /sponsors/workos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jaredhanson/passport/217018dbc46dcd4118dd6f2c60c8d97010c587f8/sponsors/workos.png -------------------------------------------------------------------------------- /test/authenticator.framework.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect */ 2 | 3 | var Authenticator = require('../lib/authenticator'); 4 | 5 | 6 | describe('Authenticator', function() { 7 | 8 | describe('#framework', function() { 9 | 10 | describe('with an authenticate function used for authorization', function() { 11 | var passport = new Authenticator(); 12 | passport.framework({ 13 | initialize: function() { 14 | return function() {}; 15 | }, 16 | authenticate: function(passport, name, options) { 17 | return function() { 18 | return 'authenticate(): ' + name + ' ' + options.assignProperty; 19 | }; 20 | } 21 | }); 22 | 23 | var rv = passport.authorize('foo')(); 24 | it('should call authenticate', function() { 25 | expect(rv).to.equal('authenticate(): foo account'); 26 | }); 27 | }); 28 | 29 | describe('with an authorize function used for authorization', function() { 30 | var passport = new Authenticator(); 31 | passport.framework({ 32 | initialize: function() { 33 | return function() {}; 34 | }, 35 | authenticate: function(passport, name, options) { 36 | return function() { 37 | return 'authenticate(): ' + name + ' ' + options.assignProperty; 38 | }; 39 | }, 40 | authorize: function(passport, name, options) { 41 | return function() { 42 | return 'authorize(): ' + name + ' ' + options.assignProperty; 43 | }; 44 | } 45 | }); 46 | 47 | var rv = passport.authorize('foo')(); 48 | it('should call authorize', function() { 49 | expect(rv).to.equal('authorize(): foo account'); 50 | }); 51 | }); 52 | 53 | }); 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /test/authenticator.middleware.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , Authenticator = require('../lib/authenticator'); 6 | 7 | 8 | describe('Authenticator', function() { 9 | 10 | describe('#initialize', function() { 11 | 12 | it('should have correct arity', function() { 13 | var passport = new Authenticator(); 14 | expect(passport.initialize).to.have.length(1); 15 | }); 16 | 17 | describe('handling a request', function() { 18 | var passport = new Authenticator(); 19 | var request, error; 20 | 21 | before(function(done) { 22 | chai.connect.use(passport.initialize()) 23 | .req(function(req) { 24 | request = req; 25 | req.session = {}; 26 | }) 27 | .next(function(err) { 28 | error = err; 29 | done(); 30 | }) 31 | .dispatch(); 32 | }); 33 | 34 | it('should not error', function() { 35 | expect(error).to.be.undefined; 36 | }); 37 | 38 | it('should not set user property on request', function() { 39 | expect(request._userProperty).to.be.undefined; 40 | }); 41 | 42 | it('should not initialize namespace within session', function() { 43 | expect(request.session.passport).to.be.undefined; 44 | }); 45 | 46 | it('should expose authenticator on internal request property', function() { 47 | expect(request._passport).to.be.an('object'); 48 | expect(request._passport.instance).to.be.an.instanceOf(Authenticator); 49 | expect(request._passport.instance).to.equal(passport); 50 | expect(request._passport.instance._sm).to.be.an('object'); 51 | expect(request._passport.instance._userProperty).to.equal('user'); 52 | }); 53 | }); 54 | 55 | describe('handling a request with custom user property', function() { 56 | var passport = new Authenticator(); 57 | var request, error; 58 | 59 | before(function(done) { 60 | chai.connect.use(passport.initialize({ userProperty: 'currentUser' })) 61 | .req(function(req) { 62 | request = req; 63 | req.session = {}; 64 | }) 65 | .next(function(err) { 66 | error = err; 67 | done(); 68 | }) 69 | .dispatch(); 70 | }); 71 | 72 | it('should not error', function() { 73 | expect(error).to.be.undefined; 74 | }); 75 | 76 | it('should set user property on request', function() { 77 | expect(request._userProperty).to.equal('currentUser'); 78 | }); 79 | 80 | it('should not initialize namespace within session', function() { 81 | expect(request.session.passport).to.be.undefined; 82 | }); 83 | 84 | it('should expose authenticator on internal request property', function() { 85 | expect(request._passport).to.be.an('object'); 86 | expect(request._passport.instance).to.be.an.instanceOf(Authenticator); 87 | expect(request._passport.instance).to.equal(passport); 88 | expect(request._passport.instance._sm).to.be.an('object'); 89 | expect(request._passport.instance._userProperty).to.equal('currentUser'); 90 | }); 91 | }); 92 | 93 | }); 94 | 95 | 96 | describe('#authenticate', function() { 97 | 98 | it('should have correct arity', function() { 99 | var passport = new Authenticator(); 100 | expect(passport.authenticate).to.have.length(3); 101 | }); 102 | 103 | describe('handling a request', function() { 104 | function Strategy() { 105 | } 106 | Strategy.prototype.authenticate = function(req) { 107 | var user = { id: '1', username: 'jaredhanson' }; 108 | this.success(user); 109 | }; 110 | 111 | var passport = new Authenticator(); 112 | passport.use('success', new Strategy()); 113 | 114 | var request, error; 115 | 116 | before(function(done) { 117 | chai.connect.use(passport.authenticate('success')) 118 | .req(function(req) { 119 | request = req; 120 | 121 | req.logIn = function(user, options, done) { 122 | this.user = user; 123 | done(); 124 | }; 125 | }) 126 | .next(function(err) { 127 | error = err; 128 | done(); 129 | }) 130 | .dispatch(); 131 | }); 132 | 133 | it('should not error', function() { 134 | expect(error).to.be.undefined; 135 | }); 136 | 137 | it('should set user', function() { 138 | expect(request.user).to.be.an('object'); 139 | expect(request.user.id).to.equal('1'); 140 | expect(request.user.username).to.equal('jaredhanson'); 141 | }); 142 | 143 | it('should set authInfo', function() { 144 | expect(request.authInfo).to.be.an('object'); 145 | expect(Object.keys(request.authInfo)).to.have.length(0); 146 | }); 147 | }); 148 | 149 | describe('handling a request with instantiated strategy', function() { 150 | function Strategy() { 151 | } 152 | Strategy.prototype.authenticate = function(req) { 153 | var user = { id: '1', username: 'jaredhanson' }; 154 | this.success(user); 155 | }; 156 | 157 | var passport = new Authenticator(); 158 | 159 | var request, error; 160 | 161 | before(function(done) { 162 | chai.connect.use(passport.authenticate(new Strategy())) 163 | .req(function(req) { 164 | request = req; 165 | 166 | req.logIn = function(user, options, done) { 167 | this.user = user; 168 | done(); 169 | }; 170 | }) 171 | .next(function(err) { 172 | error = err; 173 | done(); 174 | }) 175 | .dispatch(); 176 | }); 177 | 178 | it('should not error', function() { 179 | expect(error).to.be.undefined; 180 | }); 181 | 182 | it('should set user', function() { 183 | expect(request.user).to.be.an('object'); 184 | expect(request.user.id).to.equal('1'); 185 | expect(request.user.username).to.equal('jaredhanson'); 186 | }); 187 | 188 | it('should set authInfo', function() { 189 | expect(request.authInfo).to.be.an('object'); 190 | expect(Object.keys(request.authInfo)).to.have.length(0); 191 | }); 192 | }); 193 | 194 | }); 195 | 196 | 197 | describe('#authorize', function() { 198 | 199 | it('should have correct arity', function() { 200 | var passport = new Authenticator(); 201 | expect(passport.authorize).to.have.length(3); 202 | }); 203 | 204 | describe('handling a request', function() { 205 | function Strategy() { 206 | } 207 | Strategy.prototype.authenticate = function(req) { 208 | var user = { id: '1', username: 'jaredhanson' }; 209 | this.success(user); 210 | }; 211 | 212 | var passport = new Authenticator(); 213 | passport.use('success', new Strategy()); 214 | 215 | var request, error; 216 | 217 | before(function(done) { 218 | chai.connect.use(passport.authorize('success')) 219 | .req(function(req) { 220 | request = req; 221 | 222 | req.logIn = function(user, options, done) { 223 | this.user = user; 224 | done(); 225 | }; 226 | }) 227 | .next(function(err) { 228 | error = err; 229 | done(); 230 | }) 231 | .dispatch(); 232 | }); 233 | 234 | it('should not error', function() { 235 | expect(error).to.be.undefined; 236 | }); 237 | 238 | it('should not set user', function() { 239 | expect(request.user).to.be.undefined; 240 | }); 241 | 242 | it('should set account', function() { 243 | expect(request.account).to.be.an('object'); 244 | expect(request.account.id).to.equal('1'); 245 | expect(request.account.username).to.equal('jaredhanson'); 246 | }); 247 | 248 | it('should set authInfo to empty object', function() { 249 | expect(request.authInfo).to.deep.equal({}); 250 | }); 251 | }); 252 | 253 | describe('handling a request with authInfo disabled', function() { 254 | function Strategy() { 255 | } 256 | Strategy.prototype.authenticate = function(req) { 257 | var user = { id: '1', username: 'jaredhanson' }; 258 | this.success(user); 259 | }; 260 | 261 | var passport = new Authenticator(); 262 | passport.use('success', new Strategy()); 263 | 264 | var request, error; 265 | 266 | before(function(done) { 267 | chai.connect.use(passport.authorize('success', { authInfo: false })) 268 | .req(function(req) { 269 | request = req; 270 | 271 | req.logIn = function(user, options, done) { 272 | this.user = user; 273 | done(); 274 | }; 275 | }) 276 | .next(function(err) { 277 | error = err; 278 | done(); 279 | }) 280 | .dispatch(); 281 | }); 282 | 283 | it('should not error', function() { 284 | expect(error).to.be.undefined; 285 | }); 286 | 287 | it('should not set user', function() { 288 | expect(request.user).to.be.undefined; 289 | }); 290 | 291 | it('should set account', function() { 292 | expect(request.account).to.be.an('object'); 293 | expect(request.account.id).to.equal('1'); 294 | expect(request.account.username).to.equal('jaredhanson'); 295 | }); 296 | 297 | it('should not set authInfo', function() { 298 | expect(request.authInfo).to.be.undefined; 299 | }); 300 | }); 301 | 302 | }); 303 | 304 | describe('#session', function() { 305 | 306 | it('should have correct arity', function() { 307 | var passport = new Authenticator(); 308 | expect(passport.session).to.have.length(1); 309 | }); 310 | 311 | describe('handling a request', function() { 312 | var passport = new Authenticator(); 313 | passport.deserializeUser(function(user, done) { 314 | done(null, { id: user }); 315 | }); 316 | 317 | var request, error; 318 | 319 | before(function(done) { 320 | chai.connect.use(passport.session()) 321 | .req(function(req) { 322 | request = req; 323 | 324 | req._passport = {}; 325 | req._passport.instance = {}; 326 | req.session = {}; 327 | req.session['passport'] = {}; 328 | req.session['passport'].user = '123456'; 329 | }) 330 | .next(function(err) { 331 | error = err; 332 | done(); 333 | }) 334 | .dispatch(); 335 | }); 336 | 337 | it('should not error', function() { 338 | expect(error).to.be.undefined; 339 | }); 340 | 341 | it('should set user', function() { 342 | expect(request.user).to.be.an('object'); 343 | expect(request.user.id).to.equal('123456'); 344 | }); 345 | 346 | it('should maintain session', function() { 347 | expect(request.session['passport']).to.be.an('object'); 348 | expect(request.session['passport'].user).to.equal('123456'); 349 | }); 350 | }); 351 | 352 | }); 353 | 354 | }); 355 | -------------------------------------------------------------------------------- /test/bootstrap/node.js: -------------------------------------------------------------------------------- 1 | var chai = require('chai'); 2 | 3 | chai.use(require('chai-connect-middleware')); 4 | chai.use(require('chai-passport-strategy')); 5 | 6 | global.$require = require('proxyquire'); 7 | global.expect = chai.expect; 8 | -------------------------------------------------------------------------------- /test/middleware/authenticate.error.callback.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('error with callback', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | this.error(new Error('something is wrong')); 16 | }; 17 | 18 | var passport = new Passport(); 19 | passport.use('error', new Strategy()); 20 | 21 | var request, error, user; 22 | 23 | before(function(done) { 24 | function callback(e, u) { 25 | error = e; 26 | user = u; 27 | done(); 28 | } 29 | 30 | chai.connect.use(authenticate(passport, 'error', callback)) 31 | .req(function(req) { 32 | request = req; 33 | }) 34 | .dispatch(); 35 | }); 36 | 37 | it('should pass error to callback', function() { 38 | expect(error).to.be.an.instanceOf(Error); 39 | expect(error.message).to.equal('something is wrong'); 40 | }); 41 | 42 | it('should pass user as undefined to callback', function() { 43 | expect(request.user).to.be.undefined; 44 | }); 45 | 46 | it('should not set user on request', function() { 47 | expect(request.user).to.be.undefined; 48 | }); 49 | }); 50 | 51 | describe('error with callback and options passed to middleware', function() { 52 | function Strategy() { 53 | } 54 | Strategy.prototype.authenticate = function(req) { 55 | this.error(new Error('something is wrong')); 56 | }; 57 | 58 | var passport = new Passport(); 59 | passport.use('error', new Strategy()); 60 | 61 | var request, error, user; 62 | 63 | before(function(done) { 64 | function callback(e, u) { 65 | error = e; 66 | user = u; 67 | done(); 68 | } 69 | 70 | chai.connect.use(authenticate(passport, 'error', { foo: 'bar' }, callback)) 71 | .req(function(req) { 72 | request = req; 73 | }) 74 | .dispatch(); 75 | }); 76 | 77 | it('should pass error to callback', function() { 78 | expect(error).to.be.an.instanceOf(Error); 79 | expect(error.message).to.equal('something is wrong'); 80 | }); 81 | 82 | it('should pass user as undefined to callback', function() { 83 | expect(request.user).to.be.undefined; 84 | }); 85 | 86 | it('should not set user on request', function() { 87 | expect(request.user).to.be.undefined; 88 | }); 89 | }); 90 | 91 | }); 92 | -------------------------------------------------------------------------------- /test/middleware/authenticate.error.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('error', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | this.error(new Error('something is wrong')); 16 | }; 17 | 18 | var passport = new Passport(); 19 | passport.use('error', new Strategy()); 20 | 21 | var request, error; 22 | 23 | before(function(done) { 24 | chai.connect.use(authenticate(passport, 'error')) 25 | .req(function(req) { 26 | request = req; 27 | }) 28 | .next(function(err) { 29 | error = err; 30 | done(); 31 | }) 32 | .dispatch(); 33 | }); 34 | 35 | it('should error', function() { 36 | expect(error).to.be.an.instanceOf(Error); 37 | expect(error.message).to.equal('something is wrong'); 38 | }); 39 | 40 | it('should not set user', function() { 41 | expect(request.user).to.be.undefined; 42 | }); 43 | }); 44 | 45 | }); 46 | -------------------------------------------------------------------------------- /test/middleware/authenticate.fail.callback.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('fail with callback', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | this.fail(); 16 | }; 17 | 18 | var passport = new Passport(); 19 | passport.use('fail', new Strategy()); 20 | 21 | var request, error, user; 22 | 23 | before(function(done) { 24 | function callback(e, u) { 25 | error = e; 26 | user = u; 27 | done(); 28 | } 29 | 30 | chai.connect.use(authenticate(passport, 'fail', callback)) 31 | .req(function(req) { 32 | request = req; 33 | }) 34 | .dispatch(); 35 | }); 36 | 37 | it('should not error', function() { 38 | expect(error).to.be.null; 39 | }); 40 | 41 | it('should pass false to callback', function() { 42 | expect(user).to.equal(false); 43 | }); 44 | 45 | it('should not set user on request', function() { 46 | expect(request.user).to.be.undefined; 47 | }); 48 | }); 49 | 50 | describe('fail with callback, passing info', function() { 51 | function Strategy() { 52 | } 53 | Strategy.prototype.authenticate = function(req) { 54 | this.fail({ message: 'Invalid password' }); 55 | }; 56 | 57 | var passport = new Passport(); 58 | passport.use('fail', new Strategy()); 59 | 60 | var request, error, user, info, status; 61 | 62 | before(function(done) { 63 | function callback(e, u, i, s) { 64 | error = e; 65 | user = u; 66 | info = i; 67 | status = s; 68 | done(); 69 | } 70 | 71 | chai.connect.use(authenticate(passport, 'fail', callback)) 72 | .req(function(req) { 73 | request = req; 74 | }) 75 | .dispatch(); 76 | }); 77 | 78 | it('should not error', function() { 79 | expect(error).to.be.null; 80 | }); 81 | 82 | it('should pass false to callback', function() { 83 | expect(user).to.equal(false); 84 | }); 85 | 86 | it('should pass info to callback', function() { 87 | expect(info).to.be.an('object'); 88 | expect(info.message).to.equal('Invalid password'); 89 | }); 90 | 91 | it('should pass status to callback', function() { 92 | expect(status).to.be.undefined; 93 | }); 94 | 95 | it('should not set user on request', function() { 96 | expect(request.user).to.be.undefined; 97 | }); 98 | }); 99 | 100 | describe('fail with callback, passing info and status', function() { 101 | function Strategy() { 102 | } 103 | Strategy.prototype.authenticate = function(req) { 104 | this.fail({ message: 'Invalid password' }, 403); 105 | }; 106 | 107 | var passport = new Passport(); 108 | passport.use('fail', new Strategy()); 109 | 110 | var request, error, user, info, status; 111 | 112 | before(function(done) { 113 | function callback(e, u, i, s) { 114 | error = e; 115 | user = u; 116 | info = i; 117 | status = s; 118 | done(); 119 | } 120 | 121 | chai.connect.use(authenticate(passport, 'fail', callback)) 122 | .req(function(req) { 123 | request = req; 124 | }) 125 | .dispatch(); 126 | }); 127 | 128 | it('should not error', function() { 129 | expect(error).to.be.null; 130 | }); 131 | 132 | it('should pass false to callback', function() { 133 | expect(user).to.equal(false); 134 | }); 135 | 136 | it('should pass info to callback', function() { 137 | expect(info).to.be.an('object'); 138 | expect(info.message).to.equal('Invalid password'); 139 | }); 140 | 141 | it('should pass status to callback', function() { 142 | expect(status).to.equal(403); 143 | }); 144 | 145 | it('should not set user on request', function() { 146 | expect(request.user).to.be.undefined; 147 | }); 148 | }); 149 | 150 | describe('fail with callback, passing challenge', function() { 151 | function Strategy() { 152 | } 153 | Strategy.prototype.authenticate = function(req) { 154 | this.fail('Bearer challenge'); 155 | }; 156 | 157 | var passport = new Passport(); 158 | passport.use('fail', new Strategy()); 159 | 160 | var request, error, user, challenge, status; 161 | 162 | before(function(done) { 163 | function callback(e, u, c, s) { 164 | error = e; 165 | user = u; 166 | challenge = c; 167 | status = s; 168 | done(); 169 | } 170 | 171 | chai.connect.use(authenticate(passport, 'fail', callback)) 172 | .req(function(req) { 173 | request = req; 174 | }) 175 | .dispatch(); 176 | }); 177 | 178 | it('should not error', function() { 179 | expect(error).to.be.null; 180 | }); 181 | 182 | it('should pass false to callback', function() { 183 | expect(user).to.equal(false); 184 | }); 185 | 186 | it('should pass challenge to callback', function() { 187 | expect(challenge).to.equal('Bearer challenge'); 188 | }); 189 | 190 | it('should pass status to callback', function() { 191 | expect(status).to.be.undefined; 192 | }); 193 | 194 | it('should not set user on request', function() { 195 | expect(request.user).to.be.undefined; 196 | }); 197 | }); 198 | 199 | describe('fail with callback, passing challenge and status', function() { 200 | function Strategy() { 201 | } 202 | Strategy.prototype.authenticate = function(req) { 203 | this.fail('Bearer challenge', 403); 204 | }; 205 | 206 | var passport = new Passport(); 207 | passport.use('fail', new Strategy()); 208 | 209 | var request, error, user, challenge, status; 210 | 211 | before(function(done) { 212 | function callback(e, u, c, s) { 213 | error = e; 214 | user = u; 215 | challenge = c; 216 | status = s; 217 | done(); 218 | } 219 | 220 | chai.connect.use(authenticate(passport, 'fail', callback)) 221 | .req(function(req) { 222 | request = req; 223 | }) 224 | .dispatch(); 225 | }); 226 | 227 | it('should not error', function() { 228 | expect(error).to.be.null; 229 | }); 230 | 231 | it('should pass false to callback', function() { 232 | expect(user).to.equal(false); 233 | }); 234 | 235 | it('should pass challenge to callback', function() { 236 | expect(challenge).to.equal('Bearer challenge'); 237 | }); 238 | 239 | it('should pass status to callback', function() { 240 | expect(status).to.equal(403); 241 | }); 242 | 243 | it('should not set user on request', function() { 244 | expect(request.user).to.be.undefined; 245 | }); 246 | }); 247 | 248 | describe('fail with callback, passing status', function() { 249 | function Strategy() { 250 | } 251 | Strategy.prototype.authenticate = function(req) { 252 | this.fail(402); 253 | }; 254 | 255 | var passport = new Passport(); 256 | passport.use('fail', new Strategy()); 257 | 258 | var request, error, user, challenge, status; 259 | 260 | before(function(done) { 261 | function callback(e, u, c, s) { 262 | error = e; 263 | user = u; 264 | challenge = c; 265 | status = s; 266 | done(); 267 | } 268 | 269 | chai.connect.use(authenticate(passport, 'fail', callback)) 270 | .req(function(req) { 271 | request = req; 272 | }) 273 | .dispatch(); 274 | }); 275 | 276 | it('should not error', function() { 277 | expect(error).to.be.null; 278 | }); 279 | 280 | it('should pass false to callback', function() { 281 | expect(user).to.equal(false); 282 | }); 283 | 284 | it('should pass challenge to callback', function() { 285 | expect(challenge).to.be.undefined; 286 | }); 287 | 288 | it('should pass status to callback', function() { 289 | expect(status).to.equal(402); 290 | }); 291 | 292 | it('should not set user on request', function() { 293 | expect(request.user).to.be.undefined; 294 | }); 295 | }); 296 | 297 | describe('fail with callback and options passed to middleware', function() { 298 | function Strategy() { 299 | } 300 | Strategy.prototype.authenticate = function(req) { 301 | this.fail(); 302 | }; 303 | 304 | var passport = new Passport(); 305 | passport.use('fail', new Strategy()); 306 | 307 | var request, error, user; 308 | 309 | before(function(done) { 310 | function callback(e, u) { 311 | error = e; 312 | user = u; 313 | done(); 314 | } 315 | 316 | chai.connect.use(authenticate(passport, 'fail', { foo: 'bar' }, callback)) 317 | .req(function(req) { 318 | request = req; 319 | }) 320 | .dispatch(); 321 | }); 322 | 323 | it('should not error', function() { 324 | expect(error).to.be.null; 325 | }); 326 | 327 | it('should pass false to callback', function() { 328 | expect(user).to.equal(false); 329 | }); 330 | 331 | it('should not set user on request', function() { 332 | expect(request.user).to.be.undefined; 333 | }); 334 | }); 335 | 336 | }); 337 | -------------------------------------------------------------------------------- /test/middleware/authenticate.fail.message.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('fail with message set by route', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | this.fail({ message: 'Invalid password' }); 16 | }; 17 | 18 | var passport = new Passport(); 19 | passport.use('fail', new Strategy()); 20 | 21 | var request, response; 22 | 23 | before(function(done) { 24 | chai.connect.use('express', authenticate(passport, 'fail', { failureMessage: 'Wrong credentials', 25 | failureRedirect: 'http://www.example.com/login' })) 26 | .req(function(req) { 27 | request = req; 28 | req.session = {}; 29 | }) 30 | .end(function(res) { 31 | response = res; 32 | done(); 33 | }) 34 | .dispatch(); 35 | }); 36 | 37 | it('should not set user', function() { 38 | expect(request.user).to.be.undefined; 39 | }); 40 | 41 | it('should add message to session', function() { 42 | expect(request.session.messages).to.have.length(1); 43 | expect(request.session.messages[0]).to.equal('Wrong credentials'); 44 | }); 45 | 46 | it('should redirect', function() { 47 | expect(response.statusCode).to.equal(302); 48 | expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); 49 | }); 50 | }); 51 | 52 | describe('fail with message set by route that is added to messages', function() { 53 | function Strategy() { 54 | } 55 | Strategy.prototype.authenticate = function(req) { 56 | this.fail({ message: 'Invalid password' }); 57 | }; 58 | 59 | var passport = new Passport(); 60 | passport.use('fail', new Strategy()); 61 | 62 | var request, response; 63 | 64 | before(function(done) { 65 | chai.connect.use('express', authenticate(passport, 'fail', { failureMessage: 'Wrong credentials', 66 | failureRedirect: 'http://www.example.com/login' })) 67 | .req(function(req) { 68 | request = req; 69 | req.session = {}; 70 | req.session.messages = [ 'I exist!' ]; 71 | }) 72 | .end(function(res) { 73 | response = res; 74 | done(); 75 | }) 76 | .dispatch(); 77 | }); 78 | 79 | it('should not set user', function() { 80 | expect(request.user).to.be.undefined; 81 | }); 82 | 83 | it('should add message to session', function() { 84 | expect(request.session.messages).to.have.length(2); 85 | expect(request.session.messages[0]).to.equal('I exist!'); 86 | expect(request.session.messages[1]).to.equal('Wrong credentials'); 87 | }); 88 | 89 | it('should redirect', function() { 90 | expect(response.statusCode).to.equal(302); 91 | expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); 92 | }); 93 | }); 94 | 95 | describe('fail with message set by strategy', function() { 96 | function Strategy() { 97 | } 98 | Strategy.prototype.authenticate = function(req) { 99 | this.fail({ message: 'Invalid password' }); 100 | }; 101 | 102 | var passport = new Passport(); 103 | passport.use('fail', new Strategy()); 104 | 105 | var request, response; 106 | 107 | before(function(done) { 108 | chai.connect.use('express', authenticate(passport, 'fail', { failureMessage: true, 109 | failureRedirect: 'http://www.example.com/login' })) 110 | .req(function(req) { 111 | request = req; 112 | req.session = {}; 113 | }) 114 | .end(function(res) { 115 | response = res; 116 | done(); 117 | }) 118 | .dispatch(); 119 | }); 120 | 121 | it('should not set user', function() { 122 | expect(request.user).to.be.undefined; 123 | }); 124 | 125 | it('should add message to session', function() { 126 | expect(request.session.messages).to.have.length(1); 127 | expect(request.session.messages[0]).to.equal('Invalid password'); 128 | }); 129 | 130 | it('should redirect', function() { 131 | expect(response.statusCode).to.equal(302); 132 | expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); 133 | }); 134 | }); 135 | 136 | describe('fail with message set by strategy with extra info', function() { 137 | function Strategy() { 138 | } 139 | Strategy.prototype.authenticate = function(req) { 140 | this.fail({ message: 'Invalid password', scope: 'read' }); 141 | }; 142 | 143 | var passport = new Passport(); 144 | passport.use('fail', new Strategy()); 145 | 146 | var request, response; 147 | 148 | before(function(done) { 149 | chai.connect.use('express', authenticate(passport, 'fail', { failureMessage: true, 150 | failureRedirect: 'http://www.example.com/login' })) 151 | .req(function(req) { 152 | request = req; 153 | req.session = {}; 154 | }) 155 | .end(function(res) { 156 | response = res; 157 | done(); 158 | }) 159 | .dispatch(); 160 | }); 161 | 162 | it('should not set user', function() { 163 | expect(request.user).to.be.undefined; 164 | }); 165 | 166 | it('should add message to session', function() { 167 | expect(request.session.messages).to.have.length(1); 168 | expect(request.session.messages[0]).to.equal('Invalid password'); 169 | }); 170 | 171 | it('should redirect', function() { 172 | expect(response.statusCode).to.equal(302); 173 | expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); 174 | }); 175 | }); 176 | 177 | }); 178 | -------------------------------------------------------------------------------- /test/middleware/authenticate.fail.multi.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('with multiple strategies, all of which fail, and responding with unauthorized status', function() { 12 | function BasicStrategy() { 13 | } 14 | BasicStrategy.prototype.authenticate = function(req) { 15 | this.fail('BASIC challenge'); 16 | }; 17 | 18 | function DigestStrategy() { 19 | } 20 | DigestStrategy.prototype.authenticate = function(req) { 21 | this.fail('DIGEST challenge'); 22 | }; 23 | 24 | function NoChallengeStrategy() { 25 | } 26 | NoChallengeStrategy.prototype.authenticate = function(req) { 27 | this.fail(); 28 | }; 29 | 30 | var passport = new Passport(); 31 | passport.use('basic', new BasicStrategy()); 32 | passport.use('digest', new DigestStrategy()); 33 | passport.use('no-challenge', new NoChallengeStrategy()); 34 | 35 | var request, response; 36 | 37 | before(function(done) { 38 | chai.connect.use(authenticate(passport, ['basic', 'no-challenge', 'digest'])) 39 | .req(function(req) { 40 | request = req; 41 | 42 | req.flash = function(type, msg) { 43 | this.message = { type: type, msg: msg }; 44 | }; 45 | }) 46 | .end(function(res) { 47 | response = res; 48 | done(); 49 | }) 50 | .dispatch(); 51 | }); 52 | 53 | it('should not set user', function() { 54 | expect(request.user).to.be.undefined; 55 | }); 56 | 57 | it('should respond', function() { 58 | expect(response.statusCode).to.equal(401); 59 | expect(response.body).to.equal('Unauthorized'); 60 | }); 61 | 62 | it('should set authenticate header on response', function() { 63 | var val = response.getHeader('WWW-Authenticate'); 64 | expect(val).to.be.an('array'); 65 | expect(val).to.have.length(2); 66 | 67 | expect(val[0]).to.equal('BASIC challenge'); 68 | expect(val[1]).to.equal('DIGEST challenge'); 69 | }); 70 | }); 71 | 72 | describe('with multiple strategies, all of which fail, and responding with specified status', function() { 73 | function BasicStrategy() { 74 | } 75 | BasicStrategy.prototype.authenticate = function(req) { 76 | this.fail('BASIC challenge', 400); 77 | }; 78 | 79 | function BearerStrategy() { 80 | } 81 | BearerStrategy.prototype.authenticate = function(req) { 82 | this.fail('BEARER challenge', 403); 83 | }; 84 | 85 | function NoChallengeStrategy() { 86 | } 87 | NoChallengeStrategy.prototype.authenticate = function(req) { 88 | this.fail(402); 89 | }; 90 | 91 | var passport = new Passport(); 92 | passport.use('basic', new BasicStrategy()); 93 | passport.use('bearer', new BearerStrategy()); 94 | passport.use('no-challenge', new NoChallengeStrategy()); 95 | 96 | var request, response; 97 | 98 | before(function(done) { 99 | chai.connect.use(authenticate(passport, ['basic', 'no-challenge', 'bearer'])) 100 | .req(function(req) { 101 | request = req; 102 | 103 | req.flash = function(type, msg) { 104 | this.message = { type: type, msg: msg }; 105 | }; 106 | }) 107 | .end(function(res) { 108 | response = res; 109 | done(); 110 | }) 111 | .dispatch(); 112 | }); 113 | 114 | it('should not set user', function() { 115 | expect(request.user).to.be.undefined; 116 | }); 117 | 118 | it('should respond', function() { 119 | expect(response.statusCode).to.equal(400); 120 | expect(response.getHeader('WWW-Authenticate')).to.be.undefined; 121 | expect(response.body).to.equal('Bad Request'); 122 | }); 123 | }); 124 | 125 | describe('with multiple strategies, all of which fail, and flashing message', function() { 126 | function StrategyA() { 127 | } 128 | StrategyA.prototype.authenticate = function(req) { 129 | this.fail('A message'); 130 | }; 131 | 132 | function StrategyB() { 133 | } 134 | StrategyB.prototype.authenticate = function(req) { 135 | this.fail('B message'); 136 | }; 137 | 138 | var passport = new Passport(); 139 | passport.use('a', new StrategyA()); 140 | passport.use('b', new StrategyB()); 141 | 142 | var request, response; 143 | 144 | before(function(done) { 145 | chai.connect.use('express', authenticate(passport, ['a', 'b'], { failureFlash: true, 146 | failureRedirect: 'http://www.example.com/login' })) 147 | .req(function(req) { 148 | request = req; 149 | 150 | req.flash = function(type, msg) { 151 | this.message = { type: type, msg: msg }; 152 | }; 153 | }) 154 | .end(function(res) { 155 | response = res; 156 | done(); 157 | }) 158 | .dispatch(); 159 | }); 160 | 161 | it('should not set user', function() { 162 | expect(request.user).to.be.undefined; 163 | }); 164 | 165 | it('should flash message', function() { 166 | expect(request.message.type).to.equal('error'); 167 | expect(request.message.msg).to.equal('A message'); 168 | }); 169 | 170 | it('should redirect', function() { 171 | expect(response.statusCode).to.equal(302); 172 | expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); 173 | }); 174 | }); 175 | 176 | describe('with multiple strategies, all of which fail with unauthorized status, and invoking callback', function() { 177 | function BasicStrategy() { 178 | } 179 | BasicStrategy.prototype.authenticate = function(req) { 180 | this.fail('BASIC challenge'); 181 | }; 182 | 183 | function DigestStrategy() { 184 | } 185 | DigestStrategy.prototype.authenticate = function(req) { 186 | this.fail('DIGEST challenge'); 187 | }; 188 | 189 | function NoChallengeStrategy() { 190 | } 191 | NoChallengeStrategy.prototype.authenticate = function(req) { 192 | this.fail(); 193 | }; 194 | 195 | var passport = new Passport(); 196 | passport.use('basic', new BasicStrategy()); 197 | passport.use('digest', new DigestStrategy()); 198 | passport.use('no-challenge', new NoChallengeStrategy()); 199 | 200 | var request, error, user, challenge, status; 201 | 202 | before(function(done) { 203 | function callback(e, u, c, s) { 204 | error = e; 205 | user = u; 206 | challenge = c; 207 | status = s; 208 | done(); 209 | } 210 | 211 | chai.connect.use(authenticate(passport, ['basic', 'no-challenge', 'digest'], callback)) 212 | .req(function(req) { 213 | request = req; 214 | }) 215 | .dispatch(); 216 | }); 217 | 218 | it('should not error', function() { 219 | expect(error).to.be.null; 220 | }); 221 | 222 | it('should pass false to callback', function() { 223 | expect(user).to.equal(false); 224 | }); 225 | 226 | it('should pass challenges to callback', function() { 227 | expect(challenge).to.be.an('array'); 228 | expect(challenge).to.have.length(3); 229 | expect(challenge[0]).to.equal('BASIC challenge'); 230 | expect(challenge[1]).to.be.undefined; 231 | expect(challenge[2]).to.equal('DIGEST challenge'); 232 | }); 233 | 234 | it('should pass statuses to callback', function() { 235 | expect(status).to.be.an('array'); 236 | expect(status).to.have.length(3); 237 | expect(status[0]).to.be.undefined; 238 | expect(status[1]).to.be.undefined; 239 | expect(status[2]).to.be.undefined; 240 | }); 241 | 242 | it('should not set user on request', function() { 243 | expect(request.user).to.be.undefined; 244 | }); 245 | }); 246 | 247 | describe('with multiple strategies, all of which fail with specific status, and invoking callback', function() { 248 | function BasicStrategy() { 249 | } 250 | BasicStrategy.prototype.authenticate = function(req) { 251 | this.fail('BASIC challenge', 400); 252 | }; 253 | 254 | function BearerStrategy() { 255 | } 256 | BearerStrategy.prototype.authenticate = function(req) { 257 | this.fail('BEARER challenge', 403); 258 | }; 259 | 260 | function NoChallengeStrategy() { 261 | } 262 | NoChallengeStrategy.prototype.authenticate = function(req) { 263 | this.fail(402); 264 | }; 265 | 266 | var passport = new Passport(); 267 | passport.use('basic', new BasicStrategy()); 268 | passport.use('bearer', new BearerStrategy()); 269 | passport.use('no-challenge', new NoChallengeStrategy()); 270 | 271 | var request, error, user, challenge, status; 272 | 273 | before(function(done) { 274 | function callback(e, u, c, s) { 275 | error = e; 276 | user = u; 277 | challenge = c; 278 | status = s; 279 | done(); 280 | } 281 | 282 | chai.connect.use(authenticate(passport, ['basic', 'no-challenge', 'bearer'], callback)) 283 | .req(function(req) { 284 | request = req; 285 | }) 286 | .dispatch(); 287 | }); 288 | 289 | it('should not error', function() { 290 | expect(error).to.be.null; 291 | }); 292 | 293 | it('should pass false to callback', function() { 294 | expect(user).to.equal(false); 295 | }); 296 | 297 | it('should pass challenges to callback', function() { 298 | expect(challenge).to.be.an('array'); 299 | expect(challenge).to.have.length(3); 300 | expect(challenge[0]).to.equal('BASIC challenge'); 301 | expect(challenge[1]).to.be.undefined; 302 | expect(challenge[2]).to.equal('BEARER challenge'); 303 | }); 304 | 305 | it('should pass statuses to callback', function() { 306 | expect(status).to.be.an('array'); 307 | expect(status).to.have.length(3); 308 | expect(status[0]).to.equal(400); 309 | expect(status[1]).to.equal(402); 310 | expect(status[2]).to.equal(403); 311 | }); 312 | 313 | it('should not set user on request', function() { 314 | expect(request.user).to.be.undefined; 315 | }); 316 | }); 317 | 318 | describe('with single strategy in list, which fails with unauthorized status, and invoking callback', function() { 319 | function BasicStrategy() { 320 | } 321 | BasicStrategy.prototype.authenticate = function(req) { 322 | this.fail('BASIC challenge'); 323 | }; 324 | 325 | var passport = new Passport(); 326 | passport.use('basic', new BasicStrategy()); 327 | 328 | var request, error, user, challenge, status; 329 | 330 | before(function(done) { 331 | function callback(e, u, c, s) { 332 | error = e; 333 | user = u; 334 | challenge = c; 335 | status = s; 336 | done(); 337 | } 338 | 339 | chai.connect.use(authenticate(passport, ['basic'], callback)) 340 | .req(function(req) { 341 | request = req; 342 | }) 343 | .dispatch(); 344 | }); 345 | 346 | it('should not error', function() { 347 | expect(error).to.be.null; 348 | }); 349 | 350 | it('should pass false to callback', function() { 351 | expect(user).to.equal(false); 352 | }); 353 | 354 | it('should pass challenges to callback', function() { 355 | expect(challenge).to.be.an('array'); 356 | expect(challenge).to.have.length(1); 357 | expect(challenge[0]).to.equal('BASIC challenge'); 358 | }); 359 | 360 | it('should pass statuses to callback', function() { 361 | expect(status).to.be.an('array'); 362 | expect(status).to.have.length(1); 363 | expect(status[0]).to.be.undefined; 364 | }); 365 | 366 | it('should not set user on request', function() { 367 | expect(request.user).to.be.undefined; 368 | }); 369 | }); 370 | 371 | }); 372 | -------------------------------------------------------------------------------- /test/middleware/authenticate.fail.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('fail', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | this.fail(); 16 | }; 17 | 18 | var passport = new Passport(); 19 | passport.use('fail', new Strategy()); 20 | 21 | var request, response; 22 | 23 | before(function(done) { 24 | chai.connect.use(authenticate(passport, 'fail')) 25 | .req(function(req) { 26 | request = req; 27 | }) 28 | .end(function(res) { 29 | response = res; 30 | done(); 31 | }) 32 | .dispatch(); 33 | }); 34 | 35 | it('should not set user', function() { 36 | expect(request.user).to.be.undefined; 37 | }); 38 | 39 | it('should respond', function() { 40 | expect(response.statusCode).to.equal(401); 41 | expect(response.getHeader('WWW-Authenticate')).to.be.undefined; 42 | expect(response.body).to.equal('Unauthorized'); 43 | }); 44 | }); 45 | 46 | describe('fail with redirect', function() { 47 | function Strategy() { 48 | } 49 | Strategy.prototype.authenticate = function(req) { 50 | this.fail(); 51 | }; 52 | 53 | var passport = new Passport(); 54 | passport.use('fail', new Strategy()); 55 | 56 | var request, response; 57 | 58 | before(function(done) { 59 | chai.connect.use('express', authenticate(passport, 'fail', { failureRedirect: 'http://www.example.com/login' })) 60 | .req(function(req) { 61 | request = req; 62 | }) 63 | .end(function(res) { 64 | response = res; 65 | done(); 66 | }) 67 | .dispatch(); 68 | }); 69 | 70 | it('should not set user', function() { 71 | expect(request.user).to.be.undefined; 72 | }); 73 | 74 | it('should redirect', function() { 75 | expect(response.statusCode).to.equal(302); 76 | expect(response.getHeader('Location')).to.equal('http://www.example.com/login'); 77 | }); 78 | }); 79 | 80 | describe('fail with challenge', function() { 81 | function Strategy() { 82 | } 83 | Strategy.prototype.authenticate = function(req) { 84 | this.fail('MOCK challenge'); 85 | }; 86 | 87 | var passport = new Passport(); 88 | passport.use('fail', new Strategy()); 89 | 90 | var request, response; 91 | 92 | before(function(done) { 93 | chai.connect.use(authenticate(passport, 'fail')) 94 | .req(function(req) { 95 | request = req; 96 | }) 97 | .end(function(res) { 98 | response = res; 99 | done(); 100 | }) 101 | .dispatch(); 102 | }); 103 | 104 | it('should not set user', function() { 105 | expect(request.user).to.be.undefined; 106 | }); 107 | 108 | it('should respond', function() { 109 | expect(response.statusCode).to.equal(401); 110 | expect(response.body).to.equal('Unauthorized'); 111 | }); 112 | 113 | it('should set authenticate header on response', function() { 114 | var val = response.getHeader('WWW-Authenticate'); 115 | expect(val).to.be.an('array'); 116 | expect(val).to.have.length(1); 117 | 118 | expect(val[0]).to.equal('MOCK challenge'); 119 | }); 120 | }); 121 | 122 | describe('fail with challenge and status', function() { 123 | function Strategy() { 124 | } 125 | Strategy.prototype.authenticate = function(req) { 126 | this.fail('MOCK challenge', 403); 127 | }; 128 | 129 | var passport = new Passport(); 130 | passport.use('fail', new Strategy()); 131 | 132 | var request, response; 133 | 134 | before(function(done) { 135 | chai.connect.use(authenticate(passport, 'fail')) 136 | .req(function(req) { 137 | request = req; 138 | }) 139 | .end(function(res) { 140 | response = res; 141 | done(); 142 | }) 143 | .dispatch(); 144 | }); 145 | 146 | it('should not set user', function() { 147 | expect(request.user).to.be.undefined; 148 | }); 149 | 150 | it('should respond', function() { 151 | expect(response.statusCode).to.equal(403); 152 | expect(response.getHeader('WWW-Authenticate')).to.be.undefined; 153 | expect(response.body).to.equal('Forbidden'); 154 | }); 155 | }); 156 | 157 | describe('fail with status', function() { 158 | function Strategy() { 159 | } 160 | Strategy.prototype.authenticate = function(req) { 161 | this.fail(400); 162 | }; 163 | 164 | var passport = new Passport(); 165 | passport.use('fail', new Strategy()); 166 | 167 | var request, response; 168 | 169 | before(function(done) { 170 | chai.connect.use(authenticate(passport, 'fail')) 171 | .req(function(req) { 172 | request = req; 173 | }) 174 | .end(function(res) { 175 | response = res; 176 | done(); 177 | }) 178 | .dispatch(); 179 | }); 180 | 181 | it('should not set user', function() { 182 | expect(request.user).to.be.undefined; 183 | }); 184 | 185 | it('should respond', function() { 186 | expect(response.statusCode).to.equal(400); 187 | expect(response.getHeader('WWW-Authenticate')).to.be.undefined; 188 | expect(response.body).to.equal('Bad Request'); 189 | }); 190 | }); 191 | 192 | describe('fail with error', function() { 193 | function Strategy() { 194 | } 195 | Strategy.prototype.authenticate = function(req) { 196 | this.fail(); 197 | }; 198 | 199 | var passport = new Passport(); 200 | passport.use('fail', new Strategy()); 201 | 202 | var request, response, error; 203 | 204 | before(function(done) { 205 | chai.connect.use('express', authenticate(passport, 'fail', { failWithError: true })) 206 | .req(function(req) { 207 | request = req; 208 | }) 209 | .res(function(res) { 210 | response = res; 211 | }) 212 | .next(function(err) { 213 | error = err; 214 | done(); 215 | }) 216 | .dispatch(); 217 | }); 218 | 219 | it('should error', function() { 220 | expect(error).to.be.an.instanceOf(Error); 221 | expect(error.constructor.name).to.equal('AuthenticationError'); 222 | expect(error.message).to.equal('Unauthorized'); 223 | expect(error.status).to.equal(401); 224 | }); 225 | 226 | it('should not set user', function() { 227 | expect(request.user).to.be.undefined; 228 | }); 229 | 230 | it('should not set body of response', function() { 231 | expect(response.statusCode).to.equal(401); 232 | expect(response.getHeader('WWW-Authenticate')).to.be.undefined; 233 | expect(response.body).to.be.undefined; 234 | }); 235 | }); 236 | 237 | describe('fail with error, passing info to fail', function() { 238 | function Strategy() { 239 | } 240 | Strategy.prototype.authenticate = function(req) { 241 | this.fail({ message: 'Invalid credentials' }); 242 | }; 243 | 244 | var passport = new Passport(); 245 | passport.use('fail', new Strategy()); 246 | 247 | var request, response, error; 248 | 249 | before(function(done) { 250 | chai.connect.use('express', authenticate(passport, 'fail', { failWithError: true })) 251 | .req(function(req) { 252 | request = req; 253 | }) 254 | .res(function(res) { 255 | response = res; 256 | }) 257 | .next(function(err) { 258 | error = err; 259 | done(); 260 | }) 261 | .dispatch(); 262 | }); 263 | 264 | it('should error', function() { 265 | expect(error).to.be.an.instanceOf(Error); 266 | expect(error.constructor.name).to.equal('AuthenticationError'); 267 | expect(error.message).to.equal('Unauthorized'); 268 | expect(error.status).to.equal(401); 269 | }); 270 | 271 | it('should not set user', function() { 272 | expect(request.user).to.be.undefined; 273 | }); 274 | 275 | it('should not set body of response', function() { 276 | expect(response.statusCode).to.equal(401); 277 | expect(response.getHeader('WWW-Authenticate')).to.be.undefined; 278 | expect(response.body).to.be.undefined; 279 | }); 280 | }); 281 | 282 | describe('fail with error, passing info and status to fail', function() { 283 | function Strategy() { 284 | } 285 | Strategy.prototype.authenticate = function(req) { 286 | this.fail({ message: 'Multiple credentials' }, 400); 287 | }; 288 | 289 | var passport = new Passport(); 290 | passport.use('fail', new Strategy()); 291 | 292 | var request, response, error; 293 | 294 | before(function(done) { 295 | chai.connect.use('express', authenticate(passport, 'fail', { failWithError: true })) 296 | .req(function(req) { 297 | request = req; 298 | }) 299 | .res(function(res) { 300 | response = res; 301 | }) 302 | .next(function(err) { 303 | error = err; 304 | done(); 305 | }) 306 | .dispatch(); 307 | }); 308 | 309 | it('should error', function() { 310 | expect(error).to.be.an.instanceOf(Error); 311 | expect(error.constructor.name).to.equal('AuthenticationError'); 312 | expect(error.message).to.equal('Bad Request'); 313 | expect(error.status).to.equal(400); 314 | }); 315 | 316 | it('should not set user', function() { 317 | expect(request.user).to.be.undefined; 318 | }); 319 | 320 | it('should not set body of response', function() { 321 | expect(response.statusCode).to.equal(400); 322 | expect(response.getHeader('WWW-Authenticate')).to.be.undefined; 323 | expect(response.body).to.be.undefined; 324 | }); 325 | }); 326 | 327 | describe('fail with error, passing challenge to fail', function() { 328 | function Strategy() { 329 | } 330 | Strategy.prototype.authenticate = function(req) { 331 | this.fail('Bearer challenge'); 332 | }; 333 | 334 | var passport = new Passport(); 335 | passport.use('fail', new Strategy()); 336 | 337 | var request, response, error; 338 | 339 | before(function(done) { 340 | chai.connect.use('express', authenticate(passport, 'fail', { failWithError: true })) 341 | .req(function(req) { 342 | request = req; 343 | }) 344 | .res(function(res) { 345 | response = res; 346 | }) 347 | .next(function(err) { 348 | error = err; 349 | done(); 350 | }) 351 | .dispatch(); 352 | }); 353 | 354 | it('should error', function() { 355 | expect(error).to.be.an.instanceOf(Error); 356 | expect(error.constructor.name).to.equal('AuthenticationError'); 357 | expect(error.message).to.equal('Unauthorized'); 358 | expect(error.status).to.equal(401); 359 | }); 360 | 361 | it('should not set user', function() { 362 | expect(request.user).to.be.undefined; 363 | }); 364 | 365 | it('should not set body of response', function() { 366 | expect(response.statusCode).to.equal(401); 367 | expect(response.body).to.be.undefined; 368 | }); 369 | 370 | it('should set authenticate header on response', function() { 371 | var val = response.getHeader('WWW-Authenticate'); 372 | expect(val).to.be.an('array'); 373 | expect(val).to.have.length(1); 374 | 375 | expect(val[0]).to.equal('Bearer challenge'); 376 | }); 377 | }); 378 | 379 | describe('fail with error, passing challenge and status to fail', function() { 380 | function Strategy() { 381 | } 382 | Strategy.prototype.authenticate = function(req) { 383 | this.fail('Bearer challenge', 403); 384 | }; 385 | 386 | var passport = new Passport(); 387 | passport.use('fail', new Strategy()); 388 | 389 | var request, response, error; 390 | 391 | before(function(done) { 392 | chai.connect.use('express', authenticate(passport, 'fail', { failWithError: true })) 393 | .req(function(req) { 394 | request = req; 395 | }) 396 | .res(function(res) { 397 | response = res; 398 | }) 399 | .next(function(err) { 400 | error = err; 401 | done(); 402 | }) 403 | .dispatch(); 404 | }); 405 | 406 | it('should error', function() { 407 | expect(error).to.be.an.instanceOf(Error); 408 | expect(error.constructor.name).to.equal('AuthenticationError'); 409 | expect(error.message).to.equal('Forbidden'); 410 | expect(error.status).to.equal(403); 411 | }); 412 | 413 | it('should not set user', function() { 414 | expect(request.user).to.be.undefined; 415 | }); 416 | 417 | it('should not set body of response', function() { 418 | expect(response.statusCode).to.equal(403); 419 | expect(response.getHeader('WWW-Authenticate')).to.be.undefined; 420 | expect(response.body).to.be.undefined; 421 | }); 422 | }); 423 | 424 | describe('fail with error, passing status to fail', function() { 425 | function Strategy() { 426 | } 427 | Strategy.prototype.authenticate = function(req) { 428 | this.fail(402); 429 | }; 430 | 431 | var passport = new Passport(); 432 | passport.use('fail', new Strategy()); 433 | 434 | var request, response, error; 435 | 436 | before(function(done) { 437 | chai.connect.use('express', authenticate(passport, 'fail', { failWithError: true })) 438 | .req(function(req) { 439 | request = req; 440 | }) 441 | .res(function(res) { 442 | response = res; 443 | }) 444 | .next(function(err) { 445 | error = err; 446 | done(); 447 | }) 448 | .dispatch(); 449 | }); 450 | 451 | it('should error', function() { 452 | expect(error).to.be.an.instanceOf(Error); 453 | expect(error.constructor.name).to.equal('AuthenticationError'); 454 | expect(error.message).to.equal('Payment Required'); 455 | expect(error.status).to.equal(402); 456 | }); 457 | 458 | it('should not set user', function() { 459 | expect(request.user).to.be.undefined; 460 | }); 461 | 462 | it('should not set body of response', function() { 463 | expect(response.statusCode).to.equal(402); 464 | expect(response.getHeader('WWW-Authenticate')).to.be.undefined; 465 | expect(response.body).to.be.undefined; 466 | }); 467 | }); 468 | 469 | }); 470 | -------------------------------------------------------------------------------- /test/middleware/authenticate.pass.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('pass', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | this.pass(); 16 | }; 17 | 18 | var passport = new Passport(); 19 | passport.use('pass', new Strategy()); 20 | 21 | var request, error; 22 | 23 | before(function(done) { 24 | chai.connect.use(authenticate(passport, 'pass')) 25 | .req(function(req) { 26 | request = req; 27 | }) 28 | .next(function(err) { 29 | error = err; 30 | done(); 31 | }) 32 | .dispatch(); 33 | }); 34 | 35 | it('should not error', function() { 36 | expect(error).to.be.undefined; 37 | }); 38 | 39 | it('should not set user', function() { 40 | expect(request.user).to.be.undefined; 41 | }); 42 | }); 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /test/middleware/authenticate.redirect.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('redirect', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | this.redirect('http://www.example.com/idp'); 16 | }; 17 | 18 | var passport = new Passport(); 19 | passport.use('redirect', new Strategy()); 20 | 21 | var request, response; 22 | 23 | before(function(done) { 24 | chai.connect.use(authenticate(passport, 'redirect')) 25 | .req(function(req) { 26 | request = req; 27 | }) 28 | .end(function(res) { 29 | response = res; 30 | done(); 31 | }) 32 | .dispatch(); 33 | }); 34 | 35 | it('should not set user', function() { 36 | expect(request.user).to.be.undefined; 37 | }); 38 | 39 | it('should redirect', function() { 40 | expect(response.statusCode).to.equal(302); 41 | expect(response.getHeader('Location')).to.equal('http://www.example.com/idp'); 42 | expect(response.getHeader('Content-Length')).to.equal('0'); 43 | }); 44 | }); 45 | 46 | describe('redirect with status', function() { 47 | function Strategy() { 48 | } 49 | Strategy.prototype.authenticate = function(req) { 50 | this.redirect('http://www.example.com/idp', 303); 51 | }; 52 | 53 | var passport = new Passport(); 54 | passport.use('redirect', new Strategy()); 55 | 56 | var request, response; 57 | 58 | before(function(done) { 59 | chai.connect.use(authenticate(passport, 'redirect')) 60 | .req(function(req) { 61 | request = req; 62 | }) 63 | .end(function(res) { 64 | response = res; 65 | done(); 66 | }) 67 | .dispatch(); 68 | }); 69 | 70 | it('should not set user', function() { 71 | expect(request.user).to.be.undefined; 72 | }); 73 | 74 | it('should redirect', function() { 75 | expect(response.statusCode).to.equal(303); 76 | expect(response.getHeader('Location')).to.equal('http://www.example.com/idp'); 77 | expect(response.getHeader('Content-Length')).to.equal('0'); 78 | }); 79 | }); 80 | 81 | describe('redirect using framework function', function() { 82 | function Strategy() { 83 | } 84 | Strategy.prototype.authenticate = function(req) { 85 | this.redirect('http://www.example.com/idp'); 86 | }; 87 | 88 | var passport = new Passport(); 89 | passport.use('redirect', new Strategy()); 90 | 91 | var request, response; 92 | 93 | before(function(done) { 94 | chai.connect.use('express', authenticate(passport, 'redirect')) 95 | .req(function(req) { 96 | request = req; 97 | }) 98 | .end(function(res) { 99 | response = res; 100 | done(); 101 | }) 102 | .dispatch(); 103 | }); 104 | 105 | it('should not set user', function() { 106 | expect(request.user).to.be.undefined; 107 | }); 108 | 109 | it('should redirect', function() { 110 | expect(response.statusCode).to.equal(302); 111 | expect(response.getHeader('Location')).to.equal('http://www.example.com/idp'); 112 | }); 113 | }); 114 | 115 | describe('redirect with status using framework function', function() { 116 | function Strategy() { 117 | } 118 | Strategy.prototype.authenticate = function(req) { 119 | this.redirect('http://www.example.com/idp', 303); 120 | }; 121 | 122 | var passport = new Passport(); 123 | passport.use('redirect', new Strategy()); 124 | 125 | var request, response; 126 | 127 | before(function(done) { 128 | chai.connect.use('express', authenticate(passport, 'redirect')) 129 | .req(function(req) { 130 | request = req; 131 | }) 132 | .end(function(res) { 133 | response = res; 134 | done(); 135 | }) 136 | .dispatch(); 137 | }); 138 | 139 | it('should not set user', function() { 140 | expect(request.user).to.be.undefined; 141 | }); 142 | 143 | it('should redirect', function() { 144 | expect(response.statusCode).to.equal(303); 145 | expect(response.getHeader('Location')).to.equal('http://www.example.com/idp'); 146 | }); 147 | }); 148 | 149 | }); 150 | -------------------------------------------------------------------------------- /test/middleware/authenticate.success.callback.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('success with callback', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | var user = { id: '1', username: 'jaredhanson' }; 16 | this.success(user, { message: 'Hello' }); 17 | }; 18 | 19 | var passport = new Passport(); 20 | passport.use('success', new Strategy()); 21 | 22 | var request, error, user, info; 23 | 24 | before(function(done) { 25 | function callback(e, u, i) { 26 | error = e; 27 | user = u; 28 | info = i; 29 | done(); 30 | } 31 | 32 | chai.connect.use(authenticate(passport, 'success', callback)) 33 | .req(function(req) { 34 | request = req; 35 | }) 36 | .dispatch(); 37 | }); 38 | 39 | it('should not error', function() { 40 | expect(error).to.be.null; 41 | }); 42 | 43 | it('should pass user to callback', function() { 44 | expect(user).to.be.an('object'); 45 | expect(user.id).to.equal('1'); 46 | expect(user.username).to.equal('jaredhanson'); 47 | }); 48 | 49 | it('should pass info to callback', function() { 50 | expect(info).to.be.an('object'); 51 | expect(info.message).to.equal('Hello'); 52 | }); 53 | 54 | it('should not set user on request', function() { 55 | expect(request.user).to.be.undefined; 56 | }); 57 | 58 | it('should not set authInfo on request', function() { 59 | expect(request.authInfo).to.be.undefined; 60 | }); 61 | }); 62 | 63 | describe('success with callback and options passed to middleware', function() { 64 | function Strategy() { 65 | } 66 | Strategy.prototype.authenticate = function(req) { 67 | var user = { id: '1', username: 'jaredhanson' }; 68 | this.success(user, { message: 'Hello' }); 69 | }; 70 | 71 | var passport = new Passport(); 72 | passport.use('success', new Strategy()); 73 | 74 | var request, error, user, info; 75 | 76 | before(function(done) { 77 | function callback(e, u, i) { 78 | error = e; 79 | user = u; 80 | info = i; 81 | done(); 82 | } 83 | 84 | chai.connect.use(authenticate(passport, 'success', { foo: 'bar' }, callback)) 85 | .req(function(req) { 86 | request = req; 87 | }) 88 | .dispatch(); 89 | }); 90 | 91 | it('should not error', function() { 92 | expect(error).to.be.null; 93 | }); 94 | 95 | it('should pass user to callback', function() { 96 | expect(user).to.be.an('object'); 97 | expect(user.id).to.equal('1'); 98 | expect(user.username).to.equal('jaredhanson'); 99 | }); 100 | 101 | it('should pass info to callback', function() { 102 | expect(info).to.be.an('object'); 103 | expect(info.message).to.equal('Hello'); 104 | }); 105 | 106 | it('should not set user on request', function() { 107 | expect(request.user).to.be.undefined; 108 | }); 109 | 110 | it('should not set authInfo on request', function() { 111 | expect(request.authInfo).to.be.undefined; 112 | }); 113 | }); 114 | 115 | }); 116 | -------------------------------------------------------------------------------- /test/middleware/authenticate.success.info.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('success with info', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | var user = { id: '1', username: 'jaredhanson' }; 16 | this.success(user, { clientId: '123', scope: 'read' }); 17 | }; 18 | 19 | var passport = new Passport(); 20 | passport.use('success', new Strategy()); 21 | 22 | var request, error; 23 | 24 | before(function(done) { 25 | chai.connect.use(authenticate(passport, 'success')) 26 | .req(function(req) { 27 | request = req; 28 | 29 | req.logIn = function(user, options, done) { 30 | this.user = user; 31 | done(); 32 | }; 33 | }) 34 | .next(function(err) { 35 | error = err; 36 | done(); 37 | }) 38 | .dispatch(); 39 | }); 40 | 41 | it('should not error', function() { 42 | expect(error).to.be.undefined; 43 | }); 44 | 45 | it('should set user', function() { 46 | expect(request.user).to.be.an('object'); 47 | expect(request.user.id).to.equal('1'); 48 | expect(request.user.username).to.equal('jaredhanson'); 49 | }); 50 | 51 | it('should set authInfo', function() { 52 | expect(request.authInfo).to.be.an('object'); 53 | expect(Object.keys(request.authInfo)).to.have.length(2); 54 | expect(request.authInfo.clientId).to.equal('123'); 55 | expect(request.authInfo.scope).to.equal('read'); 56 | }); 57 | }); 58 | 59 | describe('success with info that is transformed', function() { 60 | function Strategy() { 61 | } 62 | Strategy.prototype.authenticate = function(req) { 63 | var user = { id: '1', username: 'jaredhanson' }; 64 | this.success(user, { clientId: '123', scope: 'read' }); 65 | }; 66 | 67 | var passport = new Passport(); 68 | passport.use('success', new Strategy()); 69 | passport.transformAuthInfo(function(info, done) { 70 | done(null, { clientId: info.clientId, client: { name: 'Foo' }, scope: info.scope }); 71 | }); 72 | 73 | var request, error; 74 | 75 | before(function(done) { 76 | chai.connect.use(authenticate(passport, 'success')) 77 | .req(function(req) { 78 | request = req; 79 | 80 | req.logIn = function(user, options, done) { 81 | this.user = user; 82 | done(); 83 | }; 84 | }) 85 | .next(function(err) { 86 | error = err; 87 | done(); 88 | }) 89 | .dispatch(); 90 | }); 91 | 92 | it('should not error', function() { 93 | expect(error).to.be.undefined; 94 | }); 95 | 96 | it('should set user', function() { 97 | expect(request.user).to.be.an('object'); 98 | expect(request.user.id).to.equal('1'); 99 | expect(request.user.username).to.equal('jaredhanson'); 100 | }); 101 | 102 | it('should set authInfo', function() { 103 | expect(request.authInfo).to.be.an('object'); 104 | expect(Object.keys(request.authInfo)).to.have.length(3); 105 | expect(request.authInfo.clientId).to.equal('123'); 106 | expect(request.authInfo.client.name).to.equal('Foo'); 107 | expect(request.authInfo.scope).to.equal('read'); 108 | }); 109 | }); 110 | 111 | describe('success with info, but transform that encounters an error', function() { 112 | function Strategy() { 113 | } 114 | Strategy.prototype.authenticate = function(req) { 115 | var user = { id: '1', username: 'jaredhanson' }; 116 | this.success(user, { clientId: '123', scope: 'read' }); 117 | }; 118 | 119 | var passport = new Passport(); 120 | passport.use('success', new Strategy()); 121 | passport.transformAuthInfo(function(info, done) { 122 | done(new Error('something went wrong')); 123 | }); 124 | 125 | var request, error; 126 | 127 | before(function(done) { 128 | chai.connect.use(authenticate(passport, 'success')) 129 | .req(function(req) { 130 | request = req; 131 | 132 | req.logIn = function(user, options, done) { 133 | this.user = user; 134 | done(); 135 | }; 136 | }) 137 | .next(function(err) { 138 | error = err; 139 | done(); 140 | }) 141 | .dispatch(); 142 | }); 143 | 144 | it('should error', function() { 145 | expect(error).to.be.an.instanceOf(Error); 146 | expect(error.message).to.equal('something went wrong'); 147 | }); 148 | 149 | it('should set user', function() { 150 | expect(request.user).to.be.an('object'); 151 | expect(request.user.id).to.equal('1'); 152 | expect(request.user.username).to.equal('jaredhanson'); 153 | }); 154 | 155 | it('should not set authInfo', function() { 156 | expect(request.authInfo).to.be.undefined; 157 | }); 158 | }); 159 | 160 | describe('success with info, but option that disables info', function() { 161 | function Strategy() { 162 | } 163 | Strategy.prototype.authenticate = function(req) { 164 | var user = { id: '1', username: 'jaredhanson' }; 165 | this.success(user, { clientId: '123', scope: 'read' }); 166 | }; 167 | 168 | var passport = new Passport(); 169 | passport.use('success', new Strategy()); 170 | 171 | var request, error; 172 | 173 | before(function(done) { 174 | chai.connect.use(authenticate(passport, 'success', { authInfo: false })) 175 | .req(function(req) { 176 | request = req; 177 | 178 | req.logIn = function(user, options, done) { 179 | this.user = user; 180 | done(); 181 | }; 182 | }) 183 | .next(function(err) { 184 | error = err; 185 | done(); 186 | }) 187 | .dispatch(); 188 | }); 189 | 190 | it('should not error', function() { 191 | expect(error).to.be.undefined; 192 | }); 193 | 194 | it('should set user', function() { 195 | expect(request.user).to.be.an('object'); 196 | expect(request.user.id).to.equal('1'); 197 | expect(request.user.username).to.equal('jaredhanson'); 198 | }); 199 | 200 | it('should not set authInfo', function() { 201 | expect(request.authInfo).to.be.undefined; 202 | }); 203 | }); 204 | 205 | }); 206 | -------------------------------------------------------------------------------- /test/middleware/authenticate.success.message.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | 3 | var chai = require('chai') 4 | , authenticate = require('../../lib/middleware/authenticate') 5 | , Passport = require('../..').Passport; 6 | 7 | 8 | describe('middleware/authenticate', function() { 9 | 10 | describe('success with message set by route', function() { 11 | function Strategy() { 12 | } 13 | Strategy.prototype.authenticate = function(req, options) { 14 | var user = { id: '1', username: 'jaredhanson' }; 15 | this.success(user, { message: 'Welcome!' }); 16 | }; 17 | 18 | var passport = new Passport(); 19 | passport.use('success', new Strategy()); 20 | 21 | var request, response; 22 | 23 | before(function(done) { 24 | chai.connect.use('express', authenticate(passport, 'success', { successMessage: 'Login complete', 25 | successRedirect: 'http://www.example.com/account' })) 26 | .req(function(req) { 27 | request = req; 28 | req.session = {}; 29 | 30 | req.logIn = function(user, options, done) { 31 | this.user = user; 32 | done(); 33 | }; 34 | }) 35 | .end(function(res) { 36 | response = res; 37 | done(); 38 | }) 39 | .dispatch(); 40 | }); 41 | 42 | it('should set user', function() { 43 | expect(request.user).to.be.an('object'); 44 | expect(request.user.id).to.equal('1'); 45 | expect(request.user.username).to.equal('jaredhanson'); 46 | }); 47 | 48 | it('should add message to session', function() { 49 | expect(request.session.messages).to.have.length(1); 50 | expect(request.session.messages[0]).to.equal('Login complete'); 51 | }); 52 | 53 | it('should redirect', function() { 54 | expect(response.statusCode).to.equal(302); 55 | expect(response.getHeader('Location')).to.equal('http://www.example.com/account'); 56 | }); 57 | }); 58 | 59 | describe('success with message set by route that is added to messages', function() { 60 | function Strategy() { 61 | } 62 | Strategy.prototype.authenticate = function(req, options) { 63 | var user = { id: '1', username: 'jaredhanson' }; 64 | this.success(user, { message: 'Welcome!' }); 65 | }; 66 | 67 | var passport = new Passport(); 68 | passport.use('success', new Strategy()); 69 | 70 | var request, response; 71 | 72 | before(function(done) { 73 | chai.connect.use('express', authenticate(passport, 'success', { successMessage: 'Login complete', 74 | successRedirect: 'http://www.example.com/account' })) 75 | .req(function(req) { 76 | request = req; 77 | req.session = {}; 78 | req.session.messages = [ 'I exist!' ]; 79 | 80 | req.logIn = function(user, options, done) { 81 | this.user = user; 82 | done(); 83 | }; 84 | }) 85 | .end(function(res) { 86 | response = res; 87 | done(); 88 | }) 89 | .dispatch(); 90 | }); 91 | 92 | it('should set user', function() { 93 | expect(request.user).to.be.an('object'); 94 | expect(request.user.id).to.equal('1'); 95 | expect(request.user.username).to.equal('jaredhanson'); 96 | }); 97 | 98 | it('should add message to session', function() { 99 | expect(request.session.messages).to.have.length(2); 100 | expect(request.session.messages[0]).to.equal('I exist!'); 101 | expect(request.session.messages[1]).to.equal('Login complete'); 102 | }); 103 | 104 | it('should redirect', function() { 105 | expect(response.statusCode).to.equal(302); 106 | expect(response.getHeader('Location')).to.equal('http://www.example.com/account'); 107 | }); 108 | }); 109 | 110 | describe('success with message set by strategy', function() { 111 | function Strategy() { 112 | } 113 | Strategy.prototype.authenticate = function(req, options) { 114 | var user = { id: '1', username: 'jaredhanson' }; 115 | this.success(user, { message: 'Welcome!' }); 116 | }; 117 | 118 | var passport = new Passport(); 119 | passport.use('success', new Strategy()); 120 | 121 | var request, response; 122 | 123 | before(function(done) { 124 | chai.connect.use('express', authenticate(passport, 'success', { successMessage: true, 125 | successRedirect: 'http://www.example.com/account' })) 126 | .req(function(req) { 127 | request = req; 128 | req.session = {}; 129 | 130 | req.logIn = function(user, options, done) { 131 | this.user = user; 132 | done(); 133 | }; 134 | }) 135 | .end(function(res) { 136 | response = res; 137 | done(); 138 | }) 139 | .dispatch(); 140 | }); 141 | 142 | it('should set user', function() { 143 | expect(request.user).to.be.an('object'); 144 | expect(request.user.id).to.equal('1'); 145 | expect(request.user.username).to.equal('jaredhanson'); 146 | }); 147 | 148 | it('should add message to session', function() { 149 | expect(request.session.messages).to.have.length(1); 150 | expect(request.session.messages[0]).to.equal('Welcome!'); 151 | }); 152 | 153 | it('should redirect', function() { 154 | expect(response.statusCode).to.equal(302); 155 | expect(response.getHeader('Location')).to.equal('http://www.example.com/account'); 156 | }); 157 | }); 158 | 159 | describe('success with message set by strategy with extra info', function() { 160 | function Strategy() { 161 | } 162 | Strategy.prototype.authenticate = function(req, options) { 163 | var user = { id: '1', username: 'jaredhanson' }; 164 | this.success(user, { message: 'Welcome!', scope: 'read' }); 165 | }; 166 | 167 | var passport = new Passport(); 168 | passport.use('success', new Strategy()); 169 | 170 | var request, response; 171 | 172 | before(function(done) { 173 | chai.connect.use('express', authenticate(passport, 'success', { successMessage: true, 174 | successRedirect: 'http://www.example.com/account' })) 175 | .req(function(req) { 176 | request = req; 177 | req.session = {}; 178 | 179 | req.logIn = function(user, options, done) { 180 | this.user = user; 181 | done(); 182 | }; 183 | }) 184 | .end(function(res) { 185 | response = res; 186 | done(); 187 | }) 188 | .dispatch(); 189 | }); 190 | 191 | it('should set user', function() { 192 | expect(request.user).to.be.an('object'); 193 | expect(request.user.id).to.equal('1'); 194 | expect(request.user.username).to.equal('jaredhanson'); 195 | }); 196 | 197 | it('should add message to session', function() { 198 | expect(request.session.messages).to.have.length(1); 199 | expect(request.session.messages[0]).to.equal('Welcome!'); 200 | }); 201 | 202 | it('should redirect', function() { 203 | expect(response.statusCode).to.equal(302); 204 | expect(response.getHeader('Location')).to.equal('http://www.example.com/account'); 205 | }); 206 | }); 207 | 208 | }); 209 | -------------------------------------------------------------------------------- /test/middleware/authenticate.success.multi.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('with multiple strategies, the first of which succeeds', function() { 12 | function StrategyA() { 13 | } 14 | StrategyA.prototype.authenticate = function(req) { 15 | this.success({ username: 'bob-a' }); 16 | }; 17 | 18 | function StrategyB() { 19 | } 20 | StrategyB.prototype.authenticate = function(req) { 21 | this.success({ username: 'bob-b' }); 22 | }; 23 | 24 | var passport = new Passport(); 25 | passport.use('a', new StrategyA()); 26 | passport.use('b', new StrategyB()); 27 | 28 | var request, error; 29 | 30 | before(function(done) { 31 | chai.connect.use(authenticate(passport, ['a', 'b'])) 32 | .req(function(req) { 33 | request = req; 34 | 35 | req.logIn = function(user, options, done) { 36 | this.user = user; 37 | done(); 38 | }; 39 | }) 40 | .next(function(err) { 41 | error = err; 42 | done(); 43 | }) 44 | .dispatch(); 45 | }); 46 | 47 | it('should not error', function() { 48 | expect(error).to.be.undefined; 49 | }); 50 | 51 | it('should set user', function() { 52 | expect(request.user).to.be.an('object'); 53 | expect(request.user.username).to.equal('bob-a'); 54 | }); 55 | }); 56 | 57 | describe('with multiple strategies, the second of which succeeds', function() { 58 | function StrategyA() { 59 | } 60 | StrategyA.prototype.authenticate = function(req) { 61 | this.fail('A challenge'); 62 | }; 63 | 64 | function StrategyB() { 65 | } 66 | StrategyB.prototype.authenticate = function(req) { 67 | this.success({ username: 'bob-b' }); 68 | }; 69 | 70 | var passport = new Passport(); 71 | passport.use('a', new StrategyA()); 72 | passport.use('b', new StrategyB()); 73 | 74 | var request, error; 75 | 76 | before(function(done) { 77 | chai.connect.use(authenticate(passport, ['a', 'b'])) 78 | .req(function(req) { 79 | request = req; 80 | 81 | req.logIn = function(user, options, done) { 82 | this.user = user; 83 | done(); 84 | }; 85 | }) 86 | .next(function(err) { 87 | error = err; 88 | done(); 89 | }) 90 | .dispatch(); 91 | }); 92 | 93 | it('should not error', function() { 94 | expect(error).to.be.undefined; 95 | }); 96 | 97 | it('should set user', function() { 98 | expect(request.user).to.be.an('object'); 99 | expect(request.user.username).to.equal('bob-b'); 100 | }); 101 | }); 102 | 103 | }); 104 | -------------------------------------------------------------------------------- /test/middleware/authenticate.success.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | describe('success', function() { 12 | function Strategy() { 13 | } 14 | Strategy.prototype.authenticate = function(req) { 15 | var user = { id: '1', username: 'jaredhanson' }; 16 | this.success(user); 17 | }; 18 | 19 | var passport = new Passport(); 20 | passport.use('success', new Strategy()); 21 | 22 | var request, error; 23 | 24 | before(function(done) { 25 | chai.connect.use(authenticate(passport, 'success')) 26 | .req(function(req) { 27 | request = req; 28 | 29 | req.logIn = function(user, options, done) { 30 | this.user = user; 31 | done(); 32 | }; 33 | }) 34 | .next(function(err) { 35 | error = err; 36 | done(); 37 | }) 38 | .dispatch(); 39 | }); 40 | 41 | it('should not error', function() { 42 | expect(error).to.be.undefined; 43 | }); 44 | 45 | it('should set user', function() { 46 | expect(request.user).to.be.an('object'); 47 | expect(request.user.id).to.equal('1'); 48 | expect(request.user.username).to.equal('jaredhanson'); 49 | }); 50 | 51 | it('should set authInfo', function() { 52 | expect(request.authInfo).to.be.an('object'); 53 | expect(Object.keys(request.authInfo)).to.have.length(0); 54 | }); 55 | }); 56 | 57 | describe('success that assigns a specific property', function() { 58 | function Strategy() { 59 | } 60 | Strategy.prototype.authenticate = function(req) { 61 | var user = { id: '1', username: 'jaredhanson' }; 62 | this.success(user); 63 | }; 64 | 65 | var passport = new Passport(); 66 | passport.use('success', new Strategy()); 67 | 68 | var request, error; 69 | 70 | before(function(done) { 71 | chai.connect.use(authenticate(passport, 'success', { assignProperty: 'account' })) 72 | .req(function(req) { 73 | request = req; 74 | 75 | req.logIn = function(user, options, done) { 76 | this.user = user; 77 | done(); 78 | }; 79 | }) 80 | .next(function(err) { 81 | error = err; 82 | done(); 83 | }) 84 | .dispatch(); 85 | }); 86 | 87 | it('should not error', function() { 88 | expect(error).to.be.undefined; 89 | }); 90 | 91 | it('should not set user', function() { 92 | expect(request.user).to.be.undefined; 93 | }); 94 | 95 | it('should set account', function() { 96 | expect(request.account).to.be.an('object'); 97 | expect(request.account.id).to.equal('1'); 98 | expect(request.account.username).to.equal('jaredhanson'); 99 | }); 100 | 101 | it('should set authInfo to empty object', function() { 102 | expect(request.authInfo).to.deep.equal({}); 103 | }); 104 | }); 105 | 106 | describe('success that assigns a specific property with authInfo disabled', function() { 107 | function Strategy() { 108 | } 109 | Strategy.prototype.authenticate = function(req) { 110 | var user = { id: '1', username: 'jaredhanson' }; 111 | this.success(user); 112 | }; 113 | 114 | var passport = new Passport(); 115 | passport.use('success', new Strategy()); 116 | 117 | var request, error; 118 | 119 | before(function(done) { 120 | chai.connect.use(authenticate(passport, 'success', { assignProperty: 'account', authInfo: false })) 121 | .req(function(req) { 122 | request = req; 123 | 124 | req.logIn = function(user, options, done) { 125 | this.user = user; 126 | done(); 127 | }; 128 | }) 129 | .next(function(err) { 130 | error = err; 131 | done(); 132 | }) 133 | .dispatch(); 134 | }); 135 | 136 | it('should not error', function() { 137 | expect(error).to.be.undefined; 138 | }); 139 | 140 | it('should not set user', function() { 141 | expect(request.user).to.be.undefined; 142 | }); 143 | 144 | it('should set account', function() { 145 | expect(request.account).to.be.an('object'); 146 | expect(request.account.id).to.equal('1'); 147 | expect(request.account.username).to.equal('jaredhanson'); 148 | }); 149 | 150 | it('should not set authInfo', function() { 151 | expect(request.authInfo).to.be.undefined; 152 | }); 153 | }); 154 | 155 | describe('success with strategy-specific options', function() { 156 | function Strategy() { 157 | } 158 | Strategy.prototype.authenticate = function(req, options) { 159 | var user = { id: '1', username: 'jaredhanson' }; 160 | if (options.scope == 'email') { 161 | user.email = 'jaredhanson@example.com'; 162 | } 163 | this.success(user); 164 | }; 165 | 166 | var passport = new Passport(); 167 | passport.use('success', new Strategy()); 168 | 169 | var request, error; 170 | 171 | before(function(done) { 172 | chai.connect.use(authenticate(passport, 'success', { scope: 'email' })) 173 | .req(function(req) { 174 | request = req; 175 | 176 | req.logIn = function(user, options, done) { 177 | if (options.scope != 'email') { return done(new Error('invalid options')); } 178 | this.user = user; 179 | done(); 180 | }; 181 | }) 182 | .next(function(err) { 183 | error = err; 184 | done(); 185 | }) 186 | .dispatch(); 187 | }); 188 | 189 | it('should not error', function() { 190 | expect(error).to.be.undefined; 191 | }); 192 | 193 | it('should set user', function() { 194 | expect(request.user).to.be.an('object'); 195 | expect(request.user.id).to.equal('1'); 196 | expect(request.user.username).to.equal('jaredhanson'); 197 | expect(request.user.email).to.equal('jaredhanson@example.com'); 198 | }); 199 | 200 | it('should set authInfo', function() { 201 | expect(request.authInfo).to.be.an('object'); 202 | expect(Object.keys(request.authInfo)).to.have.length(0); 203 | }); 204 | }); 205 | 206 | describe('success with redirect', function() { 207 | function Strategy() { 208 | } 209 | Strategy.prototype.authenticate = function(req, options) { 210 | var user = { id: '1', username: 'jaredhanson' }; 211 | this.success(user); 212 | }; 213 | 214 | var passport = new Passport(); 215 | passport.use('success', new Strategy()); 216 | 217 | var request, response; 218 | 219 | before(function(done) { 220 | chai.connect.use('express', authenticate(passport, 'success', { successRedirect: 'http://www.example.com/account' })) 221 | .req(function(req) { 222 | request = req; 223 | 224 | req.logIn = function(user, options, done) { 225 | this.user = user; 226 | done(); 227 | }; 228 | }) 229 | .end(function(res) { 230 | response = res; 231 | done(); 232 | }) 233 | .dispatch(); 234 | }); 235 | 236 | it('should set user', function() { 237 | expect(request.user).to.be.an('object'); 238 | expect(request.user.id).to.equal('1'); 239 | expect(request.user.username).to.equal('jaredhanson'); 240 | }); 241 | 242 | it('should set authInfo', function() { 243 | expect(request.authInfo).to.be.an('object'); 244 | expect(Object.keys(request.authInfo)).to.have.length(0); 245 | }); 246 | 247 | it('should redirect', function() { 248 | expect(response.statusCode).to.equal(302); 249 | expect(response.getHeader('Location')).to.equal('http://www.example.com/account'); 250 | }); 251 | }); 252 | 253 | describe('success with return to previous location', function() { 254 | function Strategy() { 255 | } 256 | Strategy.prototype.authenticate = function(req, options) { 257 | var user = { id: '1', username: 'jaredhanson' }; 258 | this.success(user); 259 | }; 260 | 261 | var passport = new Passport(); 262 | passport.use('success', new Strategy()); 263 | 264 | var request, response; 265 | 266 | before(function(done) { 267 | chai.connect.use('express', authenticate(passport, 'success', { successReturnToOrRedirect: 'http://www.example.com/default' })) 268 | .req(function(req) { 269 | request = req; 270 | req.session = { returnTo: 'http://www.example.com/return' }; 271 | 272 | req.logIn = function(user, options, done) { 273 | this.user = user; 274 | done(); 275 | }; 276 | }) 277 | .end(function(res) { 278 | response = res; 279 | done(); 280 | }) 281 | .dispatch(); 282 | }); 283 | 284 | it('should set user', function() { 285 | expect(request.user).to.be.an('object'); 286 | expect(request.user.id).to.equal('1'); 287 | expect(request.user.username).to.equal('jaredhanson'); 288 | }); 289 | 290 | it('should set authInfo', function() { 291 | expect(request.authInfo).to.be.an('object'); 292 | expect(Object.keys(request.authInfo)).to.have.length(0); 293 | }); 294 | 295 | it('should redirect', function() { 296 | expect(response.statusCode).to.equal(302); 297 | expect(response.getHeader('Location')).to.equal('http://www.example.com/return'); 298 | }); 299 | 300 | it('should move return to from session', function() { 301 | expect(request.session.returnTo).to.be.undefined; 302 | }); 303 | }); 304 | 305 | describe('success with return to default location', function() { 306 | function Strategy() { 307 | } 308 | Strategy.prototype.authenticate = function(req, options) { 309 | var user = { id: '1', username: 'jaredhanson' }; 310 | this.success(user); 311 | }; 312 | 313 | var passport = new Passport(); 314 | passport.use('success', new Strategy()); 315 | 316 | var request, response; 317 | 318 | before(function(done) { 319 | chai.connect.use('express', authenticate(passport, 'success', { successReturnToOrRedirect: 'http://www.example.com/default' })) 320 | .req(function(req) { 321 | request = req; 322 | 323 | req.logIn = function(user, options, done) { 324 | this.user = user; 325 | done(); 326 | }; 327 | }) 328 | .end(function(res) { 329 | response = res; 330 | done(); 331 | }) 332 | .dispatch(); 333 | }); 334 | 335 | it('should set user', function() { 336 | expect(request.user).to.be.an('object'); 337 | expect(request.user.id).to.equal('1'); 338 | expect(request.user.username).to.equal('jaredhanson'); 339 | }); 340 | 341 | it('should set authInfo', function() { 342 | expect(request.authInfo).to.be.an('object'); 343 | expect(Object.keys(request.authInfo)).to.have.length(0); 344 | }); 345 | 346 | it('should redirect', function() { 347 | expect(response.statusCode).to.equal(302); 348 | expect(response.getHeader('Location')).to.equal('http://www.example.com/default'); 349 | }); 350 | }); 351 | 352 | describe('success, but login that encounters an error', function() { 353 | function Strategy() { 354 | } 355 | Strategy.prototype.authenticate = function(req) { 356 | var user = { id: '1', username: 'jaredhanson' }; 357 | this.success(user); 358 | }; 359 | 360 | var passport = new Passport(); 361 | passport.use('success', new Strategy()); 362 | 363 | var request, error; 364 | 365 | before(function(done) { 366 | chai.connect.use(authenticate(passport, 'success')) 367 | .req(function(req) { 368 | request = req; 369 | 370 | req.logIn = function(user, options, done) { 371 | done(new Error('something went wrong')); 372 | }; 373 | }) 374 | .next(function(err) { 375 | error = err; 376 | done(); 377 | }) 378 | .dispatch(); 379 | }); 380 | 381 | it('should error', function() { 382 | expect(error).to.be.an.instanceOf(Error); 383 | expect(error.message).to.equal('something went wrong'); 384 | }); 385 | 386 | it('should not set user', function() { 387 | expect(request.user).to.be.undefined; 388 | }); 389 | 390 | it('should not set authInfo', function() { 391 | expect(request.authInfo).to.be.undefined; 392 | }); 393 | }); 394 | 395 | }); 396 | -------------------------------------------------------------------------------- /test/middleware/authenticate.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , authenticate = require('../../lib/middleware/authenticate') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/authenticate', function() { 10 | 11 | it('should be named authenticate', function() { 12 | expect(authenticate().name).to.equal('authenticate'); 13 | }); 14 | 15 | describe('with unknown strategy', function() { 16 | var passport = new Passport(); 17 | 18 | var request, error; 19 | 20 | before(function(done) { 21 | chai.connect.use(authenticate(passport, 'foo')) 22 | .req(function(req) { 23 | request = req; 24 | 25 | req.logIn = function(user, options, done) { 26 | this.user = user; 27 | done(); 28 | }; 29 | }) 30 | .next(function(err) { 31 | error = err; 32 | done(); 33 | }) 34 | .dispatch(); 35 | }); 36 | 37 | it('should error', function() { 38 | expect(error).to.be.an.instanceOf(Error); 39 | expect(error.message).to.equal('Unknown authentication strategy "foo"'); 40 | }); 41 | 42 | it('should not set user', function() { 43 | expect(request.user).to.be.undefined; 44 | }); 45 | 46 | it('should not set authInfo', function() { 47 | expect(request.authInfo).to.be.undefined; 48 | }); 49 | }); 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /test/middleware/initialize.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , initialize = require('../../lib/middleware/initialize') 6 | , Passport = require('../..').Passport; 7 | 8 | 9 | describe('middleware/initialize', function() { 10 | 11 | it('should be named initialize', function() { 12 | expect(initialize().name).to.equal('initialize'); 13 | }); 14 | 15 | describe('handling a request without a session', function() { 16 | var passport = new Passport(); 17 | var request, error; 18 | 19 | before(function(done) { 20 | chai.connect.use(initialize(passport)) 21 | .req(function(req) { 22 | request = req; 23 | }) 24 | .next(function(err) { 25 | error = err; 26 | done(); 27 | }) 28 | .dispatch(); 29 | }); 30 | 31 | it('should not error', function() { 32 | expect(error).to.be.undefined; 33 | }); 34 | 35 | it('should expose authenticator on internal request property', function() { 36 | expect(request._passport).to.be.an('object'); 37 | expect(request._passport.instance).to.be.an.instanceOf(Passport); 38 | expect(request._passport.instance).to.equal(passport); 39 | expect(request._passport.instance._sm).to.be.an('object'); 40 | expect(request._passport.instance._userProperty).to.equal('user'); 41 | }); 42 | }); 43 | 44 | describe('handling a request with a new session', function() { 45 | var passport = new Passport(); 46 | var request, error; 47 | 48 | before(function(done) { 49 | chai.connect.use(initialize(passport)) 50 | .req(function(req) { 51 | request = req; 52 | 53 | req.session = {}; 54 | }) 55 | .next(function(err) { 56 | error = err; 57 | done(); 58 | }) 59 | .dispatch(); 60 | }); 61 | 62 | it('should not error', function() { 63 | expect(error).to.be.undefined; 64 | }); 65 | 66 | it('should not initialize namespace within session', function() { 67 | expect(request.session.passport).to.be.undefined; 68 | }); 69 | 70 | it('should expose authenticator on internal request property', function() { 71 | expect(request._passport).to.be.an('object'); 72 | expect(request._passport.instance).to.be.an.instanceOf(Passport); 73 | expect(request._passport.instance).to.equal(passport); 74 | expect(request._passport.instance._sm).to.be.an('object'); 75 | expect(request._passport.instance._userProperty).to.equal('user'); 76 | }); 77 | }); 78 | 79 | describe('handling a request with an existing session', function() { 80 | var passport = new Passport(); 81 | var request, error; 82 | 83 | before(function(done) { 84 | chai.connect.use(initialize(passport)) 85 | .req(function(req) { 86 | request = req; 87 | 88 | req.session = {}; 89 | req.session.passport = {}; 90 | req.session.passport.user = '123456'; 91 | }) 92 | .next(function(err) { 93 | error = err; 94 | done(); 95 | }) 96 | .dispatch(); 97 | }); 98 | 99 | it('should not error', function() { 100 | expect(error).to.be.undefined; 101 | }); 102 | 103 | it('should maintain data within session', function() { 104 | expect(request.session.passport).to.be.an('object'); 105 | expect(Object.keys(request.session.passport)).to.have.length(1); 106 | expect(request.session.passport.user).to.equal('123456'); 107 | }); 108 | 109 | it('should expose authenticator on internal request property', function() { 110 | expect(request._passport).to.be.an('object'); 111 | expect(request._passport.instance).to.be.an.instanceOf(Passport); 112 | expect(request._passport.instance).to.equal(passport); 113 | expect(request._passport.instance._sm).to.be.an('object'); 114 | expect(request._passport.instance._userProperty).to.equal('user'); 115 | }); 116 | }); 117 | 118 | describe('handling a request with an existing session using custom session key', function() { 119 | var passport = new Passport(); 120 | passport._key = 'authentication'; 121 | var request, error; 122 | 123 | before(function(done) { 124 | chai.connect.use(initialize(passport)) 125 | .req(function(req) { 126 | request = req; 127 | 128 | req.session = {}; 129 | req.session.authentication = {}; 130 | req.session.authentication.user = '123456'; 131 | }) 132 | .next(function(err) { 133 | error = err; 134 | done(); 135 | }) 136 | .dispatch(); 137 | }); 138 | 139 | it('should not error', function() { 140 | expect(error).to.be.undefined; 141 | }); 142 | 143 | it('should maintain data within session', function() { 144 | expect(request.session.authentication).to.be.an('object'); 145 | expect(Object.keys(request.session.authentication)).to.have.length(1); 146 | expect(request.session.authentication.user).to.equal('123456'); 147 | }); 148 | 149 | it('should expose authenticator on internal request property', function() { 150 | expect(request._passport).to.be.an('object'); 151 | expect(request._passport.instance).to.be.an.instanceOf(Passport); 152 | expect(request._passport.instance).to.equal(passport); 153 | expect(request._passport.instance._sm).to.be.an('object'); 154 | expect(request._passport.instance._userProperty).to.equal('user'); 155 | }); 156 | }); 157 | 158 | describe('handling a request with a new session without compat mode', function() { 159 | var passport = new Passport(); 160 | var request, error; 161 | 162 | before(function(done) { 163 | chai.connect.use(initialize(passport, { compat: false })) 164 | .req(function(req) { 165 | request = req; 166 | 167 | req.session = {}; 168 | }) 169 | .next(function(err) { 170 | error = err; 171 | done(); 172 | }) 173 | .dispatch(); 174 | }); 175 | 176 | it('should not error', function() { 177 | expect(error).to.be.undefined; 178 | }); 179 | 180 | it('should not initialize namespace within session', function() { 181 | expect(request.session.passport).to.be.undefined; 182 | }); 183 | 184 | it('should expose authenticator on internal request property', function() { 185 | expect(request._passport).to.be.undefined; 186 | }); 187 | }); 188 | 189 | }); 190 | -------------------------------------------------------------------------------- /test/package.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect */ 2 | 3 | var passport = require('..'); 4 | 5 | describe('passport', function() { 6 | 7 | it('should expose singleton authenticator', function() { 8 | expect(passport).to.be.an('object'); 9 | expect(passport).to.be.an.instanceOf(passport.Authenticator); 10 | }); 11 | 12 | it('should export constructors', function() { 13 | expect(passport.Authenticator).to.equal(passport.Passport); 14 | expect(passport.Authenticator).to.be.a('function'); 15 | expect(passport.Strategy).to.be.a('function'); 16 | }); 17 | 18 | it('should export strategies', function() { 19 | expect(passport.strategies.SessionStrategy).to.be.a('function'); 20 | }); 21 | 22 | }); 23 | -------------------------------------------------------------------------------- /test/strategies/session.pause.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai'); 5 | 6 | 7 | describe('SessionStrategy', function() { 8 | 9 | describe('handling a request with a login session, pausing for deserialization', function() { 10 | var spy = []; 11 | 12 | var pause = function() { 13 | spy.push({ context: this, args: [].slice.call(arguments) }); 14 | return { resume: function() { spy.push({ context: this, args: [].slice.call(arguments) }); } }; 15 | } 16 | 17 | 18 | var SessionStrategy = $require('../../lib/strategies/session', { pause: pause }); 19 | var strategy = new SessionStrategy(function(user, req, done) { 20 | done(null, { id: user }); 21 | }); 22 | 23 | var request, pass = false; 24 | 25 | before(function(done) { 26 | chai.passport.use(strategy) 27 | .pass(function() { 28 | pass = true; 29 | done(); 30 | }) 31 | .req(function(req) { 32 | request = req; 33 | 34 | req._passport = {}; 35 | req._passport.instance = {}; 36 | req.session = {}; 37 | req.session['passport'] = {}; 38 | req.session['passport'].user = '123456'; 39 | }) 40 | .authenticate({ pauseStream: true }); 41 | }); 42 | 43 | it('should spy correctly', function() { 44 | expect(spy).to.have.length(2); 45 | }); 46 | 47 | it('should pass', function() { 48 | expect(pass).to.be.true; 49 | }); 50 | 51 | it('should set user on request', function() { 52 | expect(request.user).to.be.an('object'); 53 | expect(request.user.id).to.equal('123456'); 54 | }); 55 | 56 | it('should maintain session', function() { 57 | expect(request.session['passport']).to.be.an('object'); 58 | expect(request.session['passport'].user).to.equal('123456'); 59 | }); 60 | 61 | it('should pause request', function() { 62 | var s0 = spy[0]; 63 | expect(s0.args[0]).to.equal(request); 64 | }); 65 | 66 | it('should resume request', function() { 67 | var s1 = spy[1]; 68 | expect(s1.args[0]).to.equal(undefined); 69 | }); 70 | }); 71 | 72 | describe('handling a request with a login session that has been invalidated, pausing for deserialization', function() { 73 | var spy = []; 74 | 75 | var pause = function() { 76 | spy.push({ context: this, args: [].slice.call(arguments) }); 77 | return { resume: function() { spy.push({ context: this, args: [].slice.call(arguments) }); } }; 78 | } 79 | 80 | 81 | var SessionStrategy = $require('../../lib/strategies/session', { pause: pause }); 82 | var strategy = new SessionStrategy(function(user, req, done) { 83 | done(null, false); 84 | }); 85 | 86 | var request, pass = false; 87 | 88 | before(function(done) { 89 | chai.passport.use(strategy) 90 | .pass(function() { 91 | pass = true; 92 | done(); 93 | }) 94 | .req(function(req) { 95 | request = req; 96 | 97 | req._passport = {}; 98 | req._passport.instance = {}; 99 | req.session = {}; 100 | req.session['passport'] = {}; 101 | req.session['passport'].user = '123456'; 102 | }) 103 | .authenticate({ pauseStream: true }); 104 | }); 105 | 106 | it('should spy correctly', function() { 107 | expect(spy).to.have.length(2); 108 | }); 109 | 110 | it('should pass', function() { 111 | expect(pass).to.be.true; 112 | }); 113 | 114 | it('should not set user on request', function() { 115 | expect(request.user).to.be.undefined; 116 | }); 117 | 118 | it('should remove user from session', function() { 119 | expect(request.session['passport']).to.be.an('object'); 120 | expect(request.session['passport'].user).to.be.undefined; 121 | }); 122 | 123 | it('should pause request', function() { 124 | var s0 = spy[0]; 125 | expect(s0.args[0]).to.equal(request); 126 | }); 127 | 128 | it('should resume request', function() { 129 | var s1 = spy[1]; 130 | expect(s1.args[0]).to.equal(undefined); 131 | }); 132 | }); 133 | 134 | }); 135 | -------------------------------------------------------------------------------- /test/strategies/session.test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it, expect, before */ 2 | /* jshint expr: true */ 3 | 4 | var chai = require('chai') 5 | , SessionStrategy = require('../../lib/strategies/session'); 6 | 7 | 8 | describe('SessionStrategy', function() { 9 | 10 | var strategy = new SessionStrategy(); 11 | 12 | it('should be named session', function() { 13 | expect(strategy.name).to.equal('session'); 14 | }); 15 | 16 | describe('handling a request without a login session', function() { 17 | var request, pass = false; 18 | 19 | before(function(done) { 20 | chai.passport.use(strategy) 21 | .pass(function() { 22 | pass = true; 23 | done(); 24 | }) 25 | .req(function(req) { 26 | request = req; 27 | 28 | req._passport = {}; 29 | req.session = {}; 30 | req.session['passport'] = {}; 31 | }) 32 | .authenticate(); 33 | }); 34 | 35 | it('should pass', function() { 36 | expect(pass).to.be.true; 37 | }); 38 | 39 | it('should not set user on request', function() { 40 | expect(request.user).to.be.undefined; 41 | }); 42 | }); 43 | 44 | describe('handling a request with a login session', function() { 45 | var strategy = new SessionStrategy(function(user, req, done) { 46 | done(null, { id: user }); 47 | }); 48 | 49 | var request, pass = false; 50 | 51 | before(function(done) { 52 | chai.passport.use(strategy) 53 | .pass(function() { 54 | pass = true; 55 | done(); 56 | }) 57 | .req(function(req) { 58 | request = req; 59 | 60 | req._passport = {}; 61 | req._passport.instance = {}; 62 | req.session = {}; 63 | req.session['passport'] = {}; 64 | req.session['passport'].user = '123456'; 65 | }) 66 | .authenticate(); 67 | }); 68 | 69 | it('should pass', function() { 70 | expect(pass).to.be.true; 71 | }); 72 | 73 | it('should set user on request', function() { 74 | expect(request.user).to.be.an('object'); 75 | expect(request.user.id).to.equal('123456'); 76 | }); 77 | 78 | it('should maintain session', function() { 79 | expect(request.session['passport']).to.be.an('object'); 80 | expect(request.session['passport'].user).to.equal('123456'); 81 | }); 82 | }); 83 | 84 | describe('handling a request with a login session serialized to 0', function() { 85 | var strategy = new SessionStrategy(function(user, req, done) { 86 | done(null, { id: user }); 87 | }); 88 | 89 | var request, pass = false; 90 | 91 | before(function(done) { 92 | chai.passport.use(strategy) 93 | .pass(function() { 94 | pass = true; 95 | done(); 96 | }) 97 | .req(function(req) { 98 | request = req; 99 | 100 | req._passport = {}; 101 | req._passport.instance = {}; 102 | req.session = {}; 103 | req.session['passport'] = {}; 104 | req.session['passport'].user = 0; 105 | }) 106 | .authenticate(); 107 | }); 108 | 109 | it('should pass', function() { 110 | expect(pass).to.be.true; 111 | }); 112 | 113 | it('should set user on request', function() { 114 | expect(request.user).to.be.an('object'); 115 | expect(request.user.id).to.equal(0); 116 | }); 117 | 118 | it('should maintain session', function() { 119 | expect(request.session['passport']).to.be.an('object'); 120 | expect(request.session['passport'].user).to.equal(0); 121 | }); 122 | }); 123 | 124 | describe('handling a request with a login session that has been invalidated', function() { 125 | var strategy = new SessionStrategy(function(user, req, done) { 126 | done(null, false); 127 | }); 128 | 129 | var request, pass = false; 130 | 131 | before(function(done) { 132 | chai.passport.use(strategy) 133 | .pass(function() { 134 | pass = true; 135 | done(); 136 | }) 137 | .req(function(req) { 138 | request = req; 139 | 140 | req._passport = {}; 141 | req._passport.instance = {}; 142 | req.session = {}; 143 | req.session['passport'] = {}; 144 | req.session['passport'].user = '123456'; 145 | }) 146 | .authenticate(); 147 | }); 148 | 149 | it('should pass', function() { 150 | expect(pass).to.be.true; 151 | }); 152 | 153 | it('should not set user on request', function() { 154 | expect(request.user).to.be.undefined; 155 | }); 156 | 157 | it('should remove user from session', function() { 158 | expect(request.session['passport']).to.be.an('object'); 159 | expect(request.session['passport'].user).to.be.undefined; 160 | }); 161 | }); 162 | 163 | describe('handling a request with a login session and setting custom user property', function() { 164 | var strategy = new SessionStrategy(function(user, req, done) { 165 | done(null, { id: user }); 166 | }); 167 | 168 | var request, pass = false; 169 | 170 | before(function(done) { 171 | chai.passport.use(strategy) 172 | .pass(function() { 173 | pass = true; 174 | done(); 175 | }) 176 | .req(function(req) { 177 | request = req; 178 | 179 | req._passport = {}; 180 | req._passport.instance = {}; 181 | req._userProperty = 'currentUser'; 182 | req.session = {}; 183 | req.session['passport'] = {}; 184 | req.session['passport'].user = '123456'; 185 | }) 186 | .authenticate(); 187 | }); 188 | 189 | it('should pass', function() { 190 | expect(pass).to.be.true; 191 | }); 192 | 193 | it('should not set "user" on request', function() { 194 | expect(request.user).to.be.undefined; 195 | }); 196 | 197 | it('should set "currentUser" on request', function() { 198 | expect(request.currentUser).to.be.an('object'); 199 | expect(request.currentUser.id).to.equal('123456'); 200 | }); 201 | }); 202 | 203 | describe('handling a request with a login session that encounters an error when deserializing', function() { 204 | var strategy = new SessionStrategy(function(user, req, done) { 205 | done(new Error('something went wrong')); 206 | }); 207 | 208 | var request, error; 209 | 210 | before(function(done) { 211 | chai.passport.use(strategy) 212 | .error(function(err) { 213 | error = err; 214 | done(); 215 | }) 216 | .req(function(req) { 217 | request = req; 218 | 219 | req._passport = {}; 220 | req._passport.instance = {}; 221 | req.session = {}; 222 | req.session['passport'] = {}; 223 | req.session['passport'].user = '123456'; 224 | }) 225 | .authenticate(); 226 | }); 227 | 228 | it('should error', function() { 229 | expect(error).to.be.an.instanceOf(Error); 230 | expect(error.message).to.equal('something went wrong'); 231 | }); 232 | 233 | it('should not set user on request', function() { 234 | expect(request.user).to.be.undefined; 235 | }); 236 | 237 | it('should maintain session', function() { 238 | expect(request.session['passport']).to.be.an('object'); 239 | expect(request.session['passport'].user).to.equal('123456'); 240 | }); 241 | }); 242 | 243 | describe('handling a request that lacks an authenticator', function() { 244 | var request, error; 245 | 246 | before(function(done) { 247 | chai.passport.use(strategy) 248 | .error(function(err) { 249 | error = err; 250 | done(); 251 | }) 252 | .req(function(req) { 253 | request = req; 254 | }) 255 | .authenticate(); 256 | }); 257 | 258 | it('should error', function() { 259 | expect(error).to.be.an.instanceOf(Error); 260 | expect(error.message).to.equal('Login sessions require session support. Did you forget to use `express-session` middleware?'); 261 | }); 262 | 263 | it('should not set user on request', function() { 264 | expect(request.user).to.be.undefined; 265 | }); 266 | }); 267 | 268 | }); 269 | --------------------------------------------------------------------------------