oauth2-client 1.0.0 → 1.1.0

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.
@@ -32,6 +32,18 @@ module OAuth2
32
32
  @connection_options = options
33
33
  end
34
34
 
35
+ # Token scope definitions vary by service e.g Google uses space delimited scopes,
36
+ # while Github uses comma seperated scopes. This method allows us to normalize
37
+ # the token scopes
38
+ #
39
+ # @see http://developer.github.com/v3/oauth/#scopes, https://developers.google.com/accounts/docs/OAuth2WebServer#formingtheurl
40
+ def normalize_scope(scope, sep=' ')
41
+ unless (scope.is_a?(String) || scope.is_a?(Array))
42
+ raise ArgumentError.new("Expected scope of type String or Array but was: #{scope.class.name}")
43
+ end
44
+ (scope.is_a?(Array) && scope.join(sep)) || scope
45
+ end
46
+
35
47
  def implicit
36
48
  OAuth2::Grant::Implicit.new(self)
37
49
  end
@@ -100,25 +100,21 @@ module OAuth2
100
100
  headers = @headers.merge(opts.fetch(:headers, {}))
101
101
  params = opts[:params] || {}
102
102
  query = Addressable::URI.form_encode(params)
103
- method = method.to_s.downcase
103
+ method = method.to_sym
104
104
  normalized_path = query.empty? ? path : [path, query].join("?")
105
105
  client = http_connection(opts.fetch(:connection_options, {}))
106
106
 
107
- if (method == 'post' || method == 'put')
107
+ if (method == :post || method == :put)
108
108
  headers['Content-Type'] ||= 'application/x-www-form-urlencoded'
109
109
  end
110
110
 
111
111
  case method
112
- when 'get'
113
- response = client.get(normalized_path, headers)
114
- when 'post'
115
- response = client.post(path, query, headers)
116
- when 'put'
117
- response = client.put(path, query, headers)
118
- when 'delete'
119
- response = client.delete(normalized_path, headers)
112
+ when :get, :delete
113
+ response = client.send(method, normalized_path, headers)
114
+ when :post, :put
115
+ response = client.send(method, path, query, headers)
120
116
  else
121
- raise UnhandledHTTPMethodError.new("Unsupported HTTP method, #{method.inspect}")
117
+ raise UnhandledHTTPMethodError.new("Unsupported HTTP method, #{method}")
122
118
  end
123
119
 
124
120
  status = response.code.to_i
@@ -129,7 +125,7 @@ module OAuth2
129
125
  if status == 303
130
126
  method = :get
131
127
  params = nil
132
- headers.delete('Content-Type')
128
+ headers.delete('Content-Type')
133
129
  end
134
130
  redirect_uri = Addressable::URI.parse(response.header['Location'])
135
131
  conn = {
@@ -37,9 +37,7 @@ module OAuth2
37
37
  #
38
38
  # @param [Hash] opts options
39
39
  def fetch_authorization_url(opts={})
40
- opts[:method] ||= :get
41
- opts[:params] ||= {}
42
- opts[:params].merge!(authorization_params)
40
+ opts[:params] = opts.fetch(:params, {}).merge(authorization_params)
43
41
  method = opts.delete(:method) || :get
44
42
  make_request(method, @authorize_path, opts)
45
43
  end
@@ -50,15 +48,16 @@ module OAuth2
50
48
  # @param [Hash] params additional params
51
49
  # @param [Hash] opts options
52
50
  def get_token(code, opts={})
53
- opts[:params] ||= {}
54
- opts[:params][:code] = code
51
+ opts[:params] = {
52
+ :grant_type => grant_type,
53
+ :code => code
54
+ }.merge(opts.fetch(:params, {}))
55
+
55
56
  opts[:authenticate] ||= :headers
56
57
  method = opts.delete(:method) || :post
57
58
  make_request(method, token_path, opts)
58
59
  end
59
-
60
- private
61
-
60
+
62
61
  # Default authorization request parameters
63
62
  def authorization_params
64
63
  {
@@ -1,7 +1,7 @@
1
1
  module OAuth2
2
2
  class Version
3
3
  MAJOR = 1 unless defined? OAuth2::Version::MAJOR
4
- MINOR = 0 unless defined? OAuth2::Version::MINOR
4
+ MINOR = 1 unless defined? OAuth2::Version::MINOR
5
5
  PATCH = 0 unless defined? OAuth2::Version::PATCH
6
6
 
7
7
  def self.to_s
@@ -1,13 +1,23 @@
1
- Gem::Specification.new do |s|
2
- s.name = 'oauth2-client'
3
- s.version = '1.0.0'
4
- s.date = %q{2013-01-22}
5
- s.summary = "OAuth2 Ruby Client"
6
- s.description = "Create quick and dirty OAuth2 clients for many services. Google OAuth2 client included"
7
- s.authors = ["Kevin Mutyaba"]
8
- s.email = %q{tiabasnk@gmail.com}
9
- s.homepage = ''
10
- s.rubygems_version = %q{1.0.0}
11
- s.files = `git ls-files`.split("\n")
12
- s.require_paths = ['lib']
13
- end
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'oauth2/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.add_dependency 'json'
7
+ spec.add_dependency 'bcrypt-ruby', '~> 3.0.0'
8
+ spec.add_dependency 'addressable'
9
+ spec.add_development_dependency 'bundler', '~> 1.0'
10
+
11
+ spec.name = 'oauth2-client'
12
+ spec.version = OAuth2::Version
13
+ spec.date = %q{2013-03-03}
14
+ spec.summary = "OAuth2 client wrapper in Ruby"
15
+ spec.description = "Create quick and dirty OAuth2 clients"
16
+ spec.authors = ["Kevin Mutyaba"]
17
+ spec.email = %q{tiabasnk@gmail.com}
18
+ spec.homepage = 'http://tiabas.github.com/oauth2-client/'
19
+ spec.files = `git ls-files`.split("\n")
20
+ spec.require_paths = ['lib']
21
+ spec.licenses = ['MIT']
22
+ spec.required_rubygems_version = '>= 1.3.6'
23
+ end
@@ -0,0 +1,59 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'github_client'
3
+
4
+ describe GithubClient do
5
+
6
+ subject do
7
+ GithubClient.new('https://github.com', '2945e6425da3d5d17ffc', '0a8f686f2835a70a79dbcece2ec63bc5079f40a8')
8
+
9
+ # GithubClient.new('https://github.com', '82f971d013e8d637a7e1', '1a1d59e1f8b8afa5f73e9dc9f17e25f7876e64ac')
10
+ end
11
+
12
+ describe "#webserver_authorization_url" do
13
+ it "returns the authorization url" do
14
+ auth_url = subject.webserver_authorization_url(
15
+ :scope => 'repo, user',
16
+ :state => '1kd84ur7q0c9rbtnd',
17
+ :redirect_uri => 'https://localhost/callback'
18
+ )
19
+
20
+ parsed_url = Addressable::URI.parse(auth_url)
21
+ expect(parsed_url.path).to eq '/login/oauth/authorize'
22
+ expect(parsed_url.query_values).to eq({
23
+ "client_id" => '2945e6425da3d5d17ffc',
24
+ "redirect_uri" => 'https://localhost/callback',
25
+ "response_type" => 'code',
26
+ "scope" => 'repo, user',
27
+ "state" => '1kd84ur7q0c9rbtnd'
28
+ })
29
+ expect(parsed_url.scheme).to eq 'https'
30
+ expect(parsed_url.host).to eq 'github.com'
31
+ end
32
+ end
33
+
34
+ describe "#exchange_auth_code_for_token" do
35
+ it "makes a request to google oauth2 server" do
36
+
37
+ stub_request(:post, "https://github.com/login/oauth/access_token").with(
38
+ :body => {
39
+ :grant_type => 'authorization_code',
40
+ :code => 'IZuJJVnL49Cc',
41
+ :redirect_uri => 'https://localhost/callback',
42
+ :client_id => '2945e6425da3d5d17ffc',
43
+ :client_secret => '0a8f686f2835a70a79dbcece2ec63bc5079f40a8'
44
+ },
45
+ :headers => {
46
+ 'Accept' => "application/json",
47
+ 'User-Agent' => "OAuth2 Ruby Gem #{OAuth2::Version}",
48
+ 'Content-Type' => "application/x-www-form-urlencoded"
49
+ }
50
+ )
51
+ response = subject.exchange_auth_code_for_token(
52
+ :params => {
53
+ :code => 'IZuJJVnL49Cc',
54
+ :redirect_uri => 'https://localhost/callback'
55
+ }
56
+ )
57
+ end
58
+ end
59
+ end
@@ -4,18 +4,7 @@ require 'google_client'
4
4
  describe GoogleClient do
5
5
 
6
6
  subject do
7
-
8
- GoogleClient.new('https://accounts.google.com', '827502413694.apps.googleusercontent.com', 'a2nQpcUm2Dgq1chWdAvbXGTk',{
9
- :token_path => '/o/oauth2/token',
10
- :authorize_path => '/o/oauth2/auth',
11
- :device_path => '/o/oauth2/device/code',
12
- :connection_options => {
13
- :headers => {
14
- "User-Agent" => "GoOAuth2 0.1",
15
- "Accept" => "application/json"
16
- }
17
- }
18
- })
7
+ GoogleClient.new('https://accounts.google.com', '827502413694.apps.googleusercontent.com', 'a2nQpcUm2Dgq1chWdAvbXGTk')
19
8
  end
20
9
  #
21
10
  # https://developers.google.com/accounts/docs/OAuth2WebServer#formingtheurl
@@ -25,7 +14,7 @@ describe GoogleClient do
25
14
  params = {
26
15
  "approval_prompt" => "force",
27
16
  "client_id" => "827502413694.apps.googleusercontent.com",
28
- "redirect_uri" => "http://localhost",
17
+ "redirect_uri" => "https://localhost",
29
18
  "response_type" =>"code",
30
19
  "scope" => "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
31
20
  "state" => "/profile",
@@ -35,7 +24,7 @@ describe GoogleClient do
35
24
  auth_url = subject.webserver_authorization_url(
36
25
  :scope => 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
37
26
  :state => '/profile',
38
- :redirect_uri => 'http://localhost',
27
+ :redirect_uri => 'https://localhost',
39
28
  :approval_prompt => 'force',
40
29
  :access_type => 'offline'
41
30
  )
@@ -53,7 +42,7 @@ describe GoogleClient do
53
42
  params = {
54
43
  "approval_prompt"=>"force",
55
44
  "client_id"=>"827502413694.apps.googleusercontent.com",
56
- "redirect_uri"=>"http://localhost",
45
+ "redirect_uri"=>"https://localhost",
57
46
  "response_type"=>"code",
58
47
  "scope"=>"https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
59
48
  "state"=>"/profile"
@@ -65,7 +54,7 @@ describe GoogleClient do
65
54
  'https://www.googleapis.com/auth/userinfo.profile'
66
55
  ],
67
56
  :state => '/profile',
68
- :redirect_uri => 'http://localhost',
57
+ :redirect_uri => 'https://localhost',
69
58
  :approval_prompt => 'force'
70
59
  )
71
60
 
@@ -91,25 +80,23 @@ describe GoogleClient do
91
80
  describe "#exchange_auth_code_for_token" do
92
81
  it "makes a request to google oauth2 server" do
93
82
 
94
- fake_response = double(
95
- :code => '200',
96
- :body => ''
97
- )
98
-
99
- Net::HTTP.any_instance.should_receive(:post).with(
100
- "/o/oauth2/token",
101
- "redirect_uri=https%3A%2F%2Flocalhost&code=4%2FdbB0-UD1cvrQg2EuEFtRtHwPEmvR.IrScsjgB5M4VuJJVnL49Cc8QdUjRdAI",
102
- {
103
- "Accept"=>"application/json",
104
- "User-Agent"=>"GoOAuth2 0.1",
105
- "Authorization"=>"Basic ODI3NTAyNDEzNjk0LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tOmEyblFwY1VtMkRncTFjaFdkQXZiWEdUaw==",
106
- "Content-Type"=>"application/x-www-form-urlencoded"
83
+ stub_request(:post, "https://accounts.google.com/o/oauth2/token").with(
84
+ :body => {
85
+ :grant_type => 'authorization_code',
86
+ :code => '4/o3xJkRT6SM_TpohrxC7T-4o3kqu6.MmOGL795LbIZuJJVnL49Cc-uiE7LeAI',
87
+ :redirect_uri => 'https://localhost',
88
+ :client_id => '827502413694.apps.googleusercontent.com',
89
+ :client_secret => 'a2nQpcUm2Dgq1chWdAvbXGTk'
90
+ },
91
+ :headers => {
92
+ 'Accept' => "application/json",
93
+ 'User-Agent' => "OAuth2 Ruby Gem #{OAuth2::Version}",
94
+ 'Content-Type' => "application/x-www-form-urlencoded"
107
95
  }
108
- ).and_return(fake_response)
109
-
110
- subject.exchange_auth_code_for_token(
96
+ )
97
+ response = subject.exchange_auth_code_for_token(
111
98
  :params => {
112
- :code => '4/dbB0-UD1cvrQg2EuEFtRtHwPEmvR.IrScsjgB5M4VuJJVnL49Cc8QdUjRdAI',
99
+ :code => '4/o3xJkRT6SM_TpohrxC7T-4o3kqu6.MmOGL795LbIZuJJVnL49Cc-uiE7LeAI',
113
100
  :redirect_uri => 'https://localhost'
114
101
  }
115
102
  )
@@ -144,26 +131,23 @@ describe GoogleClient do
144
131
 
145
132
  describe "#refresh_token" do
146
133
  it "makes a request to google to obtain new token" do
147
- fake_response = double(
148
- :code => '200',
149
- :body => ''
150
- )
151
134
 
152
- Net::HTTP.any_instance.should_receive(:post).with(
153
- "/o/oauth2/token",
154
- "state=%2Fprofile&grant_type=refresh_token&refresh_token=2YotnFZFEjr1zCsicMWpAA",
155
- {
156
- "Accept" => "application/json",
157
- "User-Agent" => "GoOAuth2 0.1",
158
- "Authorization" => "Basic ODI3NTAyNDEzNjk0LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tOmEyblFwY1VtMkRncTFjaFdkQXZiWEdUaw==",
159
- "Content-Type" => "application/x-www-form-urlencoded"
135
+ stub_request(:post, "https://accounts.google.com/o/oauth2/token").with(
136
+ :body => {
137
+ :grant_type => 'refresh_token',
138
+ :refresh_token => '1/6BMfW9j53gdGImsiyUH5kU5RsR4zwI9lUVX-tqf8JXQ',
139
+ :client_id => '827502413694.apps.googleusercontent.com',
140
+ :client_secret => 'a2nQpcUm2Dgq1chWdAvbXGTk'
141
+ },
142
+ :headers => {
143
+ 'Accept' => 'application/json',
144
+ 'User-Agent' => 'OAuth2 Ruby Gem 1.1.0',
145
+ 'Content-Type' => 'application/x-www-form-urlencoded'
160
146
  }
161
- ).and_return(fake_response)
162
-
147
+ )
163
148
  subject.refresh_access_token(
164
149
  :params => {
165
- :state => '/profile',
166
- :refresh_token => '2YotnFZFEjr1zCsicMWpAA'
150
+ :refresh_token => '1/6BMfW9j53gdGImsiyUH5kU5RsR4zwI9lUVX-tqf8JXQ'
167
151
  }
168
152
  )
169
153
  end
@@ -171,21 +155,17 @@ describe GoogleClient do
171
155
 
172
156
  describe "#get_device_code" do
173
157
  it "makes a request to google to obtain a device code" do
174
- fake_response = double(
175
- :code => '200',
176
- :body => ''
177
- )
178
-
179
- Net::HTTP.any_instance.should_receive(:post).with(
180
- "/o/oauth2/device/code",
181
- "scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile&client_id=827502413694.apps.googleusercontent.com",
182
- {
183
- "Accept"=>"application/json",
184
- "User-Agent"=>"GoOAuth2 0.1",
185
- "Content-Type"=>"application/x-www-form-urlencoded"
158
+ stub_request(:post, "https://accounts.google.com/o/oauth2/device/code").with(
159
+ :body => {
160
+ :scope => 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
161
+ :client_id => '827502413694.apps.googleusercontent.com'
162
+ },
163
+ :headers => {
164
+ 'Accept' => 'application/json',
165
+ 'User-Agent' => 'OAuth2 Ruby Gem 1.1.0',
166
+ 'Content-Type' => 'application/x-www-form-urlencoded'
186
167
  }
187
- ).and_return(fake_response)
188
-
168
+ )
189
169
  subject.get_device_code(
190
170
  :params => {
191
171
  :scope => 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile'
@@ -196,22 +176,18 @@ describe GoogleClient do
196
176
 
197
177
  describe "#exchange_device_code_for_token" do
198
178
  it "makes request to google to obtain an access token" do
199
- fake_response = double(
200
- :code => '200',
201
- :body => ''
202
- )
203
-
204
- Net::HTTP.any_instance.should_receive(:post).with(
205
- "/o/oauth2/token",
206
- "state=%2Fprofile&code=G3Y6jU3a&grant_type=http%3A%2F%2Foauth.net%2Fgrant_type%2Fdevice%2F1.0",
207
- {
208
- "Accept"=>"application/json",
209
- "User-Agent" => "GoOAuth2 0.1",
210
- "Authorization"=>"Basic ODI3NTAyNDEzNjk0LmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tOmEyblFwY1VtMkRncTFjaFdkQXZiWEdUaw==",
211
- "Content-Type"=>"application/x-www-form-urlencoded"
179
+ stub_request(:post, "https://827502413694.apps.googleusercontent.com:a2nQpcUm2Dgq1chWdAvbXGTk@accounts.google.com/o/oauth2/token").with(
180
+ :body => {
181
+ :grant_type => 'http://oauth.net/grant_type/device/1.0',
182
+ :state => '/profile',
183
+ :code => 'G3Y6jU3a'
184
+ },
185
+ :headers => {
186
+ 'Accept' => 'application/json',
187
+ 'User-Agent' => 'OAuth2 Ruby Gem 1.1.0',
188
+ 'Content-Type' => 'application/x-www-form-urlencoded'
212
189
  }
213
- ).and_return(fake_response)
214
-
190
+ )
215
191
  subject.exchange_device_code_for_token(
216
192
  :params => {
217
193
  :state => '/profile',
@@ -0,0 +1,75 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'yammer_client'
3
+
4
+ describe YammerClient do
5
+
6
+ subject do
7
+ YammerClient.new('https://www.yammer.com', 'PRbTcg9qjgKsp4jjpm1pw', 'a2nQpcUm2Dgq1chWdAvbXGTk')
8
+ end
9
+
10
+ describe "#clientside_authorization_url" do
11
+ it "returns url string for obtaining authorization" do
12
+ params = {
13
+ 'client_id' => 'PRbTcg9qjgKsp4jjpm1pw',
14
+ 'response_type' => 'token'
15
+ }
16
+
17
+ auth_url = subject.clientside_authorization_url
18
+
19
+ parsed_url = Addressable::URI.parse(auth_url)
20
+ expect(parsed_url.path).to eq '/dialog/oauth/authorize'
21
+ expect(parsed_url.query_values).to eq params
22
+ expect(parsed_url.scheme).to eq 'https'
23
+ expect(parsed_url.host).to eq 'www.yammer.com'
24
+ end
25
+ end
26
+
27
+ describe "#webserver_authorization_url" do
28
+ it "returns the authorization url" do
29
+ params = {
30
+ "client_id" => "PRbTcg9qjgKsp4jjpm1pw",
31
+ "redirect_uri" => "https://localhost/callback",
32
+ "response_type" =>"code",
33
+ "state" => "12345"
34
+ }
35
+
36
+ auth_url = subject.webserver_authorization_url(
37
+ :client_id => 'PRbTcg9qjgKsp4jjpm1pw',
38
+ :state => '12345',
39
+ :redirect_uri => 'https://localhost/callback'
40
+ )
41
+
42
+ parsed_url = Addressable::URI.parse(auth_url)
43
+ expect(parsed_url.path).to eq '/dialog/oauth/authorize'
44
+ expect(parsed_url.query_values).to eq params
45
+ expect(parsed_url.scheme).to eq 'https'
46
+ expect(parsed_url.host).to eq 'www.yammer.com'
47
+ end
48
+ end
49
+
50
+ describe "#exchange_auth_code_for_token" do
51
+ it "makes a request to google oauth2 server" do
52
+
53
+ stub_request(:post, "https://www.yammer.com/oauth2/token").with(
54
+ :body => {
55
+ :grant_type => 'authorization_code',
56
+ :code => 'MmOGL795LbIZuJJVnL49Cc',
57
+ :redirect_uri => 'https://localhost',
58
+ :client_id => 'PRbTcg9qjgKsp4jjpm1pw',
59
+ :client_secret => 'a2nQpcUm2Dgq1chWdAvbXGTk'
60
+ },
61
+ :headers => {
62
+ 'Accept' => "application/json",
63
+ 'User-Agent' => "OAuth2 Ruby Gem #{OAuth2::Version}",
64
+ 'Content-Type' => "application/x-www-form-urlencoded"
65
+ }
66
+ )
67
+ response = subject.exchange_auth_code_for_token(
68
+ :params => {
69
+ :code => 'MmOGL795LbIZuJJVnL49Cc',
70
+ :redirect_uri => 'https://localhost'
71
+ }
72
+ )
73
+ end
74
+ end
75
+ end