├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── README.md ├── lib ├── omniauth-paypal-oauth2.rb └── omniauth │ ├── paypal_oauth2.rb │ ├── paypal_oauth2 │ └── version.rb │ └── strategies │ └── paypal_oauth2.rb ├── omniauth-paypal-oauth2.gemspec └── spec └── spec_helper.rb /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | - package-ecosystem: "bundler" 13 | directory: "/" 14 | schedule: 15 | interval: "daily" 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | ruby: 12 | - '2.5.x' 13 | - '2.6.x' 14 | - '2.7.x' 15 | - '3.0.x' 16 | 17 | name: Ruby ${{ matrix.ruby }} 18 | steps: 19 | - uses: actions/checkout@master 20 | - uses: actions/setup-ruby@v1.1.3 21 | with: 22 | ruby-version: ${{ matrix.ruby }} 23 | - name: Install dependencies 24 | run: | 25 | sudo apt-get install libsqlite3-dev 26 | gem install bundler 27 | bundle install --jobs 4 --retry 3 28 | - name: Run RuboCop 29 | run: bundle exec rubocop 30 | - name: Run RSpec 31 | run: bundle exec rspec 32 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | build: 9 | 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - uses: actions/checkout@master 14 | - uses: actions/setup-ruby@v1.1.3 15 | with: 16 | version: 2.6.x 17 | 18 | - name: Publish to GPR 19 | run: | 20 | mkdir -p $HOME/.gem 21 | touch $HOME/.gem/credentials 22 | chmod 0600 $HOME/.gem/credentials 23 | printf -- "---\n:github: Bearer ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials 24 | gem build *.gemspec 25 | gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem 26 | env: 27 | GEM_HOST_API_KEY: ${{secrets.GPR_AUTH_TOKEN}} 28 | OWNER: jonhue 29 | 30 | - name: Publish to RubyGems 31 | run: | 32 | mkdir -p $HOME/.gem 33 | touch $HOME/.gem/credentials 34 | chmod 0600 $HOME/.gem/credentials 35 | printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials 36 | gem build *.gemspec 37 | gem push *.gem 38 | env: 39 | GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}} 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /*.gem 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | require: rubocop-rspec 2 | 3 | AllCops: 4 | Exclude: 5 | - vendor/**/* 6 | TargetRubyVersion: 2.5 7 | 8 | Gemspec/RequiredRubyVersion: 9 | Enabled: false 10 | 11 | Metrics/BlockLength: 12 | Exclude: 13 | - spec/**/*_spec.rb 14 | 15 | Naming/FileName: 16 | Exclude: 17 | - lib/omniauth-paypal-oauth2.rb 18 | 19 | Style/Documentation: 20 | Enabled: false 21 | 22 | Style/SymbolArray: 23 | EnforcedStyle: brackets 24 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | This file tracks all unreleased breaking changes and deprecations on `master`. You can find a list of all releases [here](https://github.com/jonhue/omniauth-paypal-oauth2/releases). 4 | 5 | OmniAuth PayPal OAuth2 follows Semantic Versioning 2.0 as defined at http://semver.org. 6 | 7 | ### Breaking Changes 8 | 9 | * None 10 | 11 | ### Deprecated 12 | 13 | * None 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at jonas.huebotter@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | omniauth-paypal-oauth2 (2.0.2) 5 | json (>= 1.7, < 3) 6 | omniauth-oauth2 (~> 1.5) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | ast (2.4.1) 12 | diff-lcs (1.4.4) 13 | faraday (1.8.0) 14 | faraday-em_http (~> 1.0) 15 | faraday-em_synchrony (~> 1.0) 16 | faraday-excon (~> 1.1) 17 | faraday-httpclient (~> 1.0.1) 18 | faraday-net_http (~> 1.0) 19 | faraday-net_http_persistent (~> 1.1) 20 | faraday-patron (~> 1.0) 21 | faraday-rack (~> 1.0) 22 | multipart-post (>= 1.2, < 3) 23 | ruby2_keywords (>= 0.0.4) 24 | faraday-em_http (1.0.0) 25 | faraday-em_synchrony (1.0.0) 26 | faraday-excon (1.1.0) 27 | faraday-httpclient (1.0.1) 28 | faraday-net_http (1.0.1) 29 | faraday-net_http_persistent (1.2.0) 30 | faraday-patron (1.0.0) 31 | faraday-rack (1.0.0) 32 | hashie (4.1.0) 33 | json (2.6.1) 34 | jwt (2.3.0) 35 | multi_json (1.15.0) 36 | multi_xml (0.6.0) 37 | multipart-post (2.1.1) 38 | oauth2 (1.4.7) 39 | faraday (>= 0.8, < 2.0) 40 | jwt (>= 1.0, < 3.0) 41 | multi_json (~> 1.3) 42 | multi_xml (~> 0.5) 43 | rack (>= 1.2, < 3) 44 | omniauth (2.0.4) 45 | hashie (>= 3.4.6) 46 | rack (>= 1.6.2, < 3) 47 | rack-protection 48 | omniauth-oauth2 (1.7.2) 49 | oauth2 (~> 1.4) 50 | omniauth (>= 1.9, < 3) 51 | parallel (1.20.1) 52 | parser (2.7.2.0) 53 | ast (~> 2.4.1) 54 | rack (2.2.3) 55 | rack-protection (2.1.0) 56 | rack 57 | rainbow (3.0.0) 58 | regexp_parser (2.0.0) 59 | rexml (3.2.5) 60 | rspec (3.10.0) 61 | rspec-core (~> 3.10.0) 62 | rspec-expectations (~> 3.10.0) 63 | rspec-mocks (~> 3.10.0) 64 | rspec-core (3.10.0) 65 | rspec-support (~> 3.10.0) 66 | rspec-expectations (3.10.0) 67 | diff-lcs (>= 1.2.0, < 2.0) 68 | rspec-support (~> 3.10.0) 69 | rspec-mocks (3.10.0) 70 | diff-lcs (>= 1.2.0, < 2.0) 71 | rspec-support (~> 3.10.0) 72 | rspec-support (3.10.0) 73 | rubocop (0.93.1) 74 | parallel (~> 1.10) 75 | parser (>= 2.7.1.5) 76 | rainbow (>= 2.2.2, < 4.0) 77 | regexp_parser (>= 1.8) 78 | rexml 79 | rubocop-ast (>= 0.6.0) 80 | ruby-progressbar (~> 1.7) 81 | unicode-display_width (>= 1.4.0, < 2.0) 82 | rubocop-ast (1.3.0) 83 | parser (>= 2.7.1.5) 84 | rubocop-rspec (1.44.1) 85 | rubocop (~> 0.87) 86 | rubocop-ast (>= 0.7.1) 87 | ruby-progressbar (1.10.1) 88 | ruby2_keywords (0.0.5) 89 | unicode-display_width (1.7.0) 90 | 91 | PLATFORMS 92 | ruby 93 | 94 | DEPENDENCIES 95 | omniauth-paypal-oauth2! 96 | rspec 97 | rubocop 98 | rubocop-rspec 99 | 100 | BUNDLED WITH 101 | 2.1.2 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jonas Hübotter 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OmniAuth PayPal OAuth2 Strategy 2 | 3 | Strategy to authenticate with PayPal via OmniAuth. 4 | 5 | Get your API key at: https://developer.paypal.com/developer/applications/ in the section **RESTApps**. Note the Client ID and the Client Secret. 6 | 7 | **Note**: You generate separate keys for development (sandbox) and production (live) with each application you register. 8 | Use the [config Gem](https://rubygems.org/gems/config) to organize your keys and keep them safe. 9 | 10 | For more details, read the PayPal docs: https://developer.paypal.com/docs/integration/direct/identity/ 11 | 12 | --- 13 | 14 | ## Table of Contents 15 | 16 | * [Installation](#installation) 17 | * [Usage](#usage) 18 | * [PayPal API Setup](#paypal-api-setup) 19 | * [Rails middleware](#rails-middleware) 20 | * [Devise](#Devise) 21 | * [Configuration](#configuration) 22 | * [Auth hash](#auth-hash) 23 | * [Testing](#testing) 24 | * [Release](#release) 25 | * [To Do](#to-do) 26 | * [Contributing](#contributing) 27 | * [Semantic versioning](#semantic-versioning) 28 | 29 | --- 30 | 31 | ## Installation 32 | 33 | Add to your `Gemfile`: 34 | 35 | ```ruby 36 | gem 'omniauth-paypal-oauth2' 37 | ``` 38 | 39 | And then execute: 40 | 41 | $ bundle 42 | 43 | Or install it yourself as: 44 | 45 | $ gem install omniauth-paypal-oauth2 46 | 47 | If you always want to be up to date fetch the latest from GitHub in your `Gemfile`: 48 | 49 | ```ruby 50 | gem 'omniauth-paypal-oauth2', github: 'jonhue/omniauth-paypal-oauth2' 51 | ``` 52 | 53 | --- 54 | 55 | ## Usage 56 | 57 | ### PayPal API Setup 58 | 59 | * Go to 'https://developer.paypal.com/developer/applications/' 60 | * Select your project. 61 | * Scroll down to 'APP SETTINGS' for each 'SANDBOX' and 'LIVE'. 62 | * Set `/users/auth/paypal_oauth2/callback` as Return URL. 63 | * Make sure "Log In with PayPal" is enabled and Save. 64 | * Go to Credentials, then select the "OAuth consent screen" tab on top, and provide an 'EMAIL ADDRESS' and a 'PRODUCT NAME' 65 | * Wait 10 minutes for changes to take effect. 66 | 67 | ### Rails middleware 68 | 69 | Here's an example for adding the middleware to a Rails app in `config/initializers/omniauth.rb`: 70 | 71 | ```ruby 72 | Rails.application.config.middleware.use OmniAuth::Builder do 73 | provider :paypal_oauth2, ENV['PAYPAL_CLIENT_ID'], ENV['PAYPAL_CLIENT_SECRET'] 74 | end 75 | ``` 76 | 77 | You can now access the OmniAuth PayPal OAuth2 URL: `/auth/paypal_oauth2` 78 | 79 | **Note**: While developing your application, if you change the scope in the initializer you will need to restart your app server. Remember that either the 'email' or 'profile' scope is required! 80 | 81 | ### Devise 82 | 83 | First define your application id and secret in `config/initializers/devise.rb`. Do not use the snippet mentioned in the [Usage](https://github.com/jonhue/omniauth-paypal-oauth2#usage) section. 84 | 85 | ```ruby 86 | require 'omniauth-paypal-oauth2' 87 | config.omniauth :paypal_oauth2, 'PAYPAL_CLIENT_ID', 'PAYPAL_CLIENT_SECRET' 88 | ``` 89 | 90 | Then add the following to 'config/routes.rb' so the callback routes are defined. 91 | 92 | ```ruby 93 | devise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' } 94 | ``` 95 | 96 | Make sure your model is omniauthable. Generally this is `'/app/models/user.rb'` 97 | 98 | ```ruby 99 | devise :omniauthable, omniauth_providers: [:paypal_oauth2] 100 | ``` 101 | 102 | Then make sure your callbacks controller is setup. 103 | 104 | ```ruby 105 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController 106 | def paypal_oauth2 107 | # You need to implement the method below in your model (e.g. app/models/user.rb) 108 | @user = User.from_omniauth(request.env['omniauth.auth']) 109 | 110 | if @user.persisted? 111 | flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: 'PayPal') 112 | sign_in_and_redirect(@user, event: :authentication) 113 | else 114 | session['devise.paypal_data'] = request.env['omniauth.auth'] 115 | redirect_to new_user_registration_url 116 | end 117 | end 118 | end 119 | ``` 120 | 121 | and bind to or create the user 122 | 123 | ```ruby 124 | def self.from_omniauth(access_token) 125 | data = access_token.info 126 | user = User.where(email: data['email']).first 127 | 128 | # Uncomment the section below if you want users to be created if they don't exist 129 | # unless user 130 | # user = User.create(name: data['name'], 131 | # email: data['email'], 132 | # password: Devise.friendly_token[0,20] 133 | # ) 134 | # end 135 | user 136 | end 137 | ``` 138 | 139 | For your views you can login using: 140 | 141 | ```erb 142 | <%= link_to 'Sign in with PayPal', user_paypal_oauth2_omniauth_authorize_path %> 143 | 144 | <%# Devise prior 4.1.0: %> 145 | <%= link_to 'Sign in with PayPal', user_omniauth_authorize_path(:paypal_oauth2) %> 146 | ``` 147 | 148 | An overview is available at https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview 149 | 150 | ### Configuration 151 | 152 | If you click from your [Applications Dashboard](https://developer.paypal.com/developer/applications/) in your Application on "Advanced Options" in the "APP SETTINGS" section and "Log In with PayPal" subsection, you can configure several options: 153 | 154 | * `Basic authentication`: The unique identifier PPID (PayPal ID) is provided. No additional customer information. **Not customizable**. 155 | 156 | * `Personal Information`: 157 | * `Full name`: Permits the Name of the customer. 158 | 159 | * `Address Information`: 160 | * `Email address`: Permits the email address of the customer. 161 | * `Street address`: Permits the street address of the customer (Street name, House number). 162 | * `City`: Permits the city name where the customer resides. 163 | * `State`: Permits the state in which the city is located. 164 | * `Country`: Permits the country in which both state and city are located. 165 | * `Zip code`: Permits the Zip code of the customer. 166 | 167 | * `Account Information`: 168 | * `Account status (verified)`: Permits a boolean which indicates whether the customer is verified by PayPal or not. 169 | 170 | ### Auth Hash 171 | 172 | Here's an example of an authentication hash available in the callback by accessing `request.env['omniauth.auth']`: 173 | 174 | ```ruby 175 | { 176 | provider: 'paypal', 177 | uid: 'bathjJwvdhKjgfgh8Jd745J7dh5Qkgflbnczd65dfnw', 178 | info: { 179 | name: 'John Smith', 180 | email: 'example@example.com', 181 | location: 'Moscow' 182 | }, 183 | credentials: { 184 | token: 'token', 185 | refresh_token: 'refresh_token', 186 | expires_at: 1355082790, 187 | expires: true 188 | }, 189 | extra: { 190 | account_creation_date: '2008-04-21', 191 | account_type: 'PERSONAL', 192 | user_id: 'https://www.paypal.com/webapps/auth/identity/user/bathjJwvdhKjgfgh8Jd745J7dh5Qkgflbnczd65dfnw', 193 | address: { 194 | country: 'US', 195 | locality: 'San Jose', 196 | postal_code: '95131', 197 | region: 'CA', 198 | street_address: '1 Main St' 199 | }, 200 | language: 'en_US', 201 | locale: 'en_US', 202 | verified_account: true, 203 | zoneinfo: 'America/Los_Angeles' 204 | } 205 | } 206 | ``` 207 | 208 | For more details see the PayPal [List Of Attributes](https://developer.paypal.com/docs/api/identity/v1/#userinfo). 209 | 210 | --- 211 | 212 | ## Testing 213 | 214 | 1. Fork this repository 215 | 2. Clone your forked git locally 216 | 3. Install dependencies 217 | 218 | `$ bundle install` 219 | 220 | 4. Run specs 221 | 222 | `$ bundle exec rspec` 223 | 224 | 5. Run RuboCop 225 | 226 | `$ bundle exec rubocop` 227 | 228 | --- 229 | 230 | ## Release 231 | 232 | 1. Review breaking changes and deprecations in `CHANGELOG.md` 233 | 2. Change the gem version in `lib/omniauth/paypal_oauth2/version.rb` 234 | 3. Reset `CHANGELOG.md` 235 | 4. Create a pull request to merge the changes into `master` 236 | 5. After the pull request was merged, create a new release listing the breaking changes and commits on `master` since the last release. 237 | 6. The release workflow will publish the gems to RubyGems and the GitHub Package Registry 238 | 239 | --- 240 | 241 | ## To Do 242 | 243 | We use [GitHub projects](https://github.com/jonhue/omniauth-paypal-oauth2/projects/1) to coordinate the work on this project. 244 | 245 | To propose your ideas, initiate the discussion by adding a [new issue](https://github.com/jonhue/omniauth-paypal-oauth2/issues/new). 246 | 247 | --- 248 | 249 | ## Contributing 250 | 251 | We hope that you will consider contributing to OmniAuth PayPal OAuth2 Strategy. Please read this short overview for some information about how to get started: 252 | 253 | [Learn more about contributing to this repository](https://github.com/jonhue/omniauth-paypal-oauth2/blob/master/CONTRIBUTING.md), [Code of Conduct](https://github.com/jonhue/omniauth-paypal-oauth2/blob/master/CODE_OF_CONDUCT.md) 254 | 255 | ### Semantic Versioning 256 | 257 | omniauth-paypal-oauth2 follows Semantic Versioning 2.0 as defined at http://semver.org. 258 | -------------------------------------------------------------------------------- /lib/omniauth-paypal-oauth2.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'omniauth/paypal_oauth2' 4 | -------------------------------------------------------------------------------- /lib/omniauth/paypal_oauth2.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'omniauth/strategies/paypal_oauth2' 4 | -------------------------------------------------------------------------------- /lib/omniauth/paypal_oauth2/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module OmniAuth 4 | module PaypalOauth2 5 | VERSION = '2.0.2' 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/omniauth/strategies/paypal_oauth2.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'omniauth-oauth2' 4 | 5 | module OmniAuth 6 | module Strategies 7 | class PaypalOauth2 < OmniAuth::Strategies::OAuth2 8 | DEFAULT_SCOPE = 'openid email profile' 9 | DEFAULT_RESPONSE_TYPE = 'code' 10 | SANDBOX_SITE = 'https://api.sandbox.paypal.com' 11 | SANDBOX_AUTHORIZE_URL = 'https://www.sandbox.paypal.com/signin/authorize' 12 | 13 | option :name, 'paypal_oauth2' 14 | 15 | option :client_options, 16 | site: 'https://api.paypal.com', 17 | authorize_url: 'https://www.paypal.com/signin/authorize', 18 | token_url: '/v1/identity/openidconnect/tokenservice', 19 | setup: true 20 | 21 | option :authorize_options, [:scope, :response_type] 22 | option :provider_ignores_state, true 23 | option :sandbox, false 24 | 25 | # https://www.paypal.com/webapps/auth/identity/user/ 26 | # baCNqjGvIxzlbvDCSsfhN3IrQDtQtsVr79AwAjMxekw => 27 | # baCNqjGvIxzlbvDCSsfhN3IrQDtQtsVr79AwAjMxekw 28 | uid { @parsed_uid ||= ((%r{\/([^\/]+)\z}.match raw_info['user_id']) || [])[1] } 29 | 30 | info do 31 | prune!( 32 | 'name' => raw_info['name'], 33 | 'email' => ((raw_info['emails'] || []).detect do |email| 34 | email['primary'] 35 | end || {})['value'], 36 | 'location' => (raw_info['address'] || {})['locality'] 37 | ) 38 | end 39 | 40 | extra do 41 | prune!( 42 | 'account_type' => raw_info['account_type'], 43 | 'user_id' => raw_info['user_id'], 44 | 'address' => raw_info['address'], 45 | 'verified_account' => (raw_info['verified_account'] == 'true'), 46 | 'language' => raw_info['language'], 47 | 'zoneinfo' => raw_info['zoneinfo'], 48 | 'locale' => raw_info['locale'], 49 | 'account_creation_date' => raw_info['account_creation_date'] 50 | ) 51 | end 52 | 53 | def callback_url 54 | full_host + script_name + callback_path 55 | end 56 | 57 | def setup_phase 58 | if options.sandbox 59 | options.client_options[:site] = SANDBOX_SITE 60 | options.client_options[:authorize_url] = SANDBOX_AUTHORIZE_URL 61 | end 62 | super 63 | end 64 | 65 | def raw_info 66 | @raw_info ||= load_identity 67 | end 68 | 69 | def authorize_params 70 | super.tap do |params| 71 | params[:scope] ||= DEFAULT_SCOPE 72 | params[:response_type] ||= DEFAULT_RESPONSE_TYPE 73 | end 74 | end 75 | 76 | private 77 | 78 | def load_identity 79 | access_token.options[:mode] = :header 80 | access_token.options[:param_name] = :access_token 81 | access_token.options[:grant_type] = :authorization_code 82 | access_token.get( 83 | '/v1/identity/oauth2/userinfo', params: { schema: 'paypalv1.1' } 84 | ).parsed || {} 85 | end 86 | 87 | def prune!(hash) 88 | hash.delete_if do |_, value| 89 | prune!(value) if value.is_a?(Hash) 90 | value.nil? || (value.respond_to?(:empty?) && value.empty?) 91 | end 92 | end 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /omniauth-paypal-oauth2.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.expand_path(File.join('..', 'lib', 'omniauth', 'paypal_oauth2', 'version'), __FILE__) 4 | 5 | Gem::Specification.new do |gem| 6 | gem.name = 'omniauth-paypal-oauth2' 7 | gem.version = OmniAuth::PaypalOauth2::VERSION 8 | gem.platform = Gem::Platform::RUBY 9 | gem.summary = 'A PayPal OAuth2 strategy for OmniAuth' 10 | gem.description = 'A PayPal OAuth2 strategy for OmniAuth' 11 | gem.authors = 'Jonas Hübotter' 12 | gem.email = 'jonas.huebotter@gmail.com' 13 | gem.homepage = 'https://github.com/jonhue/omniauth-paypal-oauth2' 14 | gem.license = 'MIT' 15 | 16 | gem.files = Dir['README.md', 'LICENSE', 'lib/**/*'] 17 | gem.require_paths = ['lib'] 18 | 19 | gem.required_ruby_version = '>= 2.5' 20 | 21 | gem.add_dependency 'json', '>= 1.7', '<3' 22 | gem.add_dependency 'omniauth-oauth2', '~> 1.5' 23 | 24 | gem.add_development_dependency 'rspec' 25 | gem.add_development_dependency 'rubocop' 26 | gem.add_development_dependency 'rubocop-rspec' 27 | end 28 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file was generated by the `rspec --init` command. Conventionally, all 4 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 5 | # The generated `.rspec` file contains `--require spec_helper` which will cause 6 | # this file to always be loaded, without a need to explicitly require it in any 7 | # files. 8 | # 9 | # Given that it is always loaded, you are encouraged to keep this file as 10 | # light-weight as possible. Requiring heavyweight dependencies from this file 11 | # will add to the boot time of your test suite on EVERY test run, even for an 12 | # individual file that may not need all of that loaded. Instead, consider making 13 | # a separate helper file that requires the additional dependencies and performs 14 | # the additional setup, and require it from the spec files that actually need 15 | # it. 16 | # 17 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 18 | RSpec.configure do |config| 19 | # rspec-expectations config goes here. You can use an alternate 20 | # assertion/expectation library such as wrong or the stdlib/minitest 21 | # assertions if you prefer. 22 | config.expect_with :rspec do |expectations| 23 | # This option will default to `true` in RSpec 4. It makes the `description` 24 | # and `failure_message` of custom matchers include text for helper methods 25 | # defined using `chain`, e.g.: 26 | # be_bigger_than(2).and_smaller_than(4).description 27 | # # => "be bigger than 2 and smaller than 4" 28 | # ...rather than: 29 | # # => "be bigger than 2" 30 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 31 | end 32 | 33 | # rspec-mocks config goes here. You can use an alternate test double 34 | # library (such as bogus or mocha) by changing the `mock_with` option here. 35 | config.mock_with :rspec do |mocks| 36 | # Prevents you from mocking or stubbing a method that does not exist on 37 | # a real object. This is generally recommended, and will default to 38 | # `true` in RSpec 4. 39 | mocks.verify_partial_doubles = true 40 | end 41 | 42 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 43 | # have no way to turn it off -- the option exists only for backwards 44 | # compatibility in RSpec 3). It causes shared context metadata to be 45 | # inherited by the metadata hash of host groups and examples, rather than 46 | # triggering implicit auto-inclusion in groups with matching metadata. 47 | config.shared_context_metadata_behavior = :apply_to_host_groups 48 | 49 | # The settings below are suggested to provide a good initial experience 50 | # with RSpec, but feel free to customize to your heart's content. 51 | # This allows you to limit a spec run to individual examples or groups 52 | # you care about by tagging them with `:focus` metadata. When nothing 53 | # is tagged with `:focus`, all examples get run. RSpec also provides 54 | # aliases for `it`, `describe`, and `context` that include `:focus` 55 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 56 | # config.filter_run_when_matching :focus 57 | 58 | # Allows RSpec to persist some state between runs in order to support 59 | # the `--only-failures` and `--next-failure` CLI options. We recommend 60 | # you configure your source control system to ignore this file. 61 | # config.example_status_persistence_file_path = "spec/examples.txt" 62 | 63 | # Limits the available syntax to the non-monkey patched syntax that is 64 | # recommended. For more details, see: 65 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 66 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 67 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 68 | # config.disable_monkey_patching! 69 | 70 | # This setting enables warnings. It's recommended, but in some cases may 71 | # be too noisy due to issues in dependencies. 72 | # config.warnings = true 73 | 74 | # Many RSpec users commonly either run the entire suite or an individual 75 | # file, and it's useful to allow more verbose output when running an 76 | # individual spec file. 77 | # if config.files_to_run.one? 78 | # # Use the documentation formatter for detailed output, 79 | # # unless a formatter has already been configured 80 | # # (e.g. via a command-line flag). 81 | # config.default_formatter = "doc" 82 | # end 83 | 84 | # Print the 10 slowest examples and example groups at the 85 | # end of the spec run, to help surface which specs are running 86 | # particularly slow. 87 | # config.profile_examples = 10 88 | 89 | # Run specs in random order to surface order dependencies. If you find an 90 | # order dependency and want to debug it, you can fix the order by providing 91 | # the seed, which is printed after each run. 92 | # --seed 1234 93 | # config.order = :random 94 | 95 | # Seed global randomization in this process using the `--seed` CLI option. 96 | # Setting this allows you to use `--seed` to deterministically reproduce 97 | # test failures related to randomization by passing the same `--seed` value 98 | # as the one that triggered the failure. 99 | # Kernel.srand config.seed 100 | end 101 | --------------------------------------------------------------------------------