├── 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 | [](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 |
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 |
--------------------------------------------------------------------------------