├── .gitignore ├── .gitmodules ├── .jshintrc ├── .travis.yml ├── CHANGELOG.md ├── Gruntfile.js ├── README.md ├── docs ├── Makefile ├── about.rst ├── changelog.rst ├── conf.py ├── help.rst ├── index.rst ├── quickstart.rst ├── setup.rst └── upgrading.rst ├── lib ├── index.js └── strategy.js ├── package.json ├── requirements.txt └── test ├── index.js └── mocks ├── req.js ├── sp-app.js ├── sp-client.js └── sp.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /coverage 3 | npm-debug.log 4 | *.ipr 5 | *.iws 6 | *.iml 7 | .idea 8 | .DS_Store 9 | docs/_build 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs/_themes/stormpath"] 2 | path = docs/_themes/stormpath 3 | url = https://github.com/stormpath/stormpath-sphinx-theme.git 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "unused": true, 11 | "boss": true, 12 | "eqnull": true, 13 | "node": true, 14 | "globals": { 15 | /* MOCHA */ 16 | "describe": false, 17 | "it": false, 18 | "before": false, 19 | "beforeEach": false, 20 | "after": false, 21 | "afterEach": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # passport-stormpath Changelog 3 | 4 | ### 0.2.2 5 | 6 | - Fix issue where `expansions` option was not used by `deserializeUser`, thanks 7 | @doublerebel (#11). 8 | 9 | ### 0.2.1 10 | 11 | - Upgrade the Stormpath Node SDK Dependency to 0.6.0 12 | 13 | ### 0.2.0 14 | 15 | - After authentication the value of `req.user` will now be an `Account` object 16 | from the Stormpath Node SDK. Previously it was an object literal. 17 | - Added an `expansions` option to the strategy constructor, allowing you to 18 | expand resources on the account during login. See below for example usage. 19 | 20 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.initConfig({ 4 | // Configure a mochaTest task 5 | mochaTest: { 6 | test: { 7 | options: { 8 | reporter: 'spec' 9 | }, 10 | src: ['test/**/*.js'] 11 | } 12 | }, 13 | jshint: { 14 | options: { 15 | jshintrc: '.jshintrc', 16 | reporter: require('jshint-stylish') 17 | }, 18 | gruntfile: { 19 | src: 'Gruntfile.js' 20 | }, 21 | lib: { 22 | src: ['lib/**/*.js'] 23 | }, 24 | test: { 25 | src: ['test/**/*.js'] 26 | } 27 | }, 28 | watch: { 29 | gruntfile: { 30 | files: '<%= jshint.gruntfile.src %>', 31 | tasks: ['jshint:gruntfile'] 32 | }, 33 | lib: { 34 | files: '<%= jshint.lib.src %>', 35 | tasks: ['jshint:lib', 'mochaTest'] 36 | }, 37 | test: { 38 | files: '<%= jshint.test.src %>', 39 | tasks: ['jshint:test', 'mochaTest'] 40 | } 41 | } 42 | }); 43 | 44 | grunt.loadNpmTasks('grunt-mocha-test'); 45 | grunt.loadNpmTasks('grunt-contrib-jshint'); 46 | grunt.loadNpmTasks('grunt-contrib-watch'); 47 | 48 | grunt.registerTask('default', ['jshint','mochaTest']); 49 | 50 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Stormpath is Joining Okta 2 | We are incredibly excited to announce that [Stormpath is joining forces with Okta](https://stormpath.com/blog/stormpaths-new-path?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement). Please visit [the Migration FAQs](https://stormpath.com/oktaplusstormpath?utm_source=github&utm_medium=readme&utm-campaign=okta-announcement) for a detailed look at what this means for Stormpath users. 3 | 4 | We're available to answer all questions at [support@stormpath.com](mailto:support@stormpath.com). 5 | 6 | # passport-stormpath 7 | 8 | [![NPM Version](https://img.shields.io/npm/v/passport-stormpath.svg?style=flat)](https://npmjs.org/package/passport-stormpath) 9 | [![NPM Downloads](http://img.shields.io/npm/dm/passport-stormpath.svg?style=flat)](https://npmjs.org/package/passport-stormpath) 10 | [![Build Status](https://img.shields.io/travis/stormpath/passport-stormpath.svg?style=flat)](https://travis-ci.org/stormpath/passport-stormpath) 11 | 12 | *A passport strategy for Stormpath, the simple user management API.* 13 | 14 | [Stormpath](https://stormpath.com/) extends Passport.js, adding a full set of user features: 15 | 16 | - Create, register and authenticate users. 17 | - Store custom user data with each account. 18 | - Create and assign permissions (groups, roles, etc.). 19 | - Handle complex authentication and authorization patterns, like multi-tenancy. 20 | - Log users in via social login with Facebook and Google OAuth. 21 | - Cache user information for quick access. 22 | - Secure all your passwords. 23 | - Automate all your password reset and account verification workflows. 24 | 25 | **NOTE**: If you're building an Express.js web application, you might want to 26 | use our [express-stormpath](https://docs.stormpath.com/nodejs/express/index.html) 27 | library instead -- it provides a simpler integration experience. 28 | 29 | 30 | ## Documentation 31 | 32 | All of this library's documentation can be found here: 33 | https://docs.stormpath.com/nodejs/passport/ 34 | 35 | If you'd like to hop right in and get started immediately, please take a look at 36 | the [Quickstart Guide](https://docs.stormpath.com/nodejs/passport/quickstart.html). 37 | (*It's ridiculously easy to get started with.*) 38 | 39 | 40 | ## Links 41 | 42 | Below are some resources you might find useful! 43 | 44 | - [passport-stormpath documentation](https://docs.stormpath.com/nodejs/passport/) 45 | - [15-Minute Tutorial: Build a Webapp With Node.js, Express, Passport and Stormpath](https://stormpath.com/blog/build-app-nodejs-express-passport-stormpath/) 46 | - [stormpath-passport-express Sample App repo](https://github.com/stormpath/stormpath-passport-express-sample) 47 | - [Stormpath Node.js library](https://github.com/stormpath/stormpath-sdk-node) 48 | - [Stormpath website](https://stormpath.com/) 49 | 50 | 51 | ## Installation 52 | 53 | To get started, you need to install this package via 54 | [npm](https://www.npmjs.org/package/passport-stormpath): 55 | 56 | ```console 57 | $ npm install passport-stormpath 58 | ``` 59 | 60 | 61 | ## Usage 62 | 63 | To use this module, you first need to export some environment variables -- these 64 | will be used by the `passport-stormpath` library to connect to the Stormpath API 65 | service: 66 | 67 | ```console 68 | $ export STORMPATH_API_KEY_ID=xxx 69 | $ export STORMPATH_API_KEY_SECRET=xxx 70 | $ export STORMPATH_APP_HREF=xxx 71 | ``` 72 | 73 | **NOTE**: These variables can be found in your 74 | [Stormpath Admin Console](https://api.stormpath.com/ui/dashboard). 75 | 76 | Once you've set the environment variables above, you can then initialize the 77 | `passport-stormpath` strategy like so: 78 | 79 | ```javascript 80 | var passport = require('passport'); 81 | var StormpathStrategy = require('passport-stormpath'); 82 | var strategy = new StormpathStrategy(); 83 | 84 | passport.use(strategy); 85 | passport.serializeUser(strategy.serializeUser); 86 | passport.deserializeUser(strategy.deserializeUser); 87 | ``` 88 | 89 | 90 | ## Options 91 | 92 | There are several options you can define when you are creating the strategy, 93 | each is listed in this section. 94 | 95 | #### Api Keys 96 | 97 | If you'd like to explicitly define your Stormpath API keys and application href 98 | settings, you can do so when creating the strategy object: 99 | 100 | ```javascript 101 | var strategy = new StormpathStrategy({ 102 | apiKeyId: 'STORMPATH_API_KEY_ID', 103 | apiKeySecret: 'STORMPATH_API_KEY_SECRET', 104 | appHref: 'STORMPATH_APP_HREF', 105 | }); 106 | ``` 107 | 108 | #### Account Store 109 | 110 | If you wish to authenticate against a particular directory, you can configure 111 | this for all authentication attempts when constructing the strategy. 112 | 113 | ```javascript 114 | var strategy = new StormpathStrategy({ 115 | accountStore: { 116 | href: 'http://api.stormpath.com/v1/directorys/your-directory' 117 | } 118 | }); 119 | ``` 120 | 121 | If you need to dynamically supply the account store, you can pass it 122 | on a per-call basis by manually invoking passport. 123 | 124 | ```javascript 125 | // Authenticate a user. 126 | router.post('/login', 127 | passport.authenticate('stormpath', 128 | { 129 | successRedirect: '/dashboard', 130 | failureRedirect: '/login', 131 | failureFlash: 'Invalid email or password.', 132 | accountStore: { 133 | href: 'http://api.stormpath.com/v1/directorys/your-directory' 134 | } 135 | } 136 | ) 137 | ); 138 | ``` 139 | 140 | 141 | #### Expansions 142 | 143 | Account resources (*e.g. Custom Data, Groups*) can be automatically expanded 144 | during the authentication process. Declare which resources you would like to 145 | expand by providing a comma separated list as the `expansions` option: 146 | 147 | ```javascript 148 | var strategy = new StormpathStrategy({ 149 | expansions: 'groups,customData' 150 | }); 151 | ``` 152 | 153 | 154 | #### Stormpath Client 155 | 156 | You can also provide your own Stormpath client instance by constructing it 157 | manually and then passing it and an application reference to the strategy 158 | constructor: 159 | 160 | ```javascript 161 | var stormpath = require('stormpath'); 162 | 163 | var spClient, spApp, strategy; 164 | 165 | spClient = new stormpath.Client({ 166 | apiKey: new stormpath.ApiKey( 167 | process.env['STORMPATH_API_KEY_ID'], 168 | process.env['STORMPATH_API_KEY_SECRET'] 169 | ) 170 | }); 171 | 172 | spClient.getApplication(process.env['STORMPATH_APP_HREF'], function(err, app) { 173 | if (err) { 174 | throw err; 175 | } 176 | spApp = app; 177 | strategy = new StormpathStrategy({ 178 | spApp: spApp, 179 | spClient: spClient 180 | }); 181 | passport.use(strategy); 182 | }); 183 | ``` 184 | 185 | 186 | ## Build Documentation 187 | 188 | All project documentation is written using [Sphinx](http://sphinx-doc.org/). To 189 | build the documentation, you'll need to have Python installed and working. 190 | 191 | To install Sphinx and all other dependencies, run: 192 | 193 | ```console 194 | $ pip install -r requirements.txt 195 | ``` 196 | 197 | This will install all the Python packages necessary to build the docs. 198 | 199 | Once you have Sphinx installed, go into the `docs` directory to build the 200 | documentation: 201 | 202 | ```console 203 | $ cd docs 204 | $ make html 205 | ``` 206 | 207 | When this process is finished, you'll see that all project documentation has 208 | been built into HTML available at `docs/_build/html`. 209 | 210 | 211 | ## Contributing 212 | 213 | You can make your own contributions by forking the `develop` branch, making 214 | your changes, and issuing pull-requests on the `develop` branch. 215 | 216 | We regularly maintain this repository, and are quick to review pull requests 217 | and accept changes! 218 | 219 | We <333 contributions! 220 | 221 | 222 | ## Copyright 223 | 224 | Copyright © 2014 Stormpath, Inc. and contributors. 225 | 226 | This project is open-source via the [Apache 2.0 License](http://www.apache.org/licenses/LICENSE-2.0). 227 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = _build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " devhelp to make HTML files and a Devhelp project" 34 | @echo " epub to make an epub" 35 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 36 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 37 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 38 | @echo " text to make text files" 39 | @echo " man to make manual pages" 40 | @echo " texinfo to make Texinfo files" 41 | @echo " info to make Texinfo files and run them through makeinfo" 42 | @echo " gettext to make PO message catalogs" 43 | @echo " changes to make an overview of all changed/added/deprecated items" 44 | @echo " xml to make Docutils-native XML files" 45 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 46 | @echo " linkcheck to check all external links for integrity" 47 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 48 | 49 | clean: 50 | rm -rf $(BUILDDIR)/* 51 | 52 | html: 53 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 54 | @echo 55 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 56 | 57 | dirhtml: 58 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 59 | @echo 60 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 61 | 62 | singlehtml: 63 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 64 | @echo 65 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 66 | 67 | pickle: 68 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 69 | @echo 70 | @echo "Build finished; now you can process the pickle files." 71 | 72 | json: 73 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 74 | @echo 75 | @echo "Build finished; now you can process the JSON files." 76 | 77 | htmlhelp: 78 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 79 | @echo 80 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 81 | ".hhp project file in $(BUILDDIR)/htmlhelp." 82 | 83 | qthelp: 84 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 85 | @echo 86 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 87 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 88 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/passport-stormpath.qhcp" 89 | @echo "To view the help file:" 90 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/passport-stormpath.qhc" 91 | 92 | devhelp: 93 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 94 | @echo 95 | @echo "Build finished." 96 | @echo "To view the help file:" 97 | @echo "# mkdir -p $$HOME/.local/share/devhelp/passport-stormpath" 98 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/passport-stormpath" 99 | @echo "# devhelp" 100 | 101 | epub: 102 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 103 | @echo 104 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 105 | 106 | latex: 107 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 108 | @echo 109 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 110 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 111 | "(use \`make latexpdf' here to do that automatically)." 112 | 113 | latexpdf: 114 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 115 | @echo "Running LaTeX files through pdflatex..." 116 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 117 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 118 | 119 | latexpdfja: 120 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 121 | @echo "Running LaTeX files through platex and dvipdfmx..." 122 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 123 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 124 | 125 | text: 126 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 127 | @echo 128 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 129 | 130 | man: 131 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 132 | @echo 133 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 134 | 135 | texinfo: 136 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 137 | @echo 138 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 139 | @echo "Run \`make' in that directory to run these through makeinfo" \ 140 | "(use \`make info' here to do that automatically)." 141 | 142 | info: 143 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 144 | @echo "Running Texinfo files through makeinfo..." 145 | make -C $(BUILDDIR)/texinfo info 146 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 147 | 148 | gettext: 149 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 150 | @echo 151 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 152 | 153 | changes: 154 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 155 | @echo 156 | @echo "The overview file is in $(BUILDDIR)/changes." 157 | 158 | linkcheck: 159 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 160 | @echo 161 | @echo "Link check complete; look for any errors in the above output " \ 162 | "or in $(BUILDDIR)/linkcheck/output.txt." 163 | 164 | doctest: 165 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 166 | @echo "Testing of doctests in the sources finished, look at the " \ 167 | "results in $(BUILDDIR)/doctest/output.txt." 168 | 169 | xml: 170 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 171 | @echo 172 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 173 | 174 | pseudoxml: 175 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 176 | @echo 177 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 178 | -------------------------------------------------------------------------------- /docs/about.rst: -------------------------------------------------------------------------------- 1 | .. _about: 2 | 3 | 4 | About 5 | ===== 6 | 7 | Not sure if passport-stormpath is for you? Read along, and I'll help you 8 | decide whether or not passport-stormpath is a good fit for your project! 9 | 10 | 11 | What is Stormpath? 12 | ------------------ 13 | 14 | `Stormpath`_ is an API service for creating and managing user accounts. 15 | Stormpath allows you to do things like: 16 | 17 | - Create user accounts. 18 | - Edit user accounts. 19 | - Store user data with each account. 20 | - Create groups and roles. 21 | - Assign users various permissions (groups, roles, etc.). 22 | - Handle complex authentication and authorization patterns. 23 | - Log users in via social login with `Facebook`_ and `Google`_ OAuth. 24 | - Cache user information for quick access. 25 | - Scale your application as you get more users. 26 | - Securely store your users and user data in a central location. 27 | 28 | In the backend, what Stormpath does is provide a simple REST API for storing 29 | user accounts. For instance, if you wanted to create a new user account given 30 | an email address and password, you could send Stormpath an ``HTTP POST`` request 31 | and Stormpath would create a new user account for you, and store it securely on 32 | Stormpath's cloud service. 33 | 34 | In addition to allowing you to create users, Stormpath also allows you to store 35 | custom data with each user account. Let's say you want to store a user's 36 | birthday -- you can send Stormpath an ``HTTP POST`` request to the user's 37 | account and store *any* variable JSON data (birthdays, images, movies, links, 38 | etc.). This information is encrypted in transit and at rest, ensuring your 39 | user data is secure. 40 | 41 | 42 | Who Should Use Stormpath 43 | ------------------------ 44 | 45 | Stormpath is a great service, but it's not for everyone! 46 | 47 | You might want to use Stormpath if: 48 | 49 | - You want to make user creation, management, and security as simple as 50 | possible. 51 | - User security is a top priority. We're known for our security. 52 | - Scaling your userbase is a potential problem (Stormpath handles scaling your 53 | users transparently). 54 | - You need to store custom user data along with your user's basic information 55 | (email, password). 56 | - You would like to have automatic email verification for new user accounts. 57 | - You would like to configure and customize password strength rules. 58 | - You'd like to keep your user data separate from your other applications to 59 | increase platform stability / availability. 60 | - You are building a service oriented application, in which multiple 61 | independent services need access to the same user data. 62 | - You are a big organization who would like to use Stormpath, but need to host 63 | it yourself (Stormpath has an on-premise system you can use internally). 64 | 65 | **Basically, Stormpath is a great match for applications of any size where 66 | security, speed, and simplicity are top priorities.** 67 | 68 | You might **NOT** want to use Stormpath if: 69 | 70 | - You are building an application that does not need user accounts. 71 | - Your application is meant for internal-only usage. 72 | - You aren't worried about user data / security much. 73 | - You aren't worried about application availability / redundancy. 74 | - You want to roll your own custom user authentication. 75 | 76 | Want to use Stormpath? OK, great! Let's get started! 77 | 78 | 79 | .. _Stormpath: https://stormpath.com/ 80 | .. _Facebook: https://www.facebook.com/ 81 | .. _Google: https://www.google.com/ 82 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | .. _changelog: 2 | 3 | 4 | Change Log 5 | ========== 6 | 7 | All library changes, in descending order. 8 | 9 | 10 | Version 0.2.3 11 | ------------- 12 | 13 | **Released on October 28, 2014.** 14 | 15 | - Updating README. 16 | - Adding new Sphinx documentation. 17 | 18 | 19 | Version 0.2.2 20 | ------------- 21 | 22 | **Released on September 27, 2014.** 23 | 24 | - Fixing an issue where the `expansions` option was not used by 25 | `deserializeUser`, thanks @doublerebel! 26 | 27 | 28 | Version 0.2.1 29 | ------------- 30 | 31 | **Released on September 22, 2014.** 32 | 33 | - Upgrading the stormpath dependency to `0.6.0`. 34 | 35 | Version 0.2.0 36 | ------------- 37 | 38 | **Released on September 19, 2014.** 39 | 40 | - After authentication the value of `req.user` will now be an `Account` object 41 | from the Stormpath Node SDK. Previously it was an object literal. 42 | - Added an `expansions` option to the strategy constructor, allowing you to 43 | expand resources on the account during login. 44 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # passport-stormpath documentation build configuration file, created by 4 | # sphinx-quickstart on Tue Feb 18 11:24:11 2014. 5 | # 6 | # This file is execfile()d with the current directory set to its 7 | # containing dir. 8 | # 9 | # Note that not all possible configuration values are present in this 10 | # autogenerated file. 11 | # 12 | # All configuration values have a default; values that are commented out 13 | # serve to show the default. 14 | 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | #sys.path.insert(0, os.path.abspath('.')) 20 | 21 | # -- General configuration ------------------------------------------------ 22 | 23 | # If your documentation needs a minimal Sphinx version, state it here. 24 | #needs_sphinx = '1.0' 25 | 26 | # Add any Sphinx extension module names here, as strings. They can be 27 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 28 | # ones. 29 | extensions = [] 30 | 31 | # Add any paths that contain templates here, relative to this directory. 32 | templates_path = ['_templates'] 33 | 34 | # The suffix of source filenames. 35 | source_suffix = '.rst' 36 | 37 | # The encoding of source files. 38 | #source_encoding = 'utf-8-sig' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # General information about the project. 44 | project = u'passport-stormpath' 45 | copyright = u'2014, Stormpath, Inc.' 46 | 47 | # The version info for the project you're documenting, acts as replacement for 48 | # |version| and |release|, also used in various other places throughout the 49 | # built documents. 50 | # 51 | # The short X.Y version. 52 | version = '0.2.3' 53 | # The full version, including alpha/beta/rc tags. 54 | release = '0.2.3' 55 | 56 | # The language for content autogenerated by Sphinx. Refer to documentation 57 | # for a list of supported languages. 58 | #language = None 59 | 60 | # There are two options for replacing |today|: either, you set today to some 61 | # non-false value, then it is used: 62 | #today = '' 63 | # Else, today_fmt is used as the format for a strftime call. 64 | #today_fmt = '%B %d, %Y' 65 | 66 | # List of patterns, relative to source directory, that match files and 67 | # directories to ignore when looking for source files. 68 | exclude_patterns = ['_build'] 69 | 70 | # The reST default role (used for this markup: `text`) to use for all 71 | # documents. 72 | #default_role = None 73 | 74 | # If true, '()' will be appended to :func: etc. cross-reference text. 75 | #add_function_parentheses = True 76 | 77 | # If true, the current module name will be prepended to all description 78 | # unit titles (such as .. function::). 79 | #add_module_names = True 80 | 81 | # If true, sectionauthor and moduleauthor directives will be shown in the 82 | # output. They are ignored by default. 83 | #show_authors = False 84 | 85 | # The name of the Pygments (syntax highlighting) style to use. 86 | pygments_style = 'sphinx' 87 | 88 | # A list of ignored prefixes for module index sorting. 89 | #modindex_common_prefix = [] 90 | 91 | # If true, keep warnings as "system message" paragraphs in the built documents. 92 | #keep_warnings = False 93 | 94 | 95 | # -- Options for HTML output ---------------------------------------------- 96 | 97 | # The theme to use for HTML and HTML Help pages. See the documentation for 98 | # a list of builtin themes. 99 | html_theme = 'stormpath' 100 | 101 | # Theme options are theme-specific and customize the look and feel of a theme 102 | # further. For a list of options available for each theme, see the 103 | # documentation. 104 | html_theme_options = {} 105 | 106 | # Add any paths that contain custom themes here, relative to this directory. 107 | html_theme_path = ['_themes'] 108 | 109 | # The name for this set of Sphinx documents. If None, it defaults to 110 | # " v documentation". 111 | #html_title = None 112 | 113 | # A shorter title for the navigation bar. Default is the same as html_title. 114 | #html_short_title = None 115 | 116 | # The name of an image file (relative to this directory) to place at the top 117 | # of the sidebar. 118 | #html_logo = None 119 | 120 | # The name of an image file (within the static path) to use as favicon of the 121 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 122 | # pixels large. 123 | #html_favicon = None 124 | 125 | # Add any paths that contain custom static files (such as style sheets) here, 126 | # relative to this directory. They are copied after the builtin static files, 127 | # so a file named "default.css" will overwrite the builtin "default.css". 128 | html_static_path = ['_static'] 129 | 130 | # Make the default syntax highlighting target Javascript code snippets. 131 | highlight_language = 'javascript' 132 | 133 | # Add any extra paths that contain custom files (such as robots.txt or 134 | # .htaccess) here, relative to this directory. These files are copied 135 | # directly to the root of the documentation. 136 | #html_extra_path = [] 137 | 138 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 139 | # using the given strftime format. 140 | #html_last_updated_fmt = '%b %d, %Y' 141 | 142 | # If true, SmartyPants will be used to convert quotes and dashes to 143 | # typographically correct entities. 144 | #html_use_smartypants = True 145 | 146 | # Custom sidebar templates, maps document names to template names. 147 | html_sidebars = {} 148 | 149 | # Additional templates that should be rendered to pages, maps page names to 150 | # template names. 151 | #html_additional_pages = {} 152 | 153 | # If false, no module index is generated. 154 | #html_domain_indices = True 155 | 156 | # If false, no index is generated. 157 | #html_use_index = True 158 | 159 | # If true, the index is split into individual pages for each letter. 160 | #html_split_index = False 161 | 162 | # If true, links to the reST sources are added to the pages. 163 | #html_show_sourcelink = True 164 | 165 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 166 | #html_show_sphinx = True 167 | 168 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 169 | #html_show_copyright = True 170 | 171 | # If true, an OpenSearch description file will be output, and all pages will 172 | # contain a tag referring to it. The value of this option must be the 173 | # base URL from which the finished HTML is served. 174 | #html_use_opensearch = '' 175 | 176 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 177 | #html_file_suffix = None 178 | 179 | # Output file base name for HTML help builder. 180 | htmlhelp_basename = 'passport-stormpathdoc' 181 | 182 | 183 | # -- Options for LaTeX output --------------------------------------------- 184 | 185 | latex_elements = { 186 | # The paper size ('letterpaper' or 'a4paper'). 187 | #'papersize': 'letterpaper', 188 | 189 | # The font size ('10pt', '11pt' or '12pt'). 190 | #'pointsize': '10pt', 191 | 192 | # Additional stuff for the LaTeX preamble. 193 | #'preamble': '', 194 | } 195 | 196 | # Grouping the document tree into LaTeX files. List of tuples 197 | # (source start file, target name, title, 198 | # author, documentclass [howto, manual, or own class]). 199 | latex_documents = [ 200 | ('index', 'passport-stormpath.tex', u'passport-stormpath Documentation', 201 | u'Stormpath, Inc.', 'manual'), 202 | ] 203 | 204 | # The name of an image file (relative to this directory) to place at the top of 205 | # the title page. 206 | #latex_logo = None 207 | 208 | # For "manual" documents, if this is true, then toplevel headings are parts, 209 | # not chapters. 210 | #latex_use_parts = False 211 | 212 | # If true, show page references after internal links. 213 | #latex_show_pagerefs = False 214 | 215 | # If true, show URL addresses after external links. 216 | #latex_show_urls = False 217 | 218 | # Documents to append as an appendix to all manuals. 219 | #latex_appendices = [] 220 | 221 | # If false, no module index is generated. 222 | #latex_domain_indices = True 223 | 224 | 225 | # -- Options for manual page output --------------------------------------- 226 | 227 | # One entry per manual page. List of tuples 228 | # (source start file, name, description, authors, manual section). 229 | man_pages = [ 230 | ('index', 'passport-stormpath', u'passport-stormpath Documentation', 231 | [u'Stormpath, Inc.'], 1) 232 | ] 233 | 234 | # If true, show URL addresses after external links. 235 | #man_show_urls = False 236 | 237 | 238 | # -- Options for Texinfo output ------------------------------------------- 239 | 240 | # Grouping the document tree into Texinfo files. List of tuples 241 | # (source start file, target name, title, author, 242 | # dir menu entry, description, category) 243 | texinfo_documents = [ 244 | ('index', 'passport-stormpath', u'passport-stormpath Documentation', 245 | u'Stormpath, Inc.', 'passport-stormpath', 'One line description of project.', 246 | 'Miscellaneous'), 247 | ] 248 | 249 | # Documents to append as an appendix to all manuals. 250 | #texinfo_appendices = [] 251 | 252 | # If false, no module index is generated. 253 | #texinfo_domain_indices = True 254 | 255 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 256 | #texinfo_show_urls = 'footnote' 257 | 258 | # If true, do not generate a @detailmenu in the "Top" node's menu. 259 | #texinfo_no_detailmenu = False 260 | -------------------------------------------------------------------------------- /docs/help.rst: -------------------------------------------------------------------------------- 1 | .. _help: 2 | 3 | 4 | Getting Help 5 | ============ 6 | 7 | Have a question you can't find an answer to? Things not working as expected? 8 | We can help! 9 | 10 | All of the official Stormpath client libraries (*including this one!*) are 11 | officially supported by Stormpath's incredibly amazing-and-hip support team! 12 | 13 | If you have a question, or need in-depth technical help, you can drop us an 14 | email anytime: support@stormpath.com 15 | 16 | If you visit our website (https://stormpath.com/), you can also click the "Chat 17 | with us!" button near the bottom right of the page to chat with us live, in 18 | real-time! 19 | 20 | And lastly, we're always available via Twitter as well! We're `@gostormpath`_ 21 | on Twitter. 22 | 23 | 24 | .. _@gostormpath: https://twitter.com/gostormpath 25 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | passport-stormpath 2 | ================== 3 | 4 | passport-stormpath is a strategy for `Passport`_ that makes it *incredibly* 5 | simple to add users and user data to your application. It aims to completely 6 | abstract away all user registration, login, authentication, and authorization 7 | problems, and make building secure websites painless. And the best part? 8 | **You don't even need a database!** 9 | 10 | .. note:: 11 | Passport.js is purely for authentication (i.e. checking that someone's 12 | username and password is valid, or getting their identity from a social 13 | provider). It is not designed for handling user registrations, manage 14 | password resets, or implement email verification workflows, etc. In turn, 15 | our Passport strategy only automates authentication. If you need to do more, like password reset, you'll need to also use the `Stormpath Node SDK`_. 16 | 17 | For a more seamless and automated experience, check out 18 | our `Express-Stormpath`_ library instead of Passport.js. It provides a full suite of user management features for your Express-based web application: 19 | 20 | - Create, register and authenticate users. 21 | - Store custom user data with each account. 22 | - Create and assign permissions (groups, roles, etc.). 23 | - Handle complex authentication and authorization patterns, like multi-tenancy. 24 | - Log users in via social login with Facebook and Google OAuth. 25 | - Cache user information for quick access. 26 | - Secure all your passwords. 27 | - Automate all your password reset and account verification workflows. 28 | 29 | 30 | User's Guide 31 | ------------ 32 | 33 | This part of the documentation will show you how to get started with 34 | passport-stormpath. If you're a new passport-stormpath user, start here! 35 | 36 | .. toctree:: 37 | :maxdepth: 2 38 | 39 | about 40 | setup 41 | quickstart 42 | help 43 | 44 | 45 | Additional Notes 46 | ---------------- 47 | 48 | This part of the documentation covers changes between versions and upgrade 49 | information, to help you migrate to newer versions of passport-stormpath 50 | easily. 51 | 52 | passport-stormpath is made available under the `Apache License, Version 2.0`_. 53 | In short, you can do pretty much whatever you want! 54 | 55 | .. toctree:: 56 | :maxdepth: 2 57 | 58 | changelog 59 | upgrading 60 | 61 | 62 | .. _Passport: http://passportjs.org/ 63 | .. _Stormpath Node SDK: https://docs.stormpath.com/nodejs/api/ 64 | .. _Express-Stormpath: https://docs.stormpath.com/nodejs/express/ 65 | .. _Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0.html 66 | -------------------------------------------------------------------------------- /docs/quickstart.rst: -------------------------------------------------------------------------------- 1 | .. _quickstart: 2 | 3 | 4 | Quickstart 5 | ========== 6 | 7 | Now that we've got all the prerequisites out of the way, let's take a look at 8 | some code! Integrating passport-stormpath into an application is pretty quick! 9 | 10 | 11 | Initialize passport-stormpath 12 | ----------------------------- 13 | 14 | To initialize passport-stormpath, you need to use the initialize the strategy as 15 | well as set some environment variables. 16 | 17 | Before continuing, you'll need to set some environment variables which contain 18 | your Stormpath credentials (*these credentials can be found inside of the 19 | apiKey.properties file you downloaded previously*):: 20 | 21 | $ export STORMPATH_API_KEY_ID=xxx 22 | $ export STORMPATH_API_KEY_SECRET=xxx 23 | $ export STORMPATH_APP_HREF=xxx 24 | 25 | .. note:: 26 | If you're using Windows, you'll need to set these environment variables in a 27 | different way. This article should help: 28 | http://www.computerhope.com/issues/ch000549.htm 29 | 30 | The ``STORMPATH_APP_HREF`` variable needs to be your Stormpath Application's 31 | URL. To get this, visit your `Stormpath Application Dashboard`_, click your 32 | application, then copy the URL. 33 | 34 | Once you've set your environment variables, you can then properly initialize the 35 | Stormpath Passport strategy like so:: 36 | 37 | var passport = require('passport'); 38 | var StormpathStrategy = require('passport-stormpath'); 39 | 40 | var strategy = new StormpathStrategy(); 41 | 42 | passport.use(strategy); 43 | passport.serializeUser(strategy.serializeuser); 44 | passport.deserializeUser(strategy.deserializeUser); 45 | 46 | // Your application logic here. 47 | 48 | That's it! 49 | 50 | You've now fully initialized the Stormpath Passport strategy, and can continue 51 | building your Passport-based application. 52 | 53 | For more information on building applications with Passport, please read through 54 | the official `Passport Guide`_. 55 | 56 | 57 | .. _Stormpath applications: https://api.stormpath.com/v#!applications 58 | .. _Stormpath Application Dashboard: https://api.stormpath.com/v#!applications 59 | .. _Stormpath dashboard: https://api.stormpath.com/ui/dashboard 60 | .. _Passport Guide: http://passportjs.org/guide/ 61 | -------------------------------------------------------------------------------- /docs/setup.rst: -------------------------------------------------------------------------------- 1 | .. _setup: 2 | 3 | 4 | Setup 5 | ===== 6 | 7 | This section covers the basic setup you need to perform in order to get started 8 | with passport-stormpath. 9 | 10 | 11 | Create a Stormpath Account 12 | -------------------------- 13 | 14 | Now that you've decided to use Stormpath, the first thing you'll want to use is 15 | create a new Stormpath account: https://api.stormpath.com/register 16 | 17 | 18 | Create an API Key Pair 19 | ---------------------- 20 | 21 | Once you've created a new account, create a new API key pair by logging into 22 | your dashboard and clicking the "Create an API Key" button. This will generate 23 | a new API key for you, and prompt you to download your keypair. 24 | 25 | .. note:: 26 | Please keep the API key pair file you just downloaded safe! These two keys 27 | allow you to make Stormpath API requests, and should be properly protected, 28 | backed up, etc. 29 | 30 | Once you've downloaded your `apiKey.properties` file, save it in your home 31 | directory in a file named `~/.stormpath/apiKey.properties`. To ensure no other 32 | users on your system can access the file, you'll also want to change the file's 33 | permissions. You can do this by running:: 34 | 35 | $ chmod go-rwx ~/.stormpath/apiKey.properties 36 | 37 | 38 | Check Out Your Stormpath Application 39 | ------------------------------------ 40 | 41 | Next, you'll want to check out your Stormpath Application. 42 | 43 | Stormpath allows you to provision any number of "Applications". An 44 | "Application" is just Stormpath's term for a project. 45 | 46 | Let's say you want to build a few separate websites. One site named 47 | "dronewars.com", and another named "carswap.com". In this case, you'd want to 48 | create two separate Stormpath Applications, one named "dronewars" and another 49 | named "carswap". Each Stormpath Application should represent a real life 50 | application of some sort. 51 | 52 | The general rule is that you should create one Application per website (or 53 | project). Since we're just getting set up, we'll only need a single 54 | Application. 55 | 56 | By default, Stormpath has already created an Application for you. To view it, 57 | click the "Applications" tab in the Stormpath dashboard, then click the 58 | application you see below. 59 | 60 | Now that you understand Applications, you're ready to plug passport-stormpath 61 | into your project! 62 | 63 | 64 | Bonus: Create a Directory 65 | ------------------------- 66 | 67 | As you might have noticed, Stormpath also has something called "Directories". 68 | When you created an Application in the previous step, Stormpath automatically 69 | created a new Directory for you. 70 | 71 | You can think of Directories in Stormpath as buckets of user accounts. Every 72 | user account that you create on Stormpath will belong to a Directory. 73 | Directories hold unique groups of users. 74 | 75 | In most situations, your Stormpath Application will have a single Directory 76 | associated with it which is where all of your users will go. In some 77 | situations, however, you might want to create additional Directories (or share a 78 | Directory between multiple Applications). 79 | 80 | Let's say you have two separate websites (*and therefore, two separate Stormpath 81 | Applications*): "dronewars.com" and "carswap.com". If you wanted both of your 82 | websites to share the same user accounts (*so that if a user signs up on 83 | dronewars.com they can use their same login on carswap.com*), you could 84 | accomplish that by having a single Directory, and mapping it to both of your 85 | Stormpath Applications. 86 | 87 | Directories are useful for sharing and segmenting users in more complex 88 | authentication scenarios. 89 | 90 | You can read more about Stormpath Directories here: 91 | http://docs.stormpath.com/rest/product-guide/#directories 92 | 93 | .. note:: 94 | Stormpath has multiple types of "Directories". There are: "Cloud 95 | Directories", "Mirror Directories", "Facebook Directories" and "Google 96 | Directories". 97 | 98 | Cloud Directories hold *typical* user accounts. 99 | 100 | Facebook and Google Directories allow you to automatically create Stormpath 101 | user accounts for both Facebook and Google users (*using social login*). 102 | Social login will be covered in detail later on. 103 | 104 | Mirror Directories are used for syncing with `Active Directory`_ and 105 | `LDAP`_ services (most people don't ever need these). 106 | 107 | 108 | Install the Package 109 | ------------------- 110 | 111 | Now that you've got a Stormpath account all setup and ready to go, all that's 112 | left to do before we can dive into the code is install the `passport-stormpath`_ 113 | package from `NPM`_. 114 | 115 | To install passport-stormpath, you'll need ``npm``. You can install the latest 116 | version of passport-stormpath by running:: 117 | 118 | $ npm install passport passport-stormpath 119 | 120 | If you'd like to upgrade to the latest version of passport-stormpath (*maybe you 121 | have an old version installed*), you can run:: 122 | 123 | $ npm update passport-stormpath 124 | 125 | To force an upgrade. 126 | 127 | 128 | .. _Active Directory: http://en.wikipedia.org/wiki/Active_Directory 129 | .. _LDAP: http://en.wikipedia.org/wiki/Lightweight_Directory_Access_Protocol 130 | .. _passport-stormpath: https://www.npmjs.org/package/passport-stormpath 131 | .. _NPM: https://www.npmjs.org/ 132 | -------------------------------------------------------------------------------- /docs/upgrading.rst: -------------------------------------------------------------------------------- 1 | .. _upgrading: 2 | 3 | Upgrade Guide 4 | ============= 5 | 6 | This page contains specific upgrading instructions to help you migrate between 7 | Express-Stormpath releases. 8 | 9 | 10 | Version 0.2.2 -> Version 0.2.3 11 | ------------------------------ 12 | 13 | **No changes needed!** 14 | 15 | 16 | Version 0.2.1 -> Version 0.2.2 17 | ------------------------------ 18 | 19 | **No changes needed!** 20 | 21 | 22 | Version 0.2.0 -> Version 0.2.1 23 | ------------------------------ 24 | 25 | **No changes needed!** 26 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var Strategy = require('./strategy'); 5 | 6 | /** 7 | * Expose `Strategy` directly from package. 8 | */ 9 | exports = module.exports = Strategy; 10 | 11 | /** 12 | * Export constructors. 13 | */ 14 | exports.Strategy = Strategy; 15 | -------------------------------------------------------------------------------- /lib/strategy.js: -------------------------------------------------------------------------------- 1 | var stormpath = require('stormpath'); 2 | var version = require('../package.json').version; 3 | var passportVersion = require('passport/package.json').version; 4 | 5 | function lookup(obj, field) { 6 | if (!obj) { 7 | return null; 8 | } 9 | 10 | var chain = field.split(']').join('').split('['); 11 | for (var i = 0, len = chain.length; i < len; i++) { 12 | var prop = obj[chain[i]]; 13 | 14 | if (typeof(prop) === 'undefined') { 15 | return null; 16 | } else if (typeof(prop) !== 'object') { 17 | return prop; 18 | } 19 | 20 | obj = prop; 21 | } 22 | 23 | return null; 24 | } 25 | 26 | function Strategy(o) { 27 | var opts = o || {}; 28 | 29 | this._usernameField = opts.usernameField || 'username'; 30 | this._passwordField = opts.passwordField || 'password'; 31 | 32 | var apiKeyId = opts.apiKeyId || process.env['STORMPATH_API_KEY_ID'] || ''; 33 | var apiKeySecret = opts.apiKeySecret || process.env['STORMPATH_API_KEY_SECRET'] || ''; 34 | var appHref = opts.appHref || process.env['STORMPATH_APP_HREF'] || ''; 35 | 36 | var self = this; 37 | self.expansions = opts.expansions || null; 38 | self.accountStore = opts.accountStore || null; 39 | 40 | if (opts.spClient) { 41 | self.spClient = opts.spClient; 42 | } else { 43 | self.spClient = new stormpath.Client({ 44 | apiKey: new stormpath.ApiKey(apiKeyId, apiKeySecret), 45 | userAgent: 'passport-stormpath/' + version + ' ' + 'passport/' + passportVersion 46 | }); 47 | } 48 | 49 | if (opts.spApp) { 50 | self.spApp = opts.spApp; 51 | } else { 52 | self.spClient.getApplication(appHref, function(err, app) { 53 | self.spApp = app; 54 | if (err) { 55 | throw err; 56 | } 57 | }); 58 | } 59 | 60 | self.serializeUser = function(user, done) { 61 | done(null, user.href); 62 | }; 63 | 64 | self.deserializeUser = function(userHref, done) { 65 | var options = self.expansions ? { expand: self.expansions } : null; 66 | self.spClient.getAccount(userHref, options, function(err, account) { 67 | done(err, account); 68 | }); 69 | }; 70 | 71 | return this; 72 | } 73 | 74 | Strategy.prototype.name = 'stormpath'; 75 | 76 | Strategy.prototype.authenticate = function(req, options) { 77 | options = options || {}; 78 | 79 | var self = this; 80 | var username = lookup(req.body, this._usernameField) || lookup(req.query, this._usernameField); 81 | var password = lookup(req.body, this._passwordField) || lookup(req.query, this._passwordField); 82 | var data = { username: username, password: password }; 83 | 84 | var accountStoreHref = (options.accountStore && options.accountStore.href) || (self.accountStore && self.accountStore.href); 85 | 86 | if (accountStoreHref) { 87 | data.accountStore = { href: accountStoreHref }; 88 | } 89 | 90 | if (!username || !password) { 91 | return self.fail({ message: options.badRequestMessage || 'Missing credentials' }, 400); 92 | } 93 | 94 | self.spApp.authenticateAccount(data, function(err, authenticationResult) { 95 | if (err) { 96 | return self.fail({ message: err.userMessage }, err.status || 500); 97 | } else { 98 | var options = self.expansions ? { expand: self.expansions } : null; 99 | authenticationResult.getAccount(options, function(err, account) { 100 | if (err) { 101 | return self.fail({ message: err.userMessage }, err.status || 500); 102 | } else { 103 | return self.success(account); 104 | } 105 | }); 106 | } 107 | }); 108 | }; 109 | 110 | module.exports = Strategy; 111 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport-stormpath", 3 | "version": "0.2.4", 4 | "description": "Official Stormpath Passport Strategy", 5 | "main": "lib/index.js", 6 | "scripts": { 7 | "test": "./node_modules/mocha/bin/mocha", 8 | "coverage": "node ./node_modules/istanbul/lib/cli cover ./node_modules/mocha/bin/_mocha" 9 | }, 10 | "keywords": [ 11 | "stormpath", 12 | "passport" 13 | ], 14 | "homepage": "https://github.com/stormpath/passport-stormpath", 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/stormpath/passport-stormpath.git" 18 | }, 19 | "author": { 20 | "name": "Stormpath, Inc.", 21 | "email": "support@stormpath.com", 22 | "url": "http://www.stormpath.com" 23 | }, 24 | "license": "Apache-2.0", 25 | "devDependencies": { 26 | "grunt": "^0.4.5", 27 | "grunt-cli": "^0.1.13", 28 | "grunt-mocha-test": "^0.10.0", 29 | "chai": "^1.9.1", 30 | "grunt-contrib-watch": "^0.6.1", 31 | "grunt-contrib-jshint": "^0.10.0", 32 | "jshint-stylish": "^0.1.5", 33 | "istanbul": "^0.2.7", 34 | "mocha": "^1.18.2", 35 | "sinon": "^1.9.1", 36 | "proxyquire": "^0.6.0" 37 | }, 38 | "dependencies": { 39 | "stormpath": "^0.6.0", 40 | "passport": "^0.3.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Jinja2==2.7.3 2 | MarkupSafe==0.23 3 | Pygments==1.6 4 | Sphinx==1.2.3 5 | docutils==0.12 6 | wsgiref==0.1.2 7 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert; 2 | var expect = require('chai').expect; 3 | 4 | var sinon = require('sinon'); 5 | 6 | var StormpathStrategy = require('../lib/strategy'); 7 | 8 | var MockSpApp = require('./mocks/sp-app'); 9 | var MockSpClient = require('./mocks/sp-client'); 10 | 11 | describe('test framework', function(){ 12 | it('should assert true', function(){ 13 | assert.equal(true,true); 14 | }); 15 | }); 16 | 17 | describe('lib entry', function(){ 18 | var entry = require('../lib'); 19 | it('should export an function', function(){ 20 | assert.equal(typeof entry,"function"); 21 | }); 22 | it('should export a constructor', function(){ 23 | assert.equal(typeof entry.Strategy,"function"); 24 | }); 25 | it('export and constructor are same thing', function(){ 26 | assert.equal(entry,entry.Strategy); 27 | }); 28 | }); 29 | 30 | describe('strategy entry', function(){ 31 | it('should export an function', function(){ 32 | assert.equal(typeof StormpathStrategy,"function"); 33 | }); 34 | }); 35 | 36 | describe('Strategy instance', function(){ 37 | 38 | describe('without env configuration',function(){ 39 | before(function(){ 40 | /* unset env vars for these tests test */ 41 | process.env['STORMPATH_API_KEY_ID'] = ""; 42 | process.env['STORMPATH_API_KEY_SECRET'] = ""; 43 | process.env['STORMPATH_APP_HREF'] = ""; 44 | }); 45 | it('should not be instantiable if no client or app data is provided',function(){ 46 | expect(function(){ 47 | return new StormpathStrategy(); 48 | }).to.throw(); 49 | }); 50 | it('should not be instantiable if no app data is provided',function(){ 51 | var stormpath = require('stormpath'); 52 | expect(function(){ 53 | return new StormpathStrategy({ 54 | spClient: new stormpath.Client({apiKey:new stormpath.ApiKey("x","x")}) 55 | }); 56 | }).to.throw(); 57 | }); 58 | }); 59 | 60 | it('should have an authenticate method',function(){ 61 | var instance = new StormpathStrategy({ 62 | spApp:new MockSpApp(), 63 | spClient: new MockSpClient() 64 | }); 65 | assert.equal(typeof instance.authenticate, "function"); 66 | }); 67 | it('should define a serializeUser rmethod',function(){ 68 | var instance = new StormpathStrategy({ 69 | spApp:new MockSpApp(), 70 | spClient: new MockSpClient() 71 | }); 72 | assert.equal(typeof instance.serializeUser, "function"); 73 | }); 74 | it('should define a deserializeUser rmethod',function(){ 75 | var instance = new StormpathStrategy({ 76 | spApp:new MockSpApp(), 77 | spClient: new MockSpClient() 78 | }); 79 | assert.equal(typeof instance.deserializeUser, "function"); 80 | }); 81 | it('should call fail if an empty request body provided',function(){ 82 | var instance = new StormpathStrategy({ 83 | spApp:new MockSpApp(), 84 | spClient: new MockSpClient() 85 | }); 86 | var fail = sinon.spy(); 87 | instance.fail = fail; 88 | expect(instance.authenticate.bind(instance,require('./mocks/req').empty)).to.not.throw(); 89 | assert(fail.called,'fail was not called'); 90 | }); 91 | it('should call fail if a malformed request body provided',function(){ 92 | var instance = new StormpathStrategy({ 93 | spApp:new MockSpApp(), 94 | spClient: new MockSpClient() 95 | }); 96 | var fail = sinon.spy(); 97 | instance.fail = fail; 98 | expect(instance.authenticate.bind(instance,require('./mocks/req').malformed)).to.not.throw(); 99 | assert(fail.called,'fail was not called'); 100 | }); 101 | it('should call fail if body params are not strings',function(){ 102 | var instance = new StormpathStrategy({ 103 | spApp:new MockSpApp(), 104 | spClient: new MockSpClient() 105 | }); 106 | var fail = sinon.spy(); 107 | instance.fail = fail; 108 | expect(instance.authenticate.bind(instance,require('./mocks/req').notStrings)).to.not.throw(); 109 | assert(fail.called,'fail was not called'); 110 | }); 111 | it('should support custom fields',function(){ 112 | var instance = new StormpathStrategy({ 113 | spApp:new MockSpApp(), 114 | spClient: new MockSpClient(), 115 | usernameField:"un", 116 | passwordField:"pw" 117 | }); 118 | var success = sinon.spy(); 119 | instance.success = success; 120 | expect(instance.authenticate.bind(instance,require('./mocks/req').custom)).to.not.throw(); 121 | assert(success.called,'success was not called'); 122 | }); 123 | it('should call success if valid login is provieded',function(){ 124 | var instance = new StormpathStrategy({ 125 | spApp:new MockSpApp(), 126 | spClient: new MockSpClient() 127 | }); 128 | var success = sinon.spy(); 129 | instance.success = success; 130 | expect(instance.authenticate.bind(instance,require('./mocks/req').good)).to.not.throw(); 131 | assert(success.called,'success was not called'); 132 | }); 133 | it('should call fail and not success if invalid login is provieded',function(){ 134 | var instance = new StormpathStrategy({ 135 | spApp:new MockSpApp(), 136 | spClient: new MockSpClient() 137 | }); 138 | var fail = sinon.spy(); 139 | var success = sinon.spy(); 140 | instance.fail = fail; 141 | instance.success = success; 142 | expect(instance.authenticate.bind(instance,require('./mocks/req').bad)).to.not.throw(); 143 | assert(fail.called,'fail was called'); 144 | assert(!success.called,'success was called but shouldnt have been'); 145 | }); 146 | it('should be instantiable without a client or app if api values are provided',function(){ 147 | expect(function(){ 148 | return new StormpathStrategy({ 149 | apiKeyId:"x", 150 | apiKeySecret: "x", 151 | appHref: "x" 152 | }); 153 | }).to.not.throw(); 154 | }); 155 | it('should be instantiable with a app object and client credentials',function(){ 156 | expect(function(){ 157 | return new StormpathStrategy({ 158 | spApp:new MockSpApp(), 159 | apiKeyId: "x", 160 | apiKeySecret: "x", 161 | }); 162 | }).to.not.throw(); 163 | }); 164 | it('should be instantiable with a client object and app href',function(){ 165 | expect(function(){ 166 | return new StormpathStrategy({ 167 | appHref: "x", 168 | spClient: new MockSpClient() 169 | }); 170 | }).to.not.throw(); 171 | }); 172 | 173 | it('should return the correct data from serializeUser',function(){ 174 | var instance = new StormpathStrategy({ 175 | spApp:new MockSpApp(), 176 | spClient: new MockSpClient() 177 | }); 178 | var cb = sinon.spy(); 179 | instance.serializeUser({href:"a"},cb); 180 | assert(cb.calledWith(null,"a")); 181 | }); 182 | 183 | it('should return the correct data from deserializeUser',function(){ 184 | var instance = new StormpathStrategy({ 185 | spApp:new MockSpApp(), 186 | spClient: new MockSpClient() 187 | }); 188 | var cb = sinon.spy(); 189 | instance.deserializeUser("a",cb); 190 | assert(cb.calledWith(null,{href:"a"})); 191 | }); 192 | 193 | it('should have a name property',function(){ 194 | var instance = new StormpathStrategy({ 195 | spApp:new MockSpApp(), 196 | spClient: new MockSpClient() 197 | }); 198 | assert.equal(instance.name, "stormpath"); 199 | }); 200 | 201 | it('should pass accountStore, if given as a config option',function(){ 202 | var mockApp = new MockSpApp(); 203 | var authenticateAccount = sinon.spy(mockApp,'authenticateAccount'); 204 | var instance = new StormpathStrategy({ 205 | spApp:mockApp, 206 | spClient: new MockSpClient(), 207 | accountStore: { 208 | href: 'xyz' 209 | } 210 | }); 211 | var success = sinon.spy(); 212 | instance.success = success; 213 | expect(instance.authenticate.bind(instance,require('./mocks/req').good)).to.not.throw(); 214 | expect(authenticateAccount.getCall(0).args[0].accountStore.href).to.equal('xyz'); 215 | }); 216 | 217 | it('should pass accountStore if given as authenticate option',function(){ 218 | var mockApp = new MockSpApp(); 219 | var authenticateAccount = sinon.spy(mockApp,'authenticateAccount'); 220 | var instance = new StormpathStrategy({ 221 | spApp:mockApp, 222 | spClient: new MockSpClient() 223 | }); 224 | var success = sinon.spy(); 225 | instance.success = success; 226 | expect(instance.authenticate.bind(instance,require('./mocks/req').good,{accountStore: {href:'abc'}})).to.not.throw(); 227 | expect(authenticateAccount.getCall(0).args[0].accountStore.href).to.equal('abc'); 228 | }); 229 | 230 | it('should pass accountStore, overriding a config default, if given as authenticate option',function(){ 231 | var mockApp = new MockSpApp(); 232 | var authenticateAccount = sinon.spy(mockApp,'authenticateAccount'); 233 | var instance = new StormpathStrategy({ 234 | spApp:mockApp, 235 | spClient: new MockSpClient(), 236 | accountStore: { 237 | href: 'xyz' 238 | } 239 | }); 240 | var success = sinon.spy(); 241 | instance.success = success; 242 | expect(instance.authenticate.bind(instance,require('./mocks/req').good,{accountStore: {href:'abc'}})).to.not.throw(); 243 | expect(authenticateAccount.getCall(0).args[0].accountStore.href).to.equal('abc'); 244 | }); 245 | }); 246 | -------------------------------------------------------------------------------- /test/mocks/req.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | empty: { 3 | body: { 4 | 5 | }, 6 | query:{ 7 | 8 | } 9 | }, 10 | good: { 11 | body: { 12 | username: "good", 13 | password: "good" 14 | }, 15 | query:{ 16 | 17 | } 18 | }, 19 | bad: { 20 | body: { 21 | username: "bad", 22 | password: "bad" 23 | }, 24 | query:{ 25 | 26 | } 27 | }, 28 | malformed:{ 29 | 30 | }, 31 | notStrings:{ 32 | body:{ 33 | username: {}, 34 | password: {} 35 | } 36 | }, 37 | custom: { 38 | body:{ 39 | un:"good", 40 | pw:"good" 41 | } 42 | } 43 | }; -------------------------------------------------------------------------------- /test/mocks/sp-app.js: -------------------------------------------------------------------------------- 1 | function MockSpApp(){ 2 | 3 | } 4 | 5 | MockSpApp.prototype.authenticateAccount = function(data,cb) { 6 | if(data.username==="good" && data.password === "good"){ 7 | cb(null,{ 8 | account:{}, 9 | getAccount: function(expansions,cb){ 10 | (expansions || cb)(null,{}); 11 | } 12 | }); 13 | }else{ 14 | cb({userMessage:"Invalid username or password."}); 15 | } 16 | 17 | }; 18 | 19 | module.exports = MockSpApp; -------------------------------------------------------------------------------- /test/mocks/sp-client.js: -------------------------------------------------------------------------------- 1 | var MockSpApp = require('./sp-app'); 2 | 3 | function MockSpClient(){ 4 | 5 | } 6 | 7 | MockSpClient.prototype.getApplication = function(appHref,cb) { 8 | cb(null,new MockSpApp()); 9 | }; 10 | 11 | 12 | MockSpClient.prototype.getAccount = function(/* userHref,[options],cb */) { 13 | var args = Array.prototype.slice.call(arguments); 14 | var href = args.shift(); 15 | var cb = args.pop(); 16 | cb(null,{href:href}); 17 | }; 18 | 19 | module.exports = MockSpClient; -------------------------------------------------------------------------------- /test/mocks/sp.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Client: require('./sp-client') 3 | }; --------------------------------------------------------------------------------