├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── lib └── passport-dropbox-oauth2 │ ├── index.js │ └── strategy.js ├── package.json ├── LICENSE └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | node_modules 5 | 6 | # Node.js 7 | npm-debug.log 8 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | README.md 2 | Makefile 3 | doc/ 4 | examples/ 5 | test/ 6 | 7 | node_modules 8 | 9 | # Mac OS X 10 | .DS_Store 11 | 12 | # Node.js 13 | .npmignore 14 | npm-debug.log 15 | 16 | # Git 17 | .git* 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 1.1.0 (2016-03-05) 2 | 3 | Bugfixes: 4 | - N/A 5 | 6 | Features: 7 | - ADD option to access v2 of Dropbox's API 8 | 9 | Documentation: 10 | - ADD details how to use v2 of Dropbox's API 11 | 12 | # 1.0.0 (2015-09-24) 13 | 14 | Features: 15 | - PUSH to v1.0.0 -------------------------------------------------------------------------------- /lib/passport-dropbox-oauth2/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var Strategy = require('./strategy'); 5 | 6 | 7 | /** 8 | * Framework version. 9 | */ 10 | require('pkginfo')(module, 'version'); 11 | 12 | /** 13 | * Expose constructors. 14 | */ 15 | exports.Strategy = Strategy; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport-dropbox-oauth2", 3 | "version": "1.1.0", 4 | "description": "Dropbox OAuth 2.0 authentication strategy for Passport.", 5 | "keywords": [ 6 | "passport", 7 | "dropbox", 8 | "oauth2", 9 | "auth", 10 | "authentication", 11 | "identity" 12 | ], 13 | "repository": { 14 | "type": "git", 15 | "url": "http://github.com/florianheinemann/passport-dropbox-oauth2.git" 16 | }, 17 | "bugs": { 18 | "url": "http://github.com/florianheinemann/passport-dropbox-oauth2/issues" 19 | }, 20 | "author": { 21 | "name": "Florian Heinemann", 22 | "email": "florian.heinemann@gmail.com", 23 | "url": "https://twitter.com/florian__h/" 24 | }, 25 | "licenses": [ 26 | { 27 | "type": "MIT", 28 | "url": "http://www.opensource.org/licenses/MIT" 29 | } 30 | ], 31 | "main": "./lib/passport-dropbox-oauth2", 32 | "dependencies": { 33 | "passport-oauth": "^1.0.0", 34 | "pkginfo": "^0.2.3" 35 | }, 36 | "engines": { 37 | "node": ">= 0.4.0" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2013-2014 Florian Heinemann 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Passport-Dropbox-OAuth2 2 | [Passport](http://passportjs.org/) strategy for authenticating with [Dropbox](https://dropbox.com/) 3 | using the OAuth 2.0 API. 4 | 5 | This module lets you authenticate using Dropbox in your Node.js applications. 6 | By plugging into Passport, Dropbox authentication can be easily and 7 | unobtrusively integrated into any application or framework that supports 8 | [Connect](http://www.senchalabs.org/connect/)-style middleware, including 9 | [Express](http://expressjs.com/). 10 | 11 | ## Install 12 | $ npm install passport-dropbox-oauth2 13 | 14 | ## Usage 15 | ### Configure Strategy 16 | 17 | The Dropbox authentication strategy authenticates users using a Dropbox account 18 | and OAuth 2.0 tokens. The strategy requires a `verify` callback, which accepts 19 | these credentials and calls `done` providing a user, as well as `options` 20 | specifying a API version, client ID, client secret, and callback URL. The library 21 | defaults to version 1 of Dropbox's API. 22 | 23 | passport.use(new DropboxOAuth2Strategy({ 24 | apiVersion: '2', 25 | clientID: DROPBOX_CLIENT_ID, 26 | clientSecret: DROPBOX_CLIENT_SECRET, 27 | callbackURL: "https://www.example.net/auth/dropbox-oauth2/callback" 28 | }, 29 | function(accessToken, refreshToken, profile, done) { 30 | User.findOrCreate({ providerId: profile.id }, function (err, user) { 31 | return done(err, user); 32 | }); 33 | } 34 | )); 35 | 36 | ### Authenticate Requests 37 | Use `passport.authenticate()`, specifying the `'dropbox-oauth2'` strategy, to 38 | authenticate requests. 39 | 40 | For example, as route middleware in an [Express](http://expressjs.com/) 41 | application: 42 | 43 | app.get('/auth/dropbox', 44 | passport.authenticate('dropbox-oauth2')); 45 | 46 | app.get('/auth/dropbox/callback', 47 | passport.authenticate('dropbox-oauth2', { failureRedirect: '/login' }), 48 | function(req, res) { 49 | // Successful authentication, redirect home. 50 | res.redirect('/'); 51 | }); 52 | 53 | ## Examples 54 | Examples not yet provided 55 | 56 | ## Tests 57 | Tests not yet provided 58 | 59 | 60 | ## Prior work 61 | This strategy is based on Jared Hanson's GitHub strategy for passport: [Jared Hanson](http://github.com/jaredhanson) 62 | 63 | ## Credits and License 64 | express-sslify is licensed under the MIT license. If you'd like to be informed about new projects follow [@TheSumOfAll](http://twitter.com/TheSumOfAll/). 65 | 66 | Copyright (c) 2013-2014 Florian Heinemann 67 | -------------------------------------------------------------------------------- /lib/passport-dropbox-oauth2/strategy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var util = require('util') 5 | , OAuth2Strategy = require('passport-oauth').OAuth2Strategy 6 | , InternalOAuthError = require('passport-oauth').InternalOAuthError; 7 | 8 | 9 | /** 10 | * `Strategy` constructor. 11 | * 12 | * The Dropbox authentication strategy authenticates requests by delegating to 13 | * Dropbox using the OAuth 2.0 protocol. 14 | * 15 | * Applications must supply a `verify` callback which accepts an `accessToken`, 16 | * `refreshToken` and service-specific `profile`, and then calls the `done` 17 | * callback supplying a `user`, which should be set to `false` if the 18 | * credentials are not valid. If an exception occurred, `err` should be set. 19 | * 20 | * Options: 21 | * - `apiVersion` (optional) the Dropbox API version to use (either '1' or '2'). Default is '1'. 22 | * - `clientID` your Dropbox application's app key found in the App Console 23 | * - `clientSecret` your Dropbox application's app secret 24 | * - `callbackURL` URL to which Dropbox will redirect the user after granting authorization 25 | * 26 | * Examples: 27 | * 28 | * passport.use(new DropboxStrategy({ 29 | * clientID: 'yourAppKey', 30 | * clientSecret: 'yourAppSecret' 31 | * callbackURL: 'https://www.example.net/auth/dropbox-oauth2/callback', 32 | * }, 33 | * function(accessToken, refreshToken, profile, done) { 34 | * User.findOrCreate(..., function (err, user) { 35 | * done(err, user); 36 | * }); 37 | * } 38 | * )); 39 | * 40 | * @param {Object} options 41 | * @param {Function} verify 42 | * @api public 43 | */ 44 | function Strategy(options, verify) { 45 | var supportedApiVersions = ['1', '2'], 46 | defaultOptionsByApiVersion = { 47 | 1: { 48 | authorizationURL: 'https://www.dropbox.com/1/oauth2/authorize', 49 | tokenURL: 'https://api.dropbox.com/1/oauth2/token', 50 | scopeSeparator: ',', 51 | customHeaders: {} 52 | }, 53 | 2: { 54 | authorizationURL: 'https://www.dropbox.com/oauth2/authorize', 55 | tokenURL: 'https://api.dropbox.com/oauth2/token', 56 | scopeSeparator: ',', 57 | customHeaders: { 58 | 'Content-Type': 'application/json' 59 | } 60 | } 61 | }; 62 | 63 | options = options || {}; 64 | 65 | if (options.apiVersion != null && supportedApiVersions.indexOf(options.apiVersion.toString()) === -1) { 66 | throw new Error('Unsupported Dropbox API version. Supported versions are "1" and "2".'); 67 | } 68 | 69 | this._apiVersion = options.apiVersion || '1'; 70 | 71 | options.authorizationURL = options.authorizationURL || defaultOptionsByApiVersion[this._apiVersion].authorizationURL; 72 | options.tokenURL = options.tokenURL || defaultOptionsByApiVersion[this._apiVersion].tokenURL; 73 | 74 | options.scopeSeparator = options.scopeSeparator || defaultOptionsByApiVersion[this._apiVersion].scopeSeparator; 75 | options.customHeaders = options.customHeaders || defaultOptionsByApiVersion[this._apiVersion].customHeaders; 76 | 77 | OAuth2Strategy.call(this, options, verify); 78 | this.name = 'dropbox-oauth2'; 79 | } 80 | 81 | /** 82 | * Inherit from `OAuth2Strategy`. 83 | */ 84 | util.inherits(Strategy, OAuth2Strategy); 85 | 86 | /** 87 | * Use a different method of the OAuth2Strategy for making an external request to the selected Dropbox API version. 88 | * Currently API v2 supports only POST requests for retrieving the user's profile. 89 | * 90 | * @param {String} accessToken 91 | * @param {Function} callback 92 | * @private 93 | */ 94 | Strategy.prototype._retrieveUserProfile = function(accessToken, callback) { 95 | if (this._apiVersion === '1') { 96 | this._oauth2.get('https://api.dropbox.com/1/account/info', accessToken, callback); 97 | } 98 | else if (this._apiVersion === '2') { 99 | // we have to provide the string 'null' as the JSON body otherwise Dropbox will complain about it not being valid. 100 | this._oauth2._request('POST', 'https://api.dropboxapi.com/2/users/get_current_account', 101 | {'Authorization': this._oauth2.buildAuthHeader(accessToken) }, 'null', accessToken, callback); 102 | } 103 | }; 104 | 105 | /** 106 | * Retrieve user profile from Dropbox. 107 | * 108 | * This function constructs a normalized profile, with the following properties: 109 | * 110 | * - `provider` always set to `dropbox` 111 | * - `id` the user's unique Dropbox ID 112 | * - `displayName` a name that can be used directly to represent the name of a user's Dropbox account 113 | * - `emails` the user's email address 114 | * 115 | * @param {String} accessToken 116 | * @param {Function} done 117 | * @api protected 118 | */ 119 | Strategy.prototype.userProfile = function(accessToken, done) { 120 | this._retrieveUserProfile(accessToken, function (err, body, res) { 121 | if (err) { return done(new InternalOAuthError('failed to fetch user profile', err)); } 122 | 123 | try { 124 | var json = JSON.parse(body); 125 | 126 | var profile = { provider: 'dropbox' }; 127 | 128 | if (this._apiVersion === '1') { 129 | profile.id = json.uid; 130 | profile.displayName = json.display_name; 131 | profile.name = { 132 | familyName: json.name_details.surname, 133 | givenName: json.name_details.given_name, 134 | middleName: '' 135 | }; 136 | profile.emails = [{ value: json.email }]; 137 | } 138 | else if (this._apiVersion === '2') { 139 | profile.id = json.account_id; 140 | profile.displayName = json.name.display_name; 141 | profile.name = { 142 | familyName: json.name.surname, 143 | givenName: json.name.given_name, 144 | middleName: '' 145 | }; 146 | profile.emails = [{ value: json.email }]; 147 | } 148 | 149 | profile._raw = body; 150 | profile._json = json; 151 | 152 | done(null, profile); 153 | } catch(e) { 154 | done(e); 155 | } 156 | }.bind(this)); 157 | }; 158 | 159 | 160 | /** 161 | * Expose `Strategy`. 162 | */ 163 | module.exports = Strategy; 164 | --------------------------------------------------------------------------------