├── examples └── login │ ├── views │ ├── login.ejs │ ├── account.ejs │ ├── index.ejs │ └── layout.ejs │ ├── package.json │ └── app.js ├── .travis.yml ├── .gitignore ├── .npmignore ├── lib └── passport-paypal-oauth │ ├── index.js │ └── strategy.js ├── test ├── index-test.js └── strategy-test.js ├── Makefile ├── package.json ├── LICENSE └── README.md /examples/login/views/login.ejs: -------------------------------------------------------------------------------- 1 | Login with PayPal 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: "node_js" 2 | node_js: 3 | - 0.4 4 | - 0.6 5 | - 0.8 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac OS X 2 | .DS_Store 3 | 4 | # Node.js 5 | node_modules/* 6 | npm-debug.log 7 | -------------------------------------------------------------------------------- /examples/login/views/account.ejs: -------------------------------------------------------------------------------- 1 |

ID: <%= user.id %>

2 |

Name: <%= user.displayName %>

3 | -------------------------------------------------------------------------------- /examples/login/views/index.ejs: -------------------------------------------------------------------------------- 1 | <% if (!user) { %> 2 |

Welcome! Please log in.

3 | <% } else { %> 4 |

Hello, <%= user.displayName %>.

5 | <% } %> 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | README.md 2 | Makefile 3 | doc/ 4 | examples/ 5 | test/ 6 | 7 | # Mac OS X 8 | .DS_Store 9 | 10 | # Node.js 11 | .npmignore 12 | node_modules/ 13 | npm-debug.log 14 | 15 | # Git 16 | .git* 17 | -------------------------------------------------------------------------------- /examples/login/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport-paypal-oauth-examples-login", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "express": ">= 0.0.0", 6 | "ejs": ">= 0.0.0", 7 | "passport": ">= 0.0.0", 8 | "passport-paypal-oauth": ">= 0.0.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/passport-paypal-oauth/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 | -------------------------------------------------------------------------------- /test/index-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'); 2 | var assert = require('assert'); 3 | var util = require('util'); 4 | var paypal = require('passport-paypal-oauth'); 5 | 6 | 7 | vows.describe('passport-paypal-oauth').addBatch({ 8 | 9 | 'module': { 10 | 'should report a version': function (x) { 11 | assert.isString(paypal.version); 12 | }, 13 | }, 14 | 15 | }).export(module); 16 | -------------------------------------------------------------------------------- /examples/login/views/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Passport-PayPal (OAuth) Example 5 | 6 | 7 | <% if (!user) { %> 8 |

9 | Home | 10 | Log In 11 |

12 | <% } else { %> 13 |

14 | Home | 15 | Account | 16 | Log Out 17 |

18 | <% } %> 19 | <%- body %> 20 | 21 | 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SOURCES = lib/**/*.js 2 | 3 | # ============================================================================== 4 | # Node Tests 5 | # ============================================================================== 6 | 7 | VOWS = ./node_modules/.bin/vows 8 | TESTS ?= test/*-test.js 9 | 10 | test: 11 | @NODE_ENV=test NODE_PATH=lib $(VOWS) $(TESTS) 12 | 13 | # ============================================================================== 14 | # Static Analysis 15 | # ============================================================================== 16 | 17 | JSHINT = jshint 18 | 19 | hint: lint 20 | lint: 21 | $(JSHINT) $(SOURCES) 22 | 23 | 24 | .PHONY: test hint lint 25 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport-paypal-oauth", 3 | "version": "0.1.0", 4 | "description": "PayPal (OAuth) authentication strategy for Passport.", 5 | "keywords": ["passport", "paypal", "auth", "authn", "authentication", "identity"], 6 | "repository": { 7 | "type": "git", 8 | "url": "git://github.com/jaredhanson/passport-paypal-oauth.git" 9 | }, 10 | "bugs": { 11 | "url": "http://github.com/jaredhanson/passport-paypal-oauth/issues" 12 | }, 13 | "author": { 14 | "name": "Jared Hanson", 15 | "email": "jaredhanson@gmail.com", 16 | "url": "http://www.jaredhanson.net/" 17 | }, 18 | "licenses": [ { 19 | "type": "MIT", 20 | "url": "http://www.opensource.org/licenses/MIT" 21 | } ], 22 | "main": "./lib/passport-paypal-oauth", 23 | "dependencies": { 24 | "pkginfo": "0.2.x", 25 | "passport-oauth": "~0.1.2" 26 | }, 27 | "devDependencies": { 28 | "vows": "0.6.x" 29 | }, 30 | "scripts": { 31 | "test": "NODE_PATH=lib node_modules/.bin/vows test/*-test.js" 32 | }, 33 | "engines": { "node": ">= 0.4.0" } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2012-2013 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Passport-PayPal-OAuth 2 | 3 | [Passport](http://passportjs.org/) strategy for authenticating with [PayPal](http://www.paypal.com/) 4 | using the OAuth 2.0 API. 5 | 6 | This module lets you authenticate using PayPal in your Node.js applications. 7 | By plugging into Passport, PayPal authentication can be easily and 8 | unobtrusively integrated into any application or framework that supports 9 | [Connect](http://www.senchalabs.org/connect/)-style middleware, including 10 | [Express](http://expressjs.com/). 11 | 12 | ## Install 13 | 14 | $ npm install passport-paypal-oauth 15 | 16 | ## Usage 17 | 18 | #### Configure Strategy 19 | 20 | The PayPal authentication strategy authenticates users using a PayPal 21 | account and OAuth 2.0 tokens. The strategy requires a `verify` callback, which 22 | accepts these credentials and calls `done` providing a user, as well as 23 | `options` specifying a app ID, app secret, and callback URL. 24 | 25 | passport.use(new PayPalStrategy({ 26 | clientID: PAYPAL_APP_ID, 27 | clientSecret: PAYPAL_APP_SECRET, 28 | callbackURL: "http://localhost:3000/auth/paypal/callback" 29 | }, 30 | function(accessToken, refreshToken, profile, done) { 31 | User.findOrCreate({ paypalId: profile.id }, function (err, user) { 32 | return done(err, user); 33 | }); 34 | } 35 | )); 36 | 37 | #### Authenticate Requests 38 | 39 | Use `passport.authenticate()`, specifying the `'paypal'` strategy, to 40 | authenticate requests. 41 | 42 | For example, as route middleware in an [Express](http://expressjs.com/) 43 | application: 44 | 45 | app.get('/auth/paypal', 46 | passport.authenticate('paypal')); 47 | 48 | app.get('/auth/paypal/callback', 49 | passport.authenticate('paypal', { failureRedirect: '/login' }), 50 | function(req, res) { 51 | // Successful authentication, redirect home. 52 | res.redirect('/'); 53 | }); 54 | 55 | ## Examples 56 | 57 | For a complete, working example, refer to the [login example](https://github.com/jaredhanson/passport-paypal-oauth/tree/master/examples/login). 58 | 59 | ## Tests 60 | 61 | $ npm install --dev 62 | $ make test 63 | 64 | [![Build Status](https://secure.travis-ci.org/jaredhanson/passport-paypal-oauth.png)](http://travis-ci.org/jaredhanson/passport-paypal-oauth) 65 | 66 | ## Credits 67 | 68 | - [Jared Hanson](http://github.com/jaredhanson) 69 | 70 | ## License 71 | 72 | [The MIT License](http://opensource.org/licenses/MIT) 73 | 74 | Copyright (c) 2012-2013 Jared Hanson <[http://jaredhanson.net/](http://jaredhanson.net/)> 75 | 76 | Sponsor 77 | -------------------------------------------------------------------------------- /lib/passport-paypal-oauth/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 PayPal authentication strategy authenticates requests by delegating to 13 | * PayPal 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 occured, `err` should be set. 19 | * 20 | * Options: 21 | * - `clientID` your application's App ID 22 | * - `clientSecret` your application's App Secret 23 | * - `callbackURL` URL to which PayPal will redirect the user after granting authorization 24 | * 25 | * Examples: 26 | * 27 | * passport.use(new PayPalStrategy({ 28 | * clientID: '123-456-789', 29 | * clientSecret: 'shhh-its-a-secret' 30 | * callbackURL: 'https://www.example.net/auth/paypal/callback' 31 | * }, 32 | * function(accessToken, refreshToken, profile, done) { 33 | * User.findOrCreate(..., function (err, user) { 34 | * done(err, user); 35 | * }); 36 | * } 37 | * )); 38 | * 39 | * @param {Object} options 40 | * @param {Function} verify 41 | * @api public 42 | */ 43 | function Strategy(options, verify) { 44 | options = options || {}; 45 | options.authorizationURL = options.authorizationURL || 'https://identity.x.com/xidentity/resources/authorize'; 46 | options.tokenURL = options.tokenURL || 'https://identity.x.com/xidentity/oauthtokenservice'; 47 | 48 | OAuth2Strategy.call(this, options, verify); 49 | this.name = 'paypal'; 50 | 51 | this._oauth2.setAccessTokenName("oauth_token"); 52 | } 53 | 54 | /** 55 | * Inherit from `OAuth2Strategy`. 56 | */ 57 | util.inherits(Strategy, OAuth2Strategy); 58 | 59 | 60 | /** 61 | * Retrieve user profile from PayPal. 62 | * 63 | * This function constructs a normalized profile, with the following properties: 64 | * 65 | * - `provider` always set to `paypal` 66 | * - `id` 67 | * - `displayName` 68 | * 69 | * @param {String} accessToken 70 | * @param {Function} done 71 | * @api protected 72 | */ 73 | Strategy.prototype.userProfile = function(accessToken, done) { 74 | this._oauth2.getProtectedResource('https://identity.x.com/xidentity/resources/profile/me', accessToken, function (err, body, res) { 75 | if (err) { return done(new InternalOAuthError('failed to fetch user profile', err)); } 76 | 77 | try { 78 | var json = JSON.parse(body); 79 | 80 | var profile = { provider: 'paypal' }; 81 | profile.id = json.identity.userId; 82 | profile.displayName = json.identity.firstName + " " + json.identity.lastName; 83 | profile.name = { familyName: json.identity.lastName, 84 | givenName: json.identity.firstName, 85 | formatted: json.identity.fullName }; 86 | profile.emails = []; 87 | json.identity.emails.forEach(function(email) { 88 | profile.emails.push({ value: email }); 89 | }); 90 | 91 | profile._raw = body; 92 | profile._json = json; 93 | 94 | done(null, profile); 95 | } catch(e) { 96 | done(e); 97 | } 98 | }); 99 | } 100 | 101 | 102 | /** 103 | * Expose `Strategy`. 104 | */ 105 | module.exports = Strategy; 106 | -------------------------------------------------------------------------------- /test/strategy-test.js: -------------------------------------------------------------------------------- 1 | var vows = require('vows'); 2 | var assert = require('assert'); 3 | var util = require('util'); 4 | var PayPalStrategy = require('passport-paypal-oauth/strategy'); 5 | 6 | 7 | vows.describe('PayPalStrategy').addBatch({ 8 | 9 | 'strategy': { 10 | topic: function() { 11 | return new PayPalStrategy({ 12 | clientID: 'ABC123', 13 | clientSecret: 'secret' 14 | }, 15 | function() {}); 16 | }, 17 | 18 | 'should be named paypal': function (strategy) { 19 | assert.equal(strategy.name, 'paypal'); 20 | }, 21 | }, 22 | 23 | 'strategy when loading user profile': { 24 | topic: function() { 25 | var strategy = new PayPalStrategy({ 26 | clientID: 'ABC123', 27 | clientSecret: 'secret' 28 | }, 29 | function() {}); 30 | 31 | // mock 32 | strategy._oauth2.getProtectedResource = function(url, accessToken, callback) { 33 | var body = '{ \ 34 | "status": "SUCCESS", \ 35 | "identity": { \ 36 | "status": "ACTIVE", \ 37 | "fullName": "Jared Hanson", \ 38 | "userId": "123456789", \ 39 | "firstName": "Jared", \ 40 | "lastName": "Hanson", \ 41 | "emails": ["jaredhanson@example.com"] \ 42 | } \ 43 | }'; 44 | 45 | callback(null, body, undefined); 46 | } 47 | 48 | return strategy; 49 | }, 50 | 51 | 'when told to load user profile': { 52 | topic: function(strategy) { 53 | var self = this; 54 | function done(err, profile) { 55 | self.callback(err, profile); 56 | } 57 | 58 | process.nextTick(function () { 59 | strategy.userProfile('access-token', done); 60 | }); 61 | }, 62 | 63 | 'should not error' : function(err, req) { 64 | assert.isNull(err); 65 | }, 66 | 'should load profile' : function(err, profile) { 67 | assert.equal(profile.provider, 'paypal'); 68 | assert.equal(profile.id, '123456789'); 69 | assert.equal(profile.displayName, 'Jared Hanson'); 70 | assert.equal(profile.name.familyName, 'Hanson'); 71 | assert.equal(profile.name.givenName, 'Jared'); 72 | assert.lengthOf(profile.emails, 1); 73 | assert.equal(profile.emails[0].value, 'jaredhanson@example.com'); 74 | }, 75 | 'should set raw property' : function(err, profile) { 76 | assert.isString(profile._raw); 77 | }, 78 | 'should set json property' : function(err, profile) { 79 | assert.isObject(profile._json); 80 | }, 81 | }, 82 | }, 83 | 84 | 'strategy when loading user profile and encountering an error': { 85 | topic: function() { 86 | var strategy = new PayPalStrategy({ 87 | clientID: 'ABC123', 88 | clientSecret: 'secret' 89 | }, 90 | function() {}); 91 | 92 | // mock 93 | strategy._oauth2.getProtectedResource = function(url, accessToken, callback) { 94 | callback(new Error('something-went-wrong')); 95 | } 96 | 97 | return strategy; 98 | }, 99 | 100 | 'when told to load user profile': { 101 | topic: function(strategy) { 102 | var self = this; 103 | function done(err, profile) { 104 | self.callback(err, profile); 105 | } 106 | 107 | process.nextTick(function () { 108 | strategy.userProfile('access-token', done); 109 | }); 110 | }, 111 | 112 | 'should error' : function(err, req) { 113 | assert.isNotNull(err); 114 | }, 115 | 'should wrap error in InternalOAuthError' : function(err, req) { 116 | assert.equal(err.constructor.name, 'InternalOAuthError'); 117 | }, 118 | 'should not load profile' : function(err, profile) { 119 | assert.isUndefined(profile); 120 | }, 121 | }, 122 | }, 123 | 124 | }).export(module); 125 | -------------------------------------------------------------------------------- /examples/login/app.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | , passport = require('passport') 3 | , util = require('util') 4 | , PayPalStrategy = require('passport-paypal-oauth').Strategy; 5 | 6 | var PAYPAL_APP_ID = "--insert-paypal-app-id-here--" 7 | var PAYPAL_APP_SECRET = "--insert-paypal-app-secret-here--"; 8 | 9 | 10 | // Passport session setup. 11 | // To support persistent login sessions, Passport needs to be able to 12 | // serialize users into and deserialize users out of the session. Typically, 13 | // this will be as simple as storing the user ID when serializing, and finding 14 | // the user by ID when deserializing. However, since this example does not 15 | // have a database of user records, the complete PayPal profile is serialized 16 | // and deserialized. 17 | passport.serializeUser(function(user, done) { 18 | done(null, user); 19 | }); 20 | 21 | passport.deserializeUser(function(obj, done) { 22 | done(null, obj); 23 | }); 24 | 25 | 26 | // Use the PayPalStrategy within Passport. 27 | // Strategies in Passport require a `verify` function, which accept 28 | // credentials (in this case, an accessToken, refreshToken, and PayPal 29 | // profile), and invoke a callback with a user object. 30 | passport.use(new PayPalStrategy({ 31 | clientID: PAYPAL_APP_ID, 32 | clientSecret: PAYPAL_APP_SECRET, 33 | callbackURL: "https://example.com:3000/auth/paypal/callback" 34 | }, 35 | function(accessToken, refreshToken, profile, done) { 36 | // asynchronous verification, for effect... 37 | process.nextTick(function () { 38 | 39 | // To keep the example simple, the user's PayPal profile is returned to 40 | // represent the logged-in user. In a typical application, you would want 41 | // to associate the PayPal account with a user record in your database, 42 | // and return that user instead. 43 | return done(null, profile); 44 | }); 45 | } 46 | )); 47 | 48 | 49 | 50 | 51 | var app = express.createServer(); 52 | 53 | // configure Express 54 | app.configure(function() { 55 | app.set('views', __dirname + '/views'); 56 | app.set('view engine', 'ejs'); 57 | app.use(express.logger()); 58 | app.use(express.cookieParser()); 59 | app.use(express.bodyParser()); 60 | app.use(express.methodOverride()); 61 | app.use(express.session({ secret: 'keyboard cat' })); 62 | // Initialize Passport! Also use passport.session() middleware, to support 63 | // persistent login sessions (recommended). 64 | app.use(passport.initialize()); 65 | app.use(passport.session()); 66 | app.use(app.router); 67 | app.use(express.static(__dirname + '/public')); 68 | }); 69 | 70 | 71 | app.get('/', function(req, res){ 72 | res.render('index', { user: req.user }); 73 | }); 74 | 75 | app.get('/account', ensureAuthenticated, function(req, res){ 76 | res.render('account', { user: req.user }); 77 | }); 78 | 79 | app.get('/login', function(req, res){ 80 | res.render('login', { user: req.user }); 81 | }); 82 | 83 | // GET /auth/paypal 84 | // Use passport.authenticate() as route middleware to authenticate the 85 | // request. The first step in PayPal authentication will involve 86 | // redirecting the user to paypal.com. After authorization, PayPal will 87 | // redirect the user back to this application at /auth/paypal/callback 88 | app.get('/auth/paypal', 89 | passport.authenticate('paypal', { scope: 'https://identity.x.com/xidentity/resources/profile/me' }), 90 | function(req, res){ 91 | // The request will be redirected to PayPal for authentication, so this 92 | // function will not be called. 93 | }); 94 | 95 | // GET /auth/paypal/callback 96 | // Use passport.authenticate() as route middleware to authenticate the 97 | // request. If authentication fails, the user will be redirected back to the 98 | // login page. Otherwise, the primary route function function will be called, 99 | // which, in this example, will redirect the user to the home page. 100 | app.get('/auth/paypal/callback', 101 | passport.authenticate('paypal', { failureRedirect: '/login' }), 102 | function(req, res) { 103 | res.redirect('/'); 104 | }); 105 | 106 | app.get('/logout', function(req, res){ 107 | req.logout(); 108 | res.redirect('/'); 109 | }); 110 | 111 | app.listen(3000); 112 | 113 | 114 | // Simple route middleware to ensure user is authenticated. 115 | // Use this route middleware on any resource that needs to be protected. If 116 | // the request is authenticated (typically via a persistent login session), 117 | // the request will proceed. Otherwise, the user will be redirected to the 118 | // login page. 119 | function ensureAuthenticated(req, res, next) { 120 | if (req.isAuthenticated()) { return next(); } 121 | res.redirect('/login') 122 | } 123 | --------------------------------------------------------------------------------