oauth2-client 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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