Skip to content

Commit

Permalink
Merge pull request #2 from ignat-z/implementation
Browse files Browse the repository at this point in the history
omniauth-ebay-oauth implementation
  • Loading branch information
Envek committed Nov 13, 2017
2 parents 5213dad + 4a6254b commit 4fd58bf
Show file tree
Hide file tree
Showing 23 changed files with 708 additions and 3 deletions.
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/.bundle/
/.yardoc
/Gemfile.lock
/_yardoc/
/coverage/
/doc/
/pkg/
/spec/reports/
/tmp/

# rspec failure tracking
.rspec_status
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--format documentation
--color
10 changes: 10 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
AllCops:
TargetRubyVersion: 2.3

Metrics/BlockLength:
Exclude:
- 'spec/**/*.rb'

Naming/FileName:
Exclude:
- 'lib/*.rb'
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.3.5
6 changes: 6 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
sudo: false
language: ruby
rvm:
- 2.3.5
- 2.4.2
before_install: gem install bundler -v 1.15.4
7 changes: 7 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

source 'https://rubygems.org'

git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

gemspec
53 changes: 50 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,64 @@ OmniAuth Strategy for eBay Apps (for using with eBay REST APIs)

## Installation

TBD
Add to your Gemfile:

`gem 'omniauth-ebay-oauth'`

Then `bundle install`.

## Usage

TBD
```ruby
use OmniAuth::Builder do
provider :ebay, CLIENT_ID, CLIENT_SECRET, callback_url: RU_NAME,
sandbox: false, scope: 'https://api.ebay.com/oauth/api_scope' # redefining additional default options
end
```

Required options:
- __CLIENT_ID__, __CLIENT_SECRET__ - Your application's [OAuth credentials](<https://developer.ebay.com/api-docs/static/oauth-credentials.html>) for the environment you're targeting.
- __callback_url__ - Your application's [RuName](<https://developer.ebay.com/api-docs/static/oauth-runame.html>) for the environment you're targeting.

Additional options:
- __sandbox__ - Are you running your application in [sandbox mode](<https://developer.ebay.com/api-docs/static/sandbox-landing.html>), default __`true`__.
- __scope__ - A list of [OAuth scopes](<https://developer.ebay.com/api-docs/static/oauth-details.html#scopes>) that provide access to the interfaces you call, default: __`[]`__. If you want change scopes you could pass it as string or as array of scopes like so: `['https://api.ebay.com/oauth/api_scope/sell.marketing.readonly', 'https://api.ebay.com/oauth/api_scope/sell.account.readonly']`
- __read_timeout__ - Number of seconds to wait for one block to be read for Auth'n'auth eBay API requests, default is 60.
- \+ all [OmniAuth](<https://github.com/omniauth/omniauth>) supported options, like: `callback_path`, `provider_ignores_state` and so on.

Additional usage information could be found on [OmniAuth README page](<https://github.com/omniauth/omniauth#integrating-omniauth-into-your-application>).

## Minimal working Sinatra application:

```ruby
require 'sinatra'
require 'omniauth-ebay-oauth'

use Rack::Session::Cookie
use OmniAuth::Builder do
provider :ebay, ENV['EBAY_CLIENT_ID'], ENV['EBAY_CLIENT_SECRET'],
callback_url: ENV['EBAY_RU_NAME']
end

get '/' do
redirect '/auth/ebay'
end

get '/auth/ebay/callback' do
"Hello, #{request.env['omniauth.auth'].dig('info', 'name')}"
end
```


## Development

TBD
To pass your code through the all checks you simply need to run:

```
bundle exec rake
```

Please, keep in mind [OmniAuth Strategy Contribution Guide](<https://github.com/omniauth/omniauth/wiki/Strategy-Contribution-Guide>) and [eBay developers program](<https://developer.ebay.com/api-docs/static/oauth-tokens.html>).


## Contributing
Expand Down
12 changes: 12 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

require 'bundler/gem_tasks'
require 'rspec/core/rake_task'
require 'rubocop/rake_task'

RSpec::Core::RakeTask.new(:spec)
RuboCop::RakeTask.new do |task|
task.options = %w[--auto-correct]
end

task default: %i[rubocop spec]
10 changes: 10 additions & 0 deletions lib/omniauth-ebay-oauth.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# frozen_string_literal: true

require 'omniauth-oauth2'

require 'omniauth/ebay-oauth/version'

require 'omniauth/ebay-oauth/errors'
require 'omniauth/ebay-oauth/user_info'
require 'omniauth/ebay-oauth/user_info_request'
require 'omniauth/strategies/ebay'
9 changes: 9 additions & 0 deletions lib/omniauth/ebay-oauth/errors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

module OmniAuth
module EbayOauth
class FailureResponseCode < StandardError; end
class FailureResponseResult < StandardError; end
class UnsupportedSchemaError < StandardError; end
end
end
6 changes: 6 additions & 0 deletions lib/omniauth/ebay-oauth/get_user.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<GetUserRequest xmlns="urn:ebay:apis:eBLBaseComponents">
<ErrorLanguage>en_US</ErrorLanguage>
<WarningLevel>High</WarningLevel>
<DetailLevel>ReturnAll</DetailLevel>
</GetUserRequest>
49 changes: 49 additions & 0 deletions lib/omniauth/ebay-oauth/user_info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# frozen_string_literal: true

module OmniAuth
module EbayOauth
# Maps user information from Auth'n'auth eBay API to OmniAuth Auth Hash
# Schema version 1.0
# https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema
class UserInfo
MAPPING = {
uid: %w[GetUserResponse User EIASToken],
name: %w[GetUserResponse User RegistrationAddress Name],
nickname: %w[GetUserResponse User UserID],
email: %w[GetUserResponse User Email]
}.freeze

def initialize(body)
@body = body
end

def uid
field(:uid, required: true)
end

def info
{
name: field(:name, required: true),
email: field(:email),
nickname: field(:nickname),
first_name: field(:name).split.first,
last_name: field(:name).split.last
}
end

def extra
{ raw_info: @body.dig('GetUserResponse', 'User') }
end

private

def field(name, required: false)
@body.dig(*MAPPING.fetch(name)).tap do |value|
if value.nil? && required
raise UnsupportedSchemaError, "Can't find field #{name}"
end
end
end
end
end
end
67 changes: 67 additions & 0 deletions lib/omniauth/ebay-oauth/user_info_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module OmniAuth
module EbayOauth
# Receives user information from Auth'n'auth eBay API
# https://developer.ebay.com/devzone/xml/docs/reference/ebay/GetUser.html
class UserInfoRequest
STATUS_PATH = %w[GetUserResponse Ack].freeze
SUCCESS_CODE = 'Success'
USER_REQUEST = File.read(
File.join(File.dirname(__FILE__), 'get_user.xml')
)

TOKEN_HEADER = 'X-EBAY-API-IAF-TOKEN'
BASIC_HEADERS = {
'Content-Type' => 'text/xml',
'X-EBAY-API-COMPATIBILITY-LEVEL' => 967,
'X-EBAY-API-SITEID' => 0,
'X-EBAY-API-CALL-NAME' => 'GetUser'
}.freeze

def initialize(access_token, request: USER_REQUEST,
user_info_endpoint:, read_timeout:, **_args)
@access_token = access_token
@url = URI(user_info_endpoint)
@read_timeout = read_timeout
@request = request
end

def call
MultiXml.parse(
http
.request(ebay_request)
.tap(&method(:ensure_success_code))
.read_body
).tap(&method(:ensure_success_result))
end

private

def ensure_success_code(response)
return if (200..299).cover?(response.code.to_i)
raise FailureResponseCode, response
end

def ensure_success_result(body)
return if body.dig(*STATUS_PATH) == SUCCESS_CODE
raise FailureResponseResult, body
end

def http
Net::HTTP.new(@url.host, @url.port).tap do |http|
http.read_timeout = @read_timeout
http.use_ssl = true
end
end

def ebay_request
Net::HTTP::Post.new(@url).tap do |request|
BASIC_HEADERS.merge(TOKEN_HEADER => @access_token)
.each { |header, value| request[header] = value }
request.body = @request
end
end
end
end
end
7 changes: 7 additions & 0 deletions lib/omniauth/ebay-oauth/version.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

module OmniAuth
module EbayOauth
VERSION = '0.1.0'
end
end
66 changes: 66 additions & 0 deletions lib/omniauth/strategies/ebay.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# frozen_string_literal: true

module OmniAuth
module Strategies
# OmniAuth strategy for eBay
class Ebay < OmniAuth::Strategies::OAuth2
option :production_client_options,
user_info_endpoint: 'https://api.ebay.com/ws/api.dll',
token_url: 'https://api.ebay.com/identity/v1/oauth2/token',
authorize_url: 'https://signin.ebay.com/authorize'
option :sandbox_client_options,
user_info_endpoint: 'https://api.sandbox.ebay.com/ws/api.dll',
token_url: 'https://api.sandbox.ebay.com/identity/v1/oauth2/token',
authorize_url: 'https://signin.sandbox.ebay.com/authorize'

option :name, :ebay
option :sandbox, true
option :callback_url

option :authorize_options, %i[scope]
option :client_options, auth_scheme: :basic_auth, read_timeout: 60

uid { user_info.uid }
info { user_info.info }
extra { user_info.extra }
credentials { user_credentials }

def setup_phase
options.scope = preprocessed_scopes
options.client_options.merge!(environment_urls)
super
end

def callback_url
options.callback_url
end

private

def user_credentials
self.class.superclass.credentials_stack(self).first.merge(
'refresh_token_expires_at' =>
access_token['refresh_token_expires_in'].to_i + Time.now.to_i
)
end

def preprocessed_scopes
Array(options.scope).join(' ')
end

def environment_urls
if options.sandbox
options.sandbox_client_options
else
options.production_client_options
end
end

def user_info
@user_info ||=
OmniAuth::EbayOauth::UserInfo.new(OmniAuth::EbayOauth::UserInfoRequest
.new(access_token.token, client.options).call)
end
end
end
end
30 changes: 30 additions & 0 deletions omniauth-ebay-oauth.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# frozen_string_literal: true

lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'omniauth/ebay-oauth/version'

Gem::Specification.new do |spec|
spec.name = 'omniauth-ebay-oauth'
spec.version = OmniAuth::EbayOauth::VERSION
spec.authors = ['Ignat Zakrevsky']
spec.email = ['iezakrevsky@gmail.com']
spec.summary = 'OmniAuth strategy for new eBay OAuth API'
spec.homepage = 'https://github.com/Envek/omniauth-ebay-oauth'
spec.license = 'MIT'

spec.required_ruby_version = '>= 2.3'

spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
spec.require_paths = ['lib']

spec.add_dependency 'omniauth', '~> 1.5'
spec.add_dependency 'omniauth-oauth2', '>= 1.4.0', '< 2.0'

spec.add_development_dependency 'bundler', '~> 1.15'
spec.add_development_dependency 'rake', '>= 10'
spec.add_development_dependency 'rspec', '~> 3.5'
spec.add_development_dependency 'rubocop', '~> 0.42'
spec.add_development_dependency 'simplecov', '~> 0.10'
spec.add_development_dependency 'webmock', '~> 2.1'
end
10 changes: 10 additions & 0 deletions spec/fixtures/request_headers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
Accept: "*/*"
Accept-Encoding: gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Content-Type: text/xml
Host: api.com
User-Agent: Ruby
X-Ebay-Api-Call-Name: GetUser
X-Ebay-Api-Compatibility-Level: '967'
X-Ebay-Api-Iaf-Token: token
X-Ebay-Api-Siteid: '0'
Loading

0 comments on commit 4fd58bf

Please sign in to comment.