OpenAuth2 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +4 -0
- data/License +9 -0
- data/Rakefile +126 -0
- data/Readme.markdown +224 -0
- data/Spec.markdown +0 -0
- data/examples/fb.rb +9 -0
- data/examples/google.rb +53 -0
- data/lib/open_auth2.rb +33 -0
- data/lib/open_auth2/client.rb +182 -0
- data/lib/open_auth2/config.rb +102 -0
- data/lib/open_auth2/connection.rb +38 -0
- data/lib/open_auth2/delegate_to_config.rb +25 -0
- data/lib/open_auth2/provider.rb +21 -0
- data/lib/open_auth2/provider/base.rb +31 -0
- data/lib/open_auth2/provider/default.rb +18 -0
- data/lib/open_auth2/provider/facebook.rb +23 -0
- data/lib/open_auth2/provider/google.rb +28 -0
- data/lib/open_auth2/token.rb +150 -0
- data/lib/open_auth2/version.rb +3 -0
- data/open_auth2.gemspec +74 -0
- data/spec/client_spec.rb +110 -0
- data/spec/config_spec.rb +119 -0
- data/spec/facebook/client_spec.rb +82 -0
- data/spec/facebook/token_spec.rb +92 -0
- data/spec/fixtures/creds.rb +18 -0
- data/spec/fixtures/vcr/fb/access_token.yml +36 -0
- data/spec/fixtures/vcr/fb/cocacola.yml +68 -0
- data/spec/fixtures/vcr/fb/me.yml +36 -0
- data/spec/fixtures/vcr/fb/post.yml +34 -0
- data/spec/fixtures/vcr/fb/refresh_token.yml +36 -0
- data/spec/fixtures/vcr/goog/access_token.yml +38 -0
- data/spec/fixtures/vcr/goog/list.yml +50 -0
- data/spec/fixtures/vcr/goog/post.yml +44 -0
- data/spec/fixtures/vcr/goog/refresh_token.yml +37 -0
- data/spec/google/client_spec.rb +59 -0
- data/spec/google/token_spec.rb +87 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/token_spec.rb +23 -0
- metadata +179 -0
@@ -0,0 +1,182 @@
|
|
1
|
+
module OpenAuth2
|
2
|
+
|
3
|
+
# Used to make GET/POST requests to OAuth server.
|
4
|
+
class Client
|
5
|
+
extend DelegateToConfig
|
6
|
+
include Connection
|
7
|
+
|
8
|
+
# Use it to set @config. Will raise error if no @config or wrong
|
9
|
+
# @config. We rely on Config for all Options info, so its important
|
10
|
+
# it is set right.
|
11
|
+
#
|
12
|
+
# Yields: self.
|
13
|
+
#
|
14
|
+
# Accepts:
|
15
|
+
# config: (optional) OpenAuth2::Config object
|
16
|
+
#
|
17
|
+
# Examples:
|
18
|
+
# config = OpenAuth2::Config.new do |c|
|
19
|
+
# c.provider = :facebook
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # set via block
|
23
|
+
# OpenAuth2::Client.new do |c|
|
24
|
+
# c.config = config
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # or pass it as an argument
|
28
|
+
# OpenAuth2::Client.new(config)
|
29
|
+
#
|
30
|
+
# Returns: self.
|
31
|
+
#
|
32
|
+
def initialize(config=nil)
|
33
|
+
@config = config
|
34
|
+
|
35
|
+
yield self if block_given?
|
36
|
+
raise_config_setter_errors
|
37
|
+
|
38
|
+
# endpoint is where the api requests are made
|
39
|
+
@faraday_url = endpoint
|
40
|
+
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# Yields: self, use it to set/change config after #initialize.
|
45
|
+
# Mainly for setting access_token and refresh_token. Will raise
|
46
|
+
# Config related errors same as #initialize.
|
47
|
+
#
|
48
|
+
# Examples:
|
49
|
+
# client = OpenAuth2::Client.new
|
50
|
+
#
|
51
|
+
# client.configure do |c|
|
52
|
+
# c.access_token = :access_token
|
53
|
+
# c.refresh_token = :refresh_token
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# Returns: self.
|
57
|
+
#
|
58
|
+
def configure
|
59
|
+
yield self
|
60
|
+
raise_config_setter_errors
|
61
|
+
|
62
|
+
self
|
63
|
+
end
|
64
|
+
|
65
|
+
# We use this to get & refresh access/refresh tokens.
|
66
|
+
#
|
67
|
+
# Returns: Token object.
|
68
|
+
#
|
69
|
+
def token
|
70
|
+
@token ||= Token.new(config)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Examples:
|
74
|
+
# client.build_code_url
|
75
|
+
# #=> 'http://...'
|
76
|
+
#
|
77
|
+
# # or
|
78
|
+
# client.build_code_url(:scope => 'publish_stream')
|
79
|
+
#
|
80
|
+
# Accepts:
|
81
|
+
# params: (optional) Hash of additional config to be bundled into
|
82
|
+
# the url.
|
83
|
+
#
|
84
|
+
# Returns: String (url).
|
85
|
+
#
|
86
|
+
def build_code_url(params={})
|
87
|
+
token.build_code_url(params)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Makes GET request to OAuth server via Faraday. If access_token
|
91
|
+
# is available, we pass that along to identify ourselves.
|
92
|
+
#
|
93
|
+
# Accepts:
|
94
|
+
# hash
|
95
|
+
# :path - (required)
|
96
|
+
#
|
97
|
+
# Examples:
|
98
|
+
# client.get(:path => '/cocacola')
|
99
|
+
# client.get(:path => '/cocacola', :limit => 1)
|
100
|
+
#
|
101
|
+
# Returns: Faraday response object.
|
102
|
+
#
|
103
|
+
def get(hash)
|
104
|
+
connection.get do |conn|
|
105
|
+
path = hash.delete(:path)
|
106
|
+
|
107
|
+
if path_prefix
|
108
|
+
path = "#{path_prefix}#{path}"
|
109
|
+
end
|
110
|
+
|
111
|
+
hash.merge!(:access_token => access_token) if access_token
|
112
|
+
|
113
|
+
conn.url(path, hash)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# Makes POST request to OAuth server via Faraday.
|
118
|
+
#
|
119
|
+
# Accepts:
|
120
|
+
# hash
|
121
|
+
# :path - (required)
|
122
|
+
# :content_type - (optional)
|
123
|
+
# :body - (optional)
|
124
|
+
#
|
125
|
+
# Examples:
|
126
|
+
# # using query params (fb uses this)
|
127
|
+
# client.post(:path => "/me/feed?message='From OpenAuth2'")
|
128
|
+
#
|
129
|
+
# # using body (google uses this)
|
130
|
+
# body = JSON.dump(:message => "From OpenAuth2)
|
131
|
+
# client.post(:path => "/me/feed,
|
132
|
+
# :body => body,
|
133
|
+
# :content_type => 'application/json')
|
134
|
+
#
|
135
|
+
# Returns: Faraday response object.
|
136
|
+
#
|
137
|
+
def post(hash)
|
138
|
+
connection.post do |conn|
|
139
|
+
if hash[:content_type]
|
140
|
+
conn.headers["Content-Type"] = hash[:content_type]
|
141
|
+
end
|
142
|
+
|
143
|
+
conn.url(hash[:path], :access_token => access_token)
|
144
|
+
conn.body = hash[:body]
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Makes request to OAuth server via Faraday#run_request. It takes
|
149
|
+
# Hash since I can never remember the order in which to pass the
|
150
|
+
# arguments.
|
151
|
+
#
|
152
|
+
# Accepts:
|
153
|
+
# hash
|
154
|
+
# :verb - (required) GET/POST etc.
|
155
|
+
# :path - (required)
|
156
|
+
# :body - (optional)
|
157
|
+
# :header - (optional)
|
158
|
+
#
|
159
|
+
# Examples:
|
160
|
+
# # public GET request
|
161
|
+
# path = "https://graph.facebook.com/cocacola"
|
162
|
+
# client.run_request(verb: :get, path: path, body: nil, header: nil)
|
163
|
+
#
|
164
|
+
# # private GET request
|
165
|
+
# path = "/me/likes?access_token=..."
|
166
|
+
# client.run_request(verb: :get, path: path, body: nil, header: nil)
|
167
|
+
#
|
168
|
+
# Returns: Faraday response object.
|
169
|
+
#
|
170
|
+
def run_request(hash)
|
171
|
+
connection.run_request(hash[:verb], hash[:path], hash[:body],
|
172
|
+
hash[:header])
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def raise_config_setter_errors
|
178
|
+
raise NoConfigObject unless config
|
179
|
+
raise UnknownConfigObject unless config.is_a?(OpenAuth2::Config)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module OpenAuth2
|
2
|
+
|
3
|
+
# Holds the info required to make a valid request to an OAuth server.
|
4
|
+
class Config
|
5
|
+
attr_accessor *Provider::Base::Keys
|
6
|
+
attr_reader :provider, :provider_const, :provider_string
|
7
|
+
|
8
|
+
# Sets provider to default.
|
9
|
+
#
|
10
|
+
# Yields: self, use it to set config.
|
11
|
+
#
|
12
|
+
# Examples:
|
13
|
+
# OpenAuth2::Config.new do |c|
|
14
|
+
# c.provider = :default
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Returns: self.
|
18
|
+
#
|
19
|
+
def initialize
|
20
|
+
set_default_as_provider
|
21
|
+
|
22
|
+
yield self if block_given?
|
23
|
+
end
|
24
|
+
|
25
|
+
# Yields: self, use it to set/change config after #initialize.
|
26
|
+
#
|
27
|
+
# Examples:
|
28
|
+
# config = OpenAuth2::Config.new
|
29
|
+
#
|
30
|
+
# config.configure do |c|
|
31
|
+
# c.provider = :google
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# Returns: self.
|
35
|
+
#
|
36
|
+
def configure
|
37
|
+
yield self if block_given?
|
38
|
+
end
|
39
|
+
|
40
|
+
# Finds provider's module & copies its Options key/values to here.
|
41
|
+
#
|
42
|
+
# Accepts:
|
43
|
+
# name - String/Symbol/Constant.
|
44
|
+
#
|
45
|
+
def provider=(name)
|
46
|
+
set_provider_vars(name)
|
47
|
+
copy_provider_keys
|
48
|
+
end
|
49
|
+
|
50
|
+
# Removes all overwritten config & resets provider to default.
|
51
|
+
def reset_provider
|
52
|
+
remove_instance_vars
|
53
|
+
set_default_as_provider
|
54
|
+
end
|
55
|
+
|
56
|
+
def parse(response_body)
|
57
|
+
@provider_const.parse(self, response_body)
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def set_default_as_provider
|
63
|
+
self.provider = :default
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_provider_vars(name)
|
67
|
+
@provider = name
|
68
|
+
@provider_string = name.to_s
|
69
|
+
@provider_const = constantize_provider_string
|
70
|
+
end
|
71
|
+
|
72
|
+
def constantize_provider_string
|
73
|
+
provider_string = @provider_string.camelize
|
74
|
+
full_string = "OpenAuth2::Provider::#{provider_string}"
|
75
|
+
|
76
|
+
@provider_const = full_string.constantize
|
77
|
+
rescue NameError => error
|
78
|
+
if error.to_s =~ /uninitialized constant/
|
79
|
+
raise UnknownProvider, "Known Providers: #{known_providers}"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def known_providers
|
84
|
+
known_providers = OpenAuth2::Provider.constants
|
85
|
+
known_providers.delete(:Base)
|
86
|
+
|
87
|
+
known_providers
|
88
|
+
end
|
89
|
+
|
90
|
+
def copy_provider_keys
|
91
|
+
@provider_const::Options.each do |key,value|
|
92
|
+
instance_variable_set("@#{key}", value)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def remove_instance_vars
|
97
|
+
instance_variables.each do |var|
|
98
|
+
remove_instance_variable var
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module OpenAuth2
|
2
|
+
|
3
|
+
# Client/Token use this to make the actual requests to OAuth server.
|
4
|
+
# Since some OAuth servers have seperate endpoints for authorization
|
5
|
+
# & api requests, we use @faraday_url to store that info.
|
6
|
+
#
|
7
|
+
module Connection
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
attr_accessor :faraday_url
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Yields: Faraday object, so user can choose choose their own
|
15
|
+
# middleware.
|
16
|
+
#
|
17
|
+
# Examples:
|
18
|
+
# config = OpenAuth2::Config.new
|
19
|
+
# client = OpenAuth2::Client.new(config)
|
20
|
+
#
|
21
|
+
# client.connection do |c|
|
22
|
+
# c.response :logger
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# Returns: Faraday object.
|
26
|
+
#
|
27
|
+
def connection
|
28
|
+
@connection ||= Faraday.new(:url => @faraday_url) do |builder|
|
29
|
+
builder.request :url_encoded
|
30
|
+
builder.adapter :net_http
|
31
|
+
end
|
32
|
+
|
33
|
+
yield @connection if block_given?
|
34
|
+
|
35
|
+
@connection
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module OpenAuth2
|
2
|
+
|
3
|
+
# Helper module that Client/Token extend so they can have access to
|
4
|
+
# Options methods. We delegate those methods to @config, which should
|
5
|
+
# contain Config object.
|
6
|
+
#
|
7
|
+
module DelegateToConfig
|
8
|
+
def self.extended(base)
|
9
|
+
base.send(:attr_accessor, :config)
|
10
|
+
base.delegate_to_config
|
11
|
+
end
|
12
|
+
|
13
|
+
def delegate_to_config
|
14
|
+
OpenAuth2::Provider::Base::Keys.each do |key|
|
15
|
+
define_method(key) do
|
16
|
+
@config.send(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
define_method("#{key}=") do |value|
|
20
|
+
@config.send("#{key}=", value)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Contains various providers & their config info stored in Options hash.
|
2
|
+
# When user sets a provider, we copy over its Options to Config.
|
3
|
+
#
|
4
|
+
# The reason for this setup is b/c various OAuth servers accept &
|
5
|
+
# return different options & values. This way users can contribute
|
6
|
+
# their own providers, i.e. a very simple plugin system.
|
7
|
+
#
|
8
|
+
# Acceptable providers are modules defined under OpenAuth2::Provider,
|
9
|
+
# have Options hash.
|
10
|
+
#
|
11
|
+
# Examples:
|
12
|
+
# module OpenAuth2::Providers::YourProviderName
|
13
|
+
# Options = {
|
14
|
+
# :authorize_url => 'https://your_provider_name.com'
|
15
|
+
# }
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
module OpenAuth2
|
19
|
+
module Provider
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module OpenAuth2
|
2
|
+
module Provider
|
3
|
+
|
4
|
+
# Contains all possible Options keys. Config allows only the ones
|
5
|
+
# specified here.
|
6
|
+
#
|
7
|
+
module Base
|
8
|
+
Keys = [
|
9
|
+
:client_id,
|
10
|
+
:client_secret,
|
11
|
+
:code,
|
12
|
+
:authorize_url,
|
13
|
+
:redirect_uri,
|
14
|
+
:code_url,
|
15
|
+
:authorize_path,
|
16
|
+
:token_path,
|
17
|
+
:access_token,
|
18
|
+
:refresh_token,
|
19
|
+
:response_type,
|
20
|
+
:access_token_grant_name,
|
21
|
+
:refresh_token_grant_name,
|
22
|
+
:refresh_token_name,
|
23
|
+
:scope,
|
24
|
+
:endpoint,
|
25
|
+
:path_prefix,
|
26
|
+
:token_expires_at,
|
27
|
+
:token_arrived_at,
|
28
|
+
]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module OpenAuth2
|
2
|
+
module Provider
|
3
|
+
|
4
|
+
# Contains the default Options, which is copied to Config on
|
5
|
+
# #initialize. We can then choose another provider or overwrite
|
6
|
+
# them individually.
|
7
|
+
#
|
8
|
+
module Default
|
9
|
+
Options = {
|
10
|
+
:response_type => 'code',
|
11
|
+
:access_token_grant_name => 'authorization_code',
|
12
|
+
:refresh_token_grant_name => 'refresh_token',
|
13
|
+
:refresh_token_name => :refresh_token,
|
14
|
+
:scope => [],
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module OpenAuth2
|
2
|
+
module Provider
|
3
|
+
module Facebook
|
4
|
+
Options = {
|
5
|
+
:authorize_url => 'https://graph.facebook.com',
|
6
|
+
:code_url => 'https://www.facebook.com',
|
7
|
+
:refresh_token_grant_name => 'fb_exchange_token',
|
8
|
+
:refresh_token_name => 'fb_exchange_token',
|
9
|
+
:authorize_path => '/dialog/oauth',
|
10
|
+
:token_path => 'oauth/access_token',
|
11
|
+
:endpoint => 'https://graph.facebook.com'
|
12
|
+
}
|
13
|
+
|
14
|
+
def self.parse(config, response_body)
|
15
|
+
access_token = response_body.gsub('access_token=', '')
|
16
|
+
config.access_token = access_token
|
17
|
+
config.refresh_token = access_token
|
18
|
+
config.token_arrived_at = Time.now
|
19
|
+
config.token_expires_at = "60 days?"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|