oauth2-client 1.0.0

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