oauth2-client 1.0.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.
@@ -0,0 +1,28 @@
1
+ module OAuth2
2
+ module Grant
3
+ class Password < Base
4
+
5
+ def grant_type
6
+ 'password'
7
+ end
8
+
9
+ # Retrieve an access token given the specified client.
10
+ #
11
+ # @param username
12
+ # @param password
13
+ # @param [Hash] params additional params
14
+ # @param [Hash] opts options
15
+ def get_token(username, password, opts={})
16
+ opts[:params] ||= {}
17
+ opts[:params].merge!({
18
+ :grant_type => grant_type,
19
+ :username => username,
20
+ :password => password
21
+ })
22
+ opts[:authenticate] ||= :headers
23
+ method = opts.delete(:method) || :post
24
+ make_request(method, @token_path, opts)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,26 @@
1
+ module OAuth2
2
+ module Grant
3
+ class RefreshToken < Base
4
+
5
+ def grant_type
6
+ 'refresh_token'
7
+ end
8
+
9
+ # Retrieve an access token for a given refresh token
10
+ #
11
+ # @param [String] refresh_token refresh token
12
+ # @param [Hash] params additional params
13
+ # @param [Hash] opts options
14
+ def get_token(refresh_token, opts={})
15
+ params = opts[:params] || {}
16
+ opts[:params] = params.merge!({
17
+ :grant_type => grant_type,
18
+ :refresh_token => refresh_token
19
+ })
20
+ opts[:authenticate] ||= :headers
21
+ method = opts.delete(:method) || :post
22
+ make_request(method, @token_path, opts)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,46 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+ require 'addressable/uri'
4
+ module OAuth2
5
+ module UrlHelper
6
+ # convenience method to build response URI
7
+ def build_url(uri, opts={})
8
+ path = opts[:path] || ''
9
+ query = opts[:params] || {}
10
+ fragment = opts[:fragment] || {}
11
+ url = Addressable::URI.parse uri
12
+ url.path = path
13
+ url.query_values = query unless query.empty?
14
+ url.fragment = Addressable::URI.form_encode(fragment) unless fragment.empty?
15
+ url.to_s
16
+ end
17
+
18
+ # generates a random key of up to +size+ bytes. The value returned is Base64 encoded with non-word
19
+ # characters removed.
20
+ def generate_urlsafe_key(size=48)
21
+ seed = Time.now.to_i
22
+ size = size - seed.to_s.length
23
+ Base64.encode64("#{ OpenSSL::Random.random_bytes(size) }#{ seed }").gsub(/\W/, '')
24
+ end
25
+ alias_method :generate_nonce, :generate_urlsafe_key
26
+
27
+ def generate_timestamp #:nodoc:
28
+ Time.now.to_i.to_s
29
+ end
30
+
31
+ def http_basic_encode(username, password)
32
+ encoded_data = ["#{username}:#{password}"].pack("m0")
33
+ "Basic #{encoded_data}"
34
+ end
35
+ module_function :http_basic_encode
36
+
37
+ # converts a hash to a URI query string
38
+ # @params [Hash] params URI parameters
39
+ def to_query(params)
40
+ unless params.is_a?(Hash)
41
+ raise "Expected Hash but got #{params.class.name}"
42
+ end
43
+ Addressable::URI.form_encode(params)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ module OAuth2
2
+ class Version
3
+ MAJOR = 1 unless defined? OAuth2::Version::MAJOR
4
+ MINOR = 0 unless defined? OAuth2::Version::MINOR
5
+ PATCH = 0 unless defined? OAuth2::Version::PATCH
6
+
7
+ def self.to_s
8
+ [MAJOR, MINOR, PATCH].compact.join('.')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
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
Binary file
@@ -0,0 +1,223 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'google_client'
3
+
4
+ describe GoogleClient do
5
+
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
+ })
19
+ end
20
+ #
21
+ # https://developers.google.com/accounts/docs/OAuth2WebServer#formingtheurl
22
+ describe "#webserver_authorization_url" do
23
+ context "with scope as string" do
24
+ it "returns the authorization url" do
25
+ params = {
26
+ "approval_prompt" => "force",
27
+ "client_id" => "827502413694.apps.googleusercontent.com",
28
+ "redirect_uri" => "http://localhost",
29
+ "response_type" =>"code",
30
+ "scope" => "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
31
+ "state" => "/profile",
32
+ "access_type" => "offline"
33
+ }
34
+
35
+ auth_url = subject.webserver_authorization_url(
36
+ :scope => 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
37
+ :state => '/profile',
38
+ :redirect_uri => 'http://localhost',
39
+ :approval_prompt => 'force',
40
+ :access_type => 'offline'
41
+ )
42
+
43
+ parsed_url = Addressable::URI.parse(auth_url)
44
+ expect(parsed_url.path).to eq '/o/oauth2/auth'
45
+ expect(parsed_url.query_values).to eq params
46
+ expect(parsed_url.scheme).to eq 'https'
47
+ expect(parsed_url.host).to eq 'accounts.google.com'
48
+ end
49
+ end
50
+
51
+ context "with scope as array" do
52
+ it "returns the authorization url" do
53
+ params = {
54
+ "approval_prompt"=>"force",
55
+ "client_id"=>"827502413694.apps.googleusercontent.com",
56
+ "redirect_uri"=>"http://localhost",
57
+ "response_type"=>"code",
58
+ "scope"=>"https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile",
59
+ "state"=>"/profile"
60
+ }
61
+
62
+ auth_url = subject.webserver_authorization_url(
63
+ :scope => [
64
+ 'https://www.googleapis.com/auth/userinfo.email',
65
+ 'https://www.googleapis.com/auth/userinfo.profile'
66
+ ],
67
+ :state => '/profile',
68
+ :redirect_uri => 'http://localhost',
69
+ :approval_prompt => 'force'
70
+ )
71
+
72
+ parsed_url = Addressable::URI.parse(auth_url)
73
+ expect(parsed_url.path).to eq '/o/oauth2/auth'
74
+ expect(parsed_url.query_values).to eq params
75
+ expect(parsed_url.scheme).to eq 'https'
76
+ expect(parsed_url.host).to eq 'accounts.google.com'
77
+ end
78
+ end
79
+
80
+ context "scope neither array or string" do
81
+ it "raises and error" do
82
+ expect do
83
+ subject.webserver_authorization_url(
84
+ :scope => {},
85
+ :redirect_uri => 'https://oauth2-login-demo.appspot.com/code')
86
+ end.to raise_error
87
+ end
88
+ end
89
+ end
90
+
91
+ describe "#exchange_auth_code_for_token" do
92
+ it "makes a request to google oauth2 server" do
93
+
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"
107
+ }
108
+ ).and_return(fake_response)
109
+
110
+ subject.exchange_auth_code_for_token(
111
+ :params => {
112
+ :code => '4/dbB0-UD1cvrQg2EuEFtRtHwPEmvR.IrScsjgB5M4VuJJVnL49Cc8QdUjRdAI',
113
+ :redirect_uri => 'https://localhost'
114
+ }
115
+ )
116
+ end
117
+ end
118
+
119
+ describe "#client_authorization_url" do
120
+ it "returns url string for obtaining authorization" do
121
+ params = {
122
+ "approval_prompt" => "force",
123
+ "client_id" => "827502413694.apps.googleusercontent.com",
124
+ "redirect_uri" => "https://oauth2-login-demo.appspot.com/token",
125
+ "response_type" => "token",
126
+ "scope" => "https://www.googleapis.com/auth/userinfo.email",
127
+ "state" => "/profile"
128
+ }
129
+
130
+ auth_url = subject.clientside_authorization_url(
131
+ :scope => 'https://www.googleapis.com/auth/userinfo.email',
132
+ :state => '/profile',
133
+ :redirect_uri => 'https://oauth2-login-demo.appspot.com/token',
134
+ :approval_prompt => 'force'
135
+ )
136
+
137
+ parsed_url = Addressable::URI.parse(auth_url)
138
+ expect(parsed_url.path).to eq '/o/oauth2/auth'
139
+ expect(parsed_url.query_values).to eq params
140
+ expect(parsed_url.scheme).to eq 'https'
141
+ expect(parsed_url.host).to eq 'accounts.google.com'
142
+ end
143
+ end
144
+
145
+ describe "#refresh_token" do
146
+ it "makes a request to google to obtain new token" do
147
+ fake_response = double(
148
+ :code => '200',
149
+ :body => ''
150
+ )
151
+
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"
160
+ }
161
+ ).and_return(fake_response)
162
+
163
+ subject.refresh_access_token(
164
+ :params => {
165
+ :state => '/profile',
166
+ :refresh_token => '2YotnFZFEjr1zCsicMWpAA'
167
+ }
168
+ )
169
+ end
170
+ end
171
+
172
+ describe "#get_device_code" do
173
+ 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"
186
+ }
187
+ ).and_return(fake_response)
188
+
189
+ subject.get_device_code(
190
+ :params => {
191
+ :scope => 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile'
192
+ }
193
+ )
194
+ end
195
+ end
196
+
197
+ describe "#exchange_device_code_for_token" do
198
+ 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"
212
+ }
213
+ ).and_return(fake_response)
214
+
215
+ subject.exchange_device_code_for_token(
216
+ :params => {
217
+ :state => '/profile',
218
+ :code => 'G3Y6jU3a'
219
+ }
220
+ )
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,60 @@
1
+ test:
2
+ google:
3
+ client_id: '827502413694.apps.googleusercontent.com'
4
+ client_secret: 'a2nQpcUm2Dgq1chWdAvbXGTk'
5
+ host: accounts.google.com
6
+ port: 443
7
+ token_path: /o/oauth2/token
8
+ authorize_path: /o/oauth2/auth
9
+ device_path: /o/oauth2/device/code
10
+ http_client: #OAuth2Client::Connection
11
+ max_redirects: 5
12
+ ssl:
13
+
14
+ yammer:
15
+ client_id: 'PRbTcg9qjgKsp4jjpm1pw'
16
+ client_secret: 'Xn7kp7Ly0TCY4GtZWkmSsqGEPg10DmMADyjWkf2U'
17
+ host: www.yammer.com
18
+ port: 443
19
+ token_path: /oauth2/access_token
20
+ authorize_path: /dialog/oauth/
21
+ device_path:
22
+ http_client: #OAuth2Client::Connection
23
+ max_redirects: 5
24
+ ssl: #
25
+
26
+ microsoft:
27
+ client_id: 'PRbTcg9qjgKsp4jjpm1pw'
28
+ client_secret: 'Xn7kp7Ly0TCY4GtZWkmSsqGEPg10DmMADyjWkf2U'
29
+ host: login.live.com
30
+ port: 443
31
+ token_path: /oauth20_token.srf
32
+ authorize_path: /oauth20_authorize.srf
33
+ device_path:
34
+ http_client: #OAuth2Client::Connection
35
+ max_redirects: 5
36
+ ssl: #
37
+
38
+ github:
39
+ client_id: '2945e6425da3d5d17ffc'
40
+ client_secret: '0a8f686f2835a70a79dbcece2ec63bc5079f40a8'
41
+ host: github.com
42
+ port: 443
43
+ token_path: /login/oauth/access_token
44
+ authorize_path: /login/oauth/authorize
45
+ device_path:
46
+ http_client: #OAuth2Client::Connection
47
+ max_redirects: 5
48
+ ssl: #
49
+
50
+ foursquare:
51
+ client_id: '2945e6425da3d5d17ffc'
52
+ client_secret: '0a8f686f2835a70a79dbcece2ec63bc5079f40a8'
53
+ host: foursquare.com
54
+ port: 443
55
+ token_path: /oauth2/access_token
56
+ authorize_path: /oauth2/authenticate
57
+ device_path:
58
+ http_client: #OAuth2Client::Connection
59
+ max_redirects: 5
60
+ ssl: #
@@ -0,0 +1,157 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+ require 'ostruct'
3
+
4
+ describe OAuth2::Client do
5
+
6
+ before :all do
7
+ @client_id= 's6BhdRkqt3'
8
+ @client_secret = '4hJZY88TCBB9q8IpkeualA2lZsUhOSclkkSKw3RXuE'
9
+ @host = 'example.com'
10
+ @client = OAuth2::Client.new(@host, @client_id, @client_secret)
11
+ end
12
+
13
+ subject { @client }
14
+
15
+ context "with default options" do
16
+ describe "#token_path" do
17
+ it "returns " do
18
+ expect(subject.token_path).to eq '/oauth2/token'
19
+ end
20
+ end
21
+
22
+ describe "#authorize_path" do
23
+ it "returns " do
24
+ expect(subject.authorize_path).to eq '/oauth2/authorize'
25
+ end
26
+ end
27
+
28
+ describe "#device_path" do
29
+ it "returns " do
30
+ expect(subject.device_path).to eq '/oauth2/device/code'
31
+ end
32
+ end
33
+ end
34
+
35
+ context "with custom options" do
36
+ subject do
37
+ OAuth2::Client.new(@host, @client_id, @client_secret, {
38
+ :token_path => '/o/v2/token',
39
+ :authorize_path => '/o/v2/authorize',
40
+ :device_path => '/o/v2/device/code'
41
+ })
42
+ end
43
+
44
+ describe "#token_path" do
45
+ it "returns token path" do
46
+ expect(subject.token_path).to eq '/o/v2/token'
47
+ end
48
+ end
49
+
50
+ describe "#authorize_path" do
51
+ it "returns authorize path" do
52
+ expect(subject.authorize_path).to eq '/o/v2/authorize'
53
+ end
54
+ end
55
+
56
+ describe "#device_path" do
57
+ it "returns device path" do
58
+ expect(subject.device_path).to eq '/o/v2/device/code'
59
+ end
60
+ end
61
+ end
62
+
63
+ describe "host" do
64
+ it "returns host" do
65
+ expect(subject.host).to eq 'example.com'
66
+ end
67
+ end
68
+
69
+ describe "host=" do
70
+ before do
71
+ subject.host = 'elpmaxe.com'
72
+ end
73
+
74
+ it "set the connection to nil" do
75
+ expect(subject.instance_variable_get(:'@connection')).to eq nil
76
+ end
77
+
78
+ it "sets new host on client" do
79
+ expect(subject.host).to eq 'elpmaxe.com'
80
+ end
81
+ end
82
+
83
+ describe "#connection_options" do
84
+ context "with default connection options" do
85
+ it "returns empty hash" do
86
+ expect(subject.connection_options).to eq ({})
87
+ end
88
+ end
89
+
90
+ context "with custom connection options" do
91
+ it "returns default options" do
92
+ subject.connection_options = { :max_redirects => 10, :use_ssl => true }
93
+ expect(subject.connection_options).to eq ({:max_redirects => 10, :use_ssl => true})
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#implicit" do
99
+ it "returns implicit grant object" do
100
+ expect(@client.implicit).to be_instance_of(OAuth2::Grant::Implicit)
101
+ end
102
+ end
103
+
104
+ describe "#authorization_code" do
105
+ it "returns authorization code grant" do
106
+ expect(@client.authorization_code).to be_instance_of(OAuth2::Grant::AuthorizationCode)
107
+ end
108
+ end
109
+
110
+ describe "#refresh_token" do
111
+ it "returns refresh token grant" do
112
+ expect(@client.refresh_token).to be_instance_of(OAuth2::Grant::RefreshToken)
113
+ end
114
+ end
115
+
116
+ describe "#client_credentials" do
117
+ it "returns client credentials grant" do
118
+ expect(@client.client_credentials).to be_instance_of(OAuth2::Grant::ClientCredentials)
119
+ end
120
+ end
121
+
122
+ describe "#password" do
123
+ it "returns password grant" do
124
+ expect(@client.password).to be_instance_of(OAuth2::Grant::Password)
125
+ end
126
+ end
127
+
128
+ describe "" do
129
+ it "returns device code grant" do
130
+ expect(@client.device_code).to be_instance_of(OAuth2::Grant::DeviceCode)
131
+ end
132
+ end
133
+
134
+
135
+ describe "#implicit" do
136
+ it "returns implicit grant object" do
137
+ expect(subject.implicit).to be_instance_of(OAuth2::Grant::Implicit)
138
+ end
139
+ end
140
+
141
+ describe "#connection" do
142
+ context "with default connection options" do
143
+ it "returns HttpConnection" do
144
+ expect(subject.send(:connection)).to be_instance_of(OAuth2::HttpConnection)
145
+ end
146
+ end
147
+
148
+ context "with custom connection options" do
149
+ it "returns custom connection" do
150
+ # custom_http = Struct.new('CustomHttpClient')
151
+ # conn_options = { :connection_client => custom_http }
152
+ # oauth_client = OAuth2::Client.new('example.com', @client_id, @client_secret, conn_options)
153
+ # expect(oauth_client.send(:connection)).to be_instance_of custom_http
154
+ end
155
+ end
156
+ end
157
+ end