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.
- data/.gitignore +18 -0
- data/.travis.yml +6 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +37 -0
- data/LICENSE +20 -0
- data/README.md +152 -0
- data/Rakefile +11 -0
- data/TODO +10 -0
- data/examples/google_client.rb +159 -0
- data/lib/oauth2.rb +7 -0
- data/lib/oauth2/client.rb +63 -0
- data/lib/oauth2/connection.rb +188 -0
- data/lib/oauth2/error.rb +3 -0
- data/lib/oauth2/grant.rb +7 -0
- data/lib/oauth2/grant/authorization_code.rb +71 -0
- data/lib/oauth2/grant/base.rb +41 -0
- data/lib/oauth2/grant/client_credentials.rb +27 -0
- data/lib/oauth2/grant/device.rb +37 -0
- data/lib/oauth2/grant/implicit.rb +46 -0
- data/lib/oauth2/grant/password.rb +28 -0
- data/lib/oauth2/grant/refresh_token.rb +26 -0
- data/lib/oauth2/helper.rb +46 -0
- data/lib/oauth2/version.rb +11 -0
- data/oauth2-client.gemspec +13 -0
- data/spec/.DS_Store +0 -0
- data/spec/examples/google_client_spec.rb +223 -0
- data/spec/mocks/oauth_client.yml +60 -0
- data/spec/oauth2/client_spec.rb +157 -0
- data/spec/oauth2/connection_spec.rb +273 -0
- data/spec/oauth2/grant/authorization_code_spec.rb +89 -0
- data/spec/oauth2/grant/base_spec.rb +57 -0
- data/spec/oauth2/grant/client_credentials_spec.rb +28 -0
- data/spec/oauth2/grant/device_spec.rb +35 -0
- data/spec/oauth2/grant/implicit_spec.rb +36 -0
- data/spec/oauth2/grant/password_spec.rb +28 -0
- data/spec/oauth2/grant/refresh_token_spec.rb +27 -0
- data/spec/spec_helper.rb +15 -0
- metadata +83 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'json'
|
4
|
+
|
5
|
+
gem 'bcrypt-ruby', '~> 3.0.0'
|
6
|
+
|
7
|
+
gem 'addressable'
|
8
|
+
|
9
|
+
gem 'rake'
|
10
|
+
|
11
|
+
gem 'rspec', '~>2.12'
|
12
|
+
|
13
|
+
gem 'simplecov'
|
14
|
+
|
15
|
+
gem 'activesupport', '~> 3.2.8'
|
16
|
+
|
17
|
+
# gem 'mocha', '>=0.12.1', :require => false
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
GEM
|
2
|
+
remote: https://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.2.8)
|
5
|
+
i18n (~> 0.6)
|
6
|
+
multi_json (~> 1.0)
|
7
|
+
addressable (2.3.2)
|
8
|
+
bcrypt-ruby (3.0.1)
|
9
|
+
diff-lcs (1.1.3)
|
10
|
+
i18n (0.6.1)
|
11
|
+
json (1.7.5)
|
12
|
+
multi_json (1.3.6)
|
13
|
+
rake (0.9.2.2)
|
14
|
+
rspec (2.12.0)
|
15
|
+
rspec-core (~> 2.12.0)
|
16
|
+
rspec-expectations (~> 2.12.0)
|
17
|
+
rspec-mocks (~> 2.12.0)
|
18
|
+
rspec-core (2.12.2)
|
19
|
+
rspec-expectations (2.12.1)
|
20
|
+
diff-lcs (~> 1.1.3)
|
21
|
+
rspec-mocks (2.12.1)
|
22
|
+
simplecov (0.7.1)
|
23
|
+
multi_json (~> 1.0)
|
24
|
+
simplecov-html (~> 0.7.1)
|
25
|
+
simplecov-html (0.7.1)
|
26
|
+
|
27
|
+
PLATFORMS
|
28
|
+
ruby
|
29
|
+
|
30
|
+
DEPENDENCIES
|
31
|
+
activesupport (~> 3.2.8)
|
32
|
+
addressable
|
33
|
+
bcrypt-ruby (~> 3.0.0)
|
34
|
+
json
|
35
|
+
rake
|
36
|
+
rspec (~> 2.12)
|
37
|
+
simplecov
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012, Kevin Mutyaba
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# OAuth2 Ruby Client
|
2
|
+
|
3
|
+
[][travis]
|
4
|
+
|
5
|
+
[travis]: http://travis-ci.org/tiabas/oauth2-client
|
6
|
+
|
7
|
+
A Ruby wrapper for the OAuth 2.0 specification. It is designed with the philosophy that not
|
8
|
+
every service that claims to support OAuth 2.0 actually implements it according to the
|
9
|
+
[standard]( http://tools.ietf.org/html/rfc6749). This version therefore, affords
|
10
|
+
the developer some degree of flexibility in generating the URLs and requests
|
11
|
+
needed to authorize an OAuth 2.0 application.
|
12
|
+
|
13
|
+
For more about the standard, take a look at http://tools.ietf.org/html/rfc6749
|
14
|
+
|
15
|
+
## Installation
|
16
|
+
Download the library and include the its location in your Gemfile
|
17
|
+
|
18
|
+
## Resources
|
19
|
+
* [View Source on GitHub][code]
|
20
|
+
* [Report Issues on GitHub][issues]
|
21
|
+
|
22
|
+
[code]: https://github.com/tiabas/oauth2-client
|
23
|
+
[issues]: https://github.com/tiabas/oauth2-client/issues
|
24
|
+
|
25
|
+
## Usage Examples
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
require 'oauth2-client'
|
29
|
+
|
30
|
+
@client = OAuth2::Client.new('https://example.com', 's6BhdRkqt3', '4hJZY88TCBB9q8IpkeualA2lZsUhOSclkkSKw3RXuE')
|
31
|
+
|
32
|
+
client.authorization_code.authorization_path(:redirect_uri => 'http://localhost/oauth2/cb')
|
33
|
+
# => "/oauth/authorize?response_type=code&client_id={client_id}&redirect_uri=http%3A%2F%2Flocalhost%2Foauth2%2Fcb"
|
34
|
+
|
35
|
+
```
|
36
|
+
|
37
|
+
## Authorization Grants
|
38
|
+
The client wraps around the creation of any given grant and passing in the parameters defined in the configuration
|
39
|
+
file. The supported grants include Authorization Code, Implicit, Resource Owner Password Credentials, Client Credentials.
|
40
|
+
There is also support for device authentication as described in Google's OAuth 2.0 authentication methods(https://developers.google.com/accounts/docs/OAuth2ForDevices). They are available via the #authorization_code, #implicit, #password, #client_credentials, #refresh_token
|
41
|
+
and #device methods on a client object.
|
42
|
+
|
43
|
+
The #get_token method on the grants does not make any assumptions about the format ofthe response from the OAuth provider. The ideal
|
44
|
+
case would be to treat all responses as JSON. However, some services may respond with in XML instead of JSON. The #get_token method
|
45
|
+
therefore, returns with an HTTPResponse object.
|
46
|
+
|
47
|
+
### Authorization Code
|
48
|
+
```ruby
|
49
|
+
auth_url = client.authorization_code.authorization_path(:redirect_uri => 'http://localhost/oauth2/cb')
|
50
|
+
# => "/oauth/authorize?response_type=code&client_id={client_id}&redirect_uri=http%3A%2F%2Flocalhost%2Foauth2%2Fcb"
|
51
|
+
|
52
|
+
token_url = client.authorization_code.token_path(
|
53
|
+
:code => aXW2c6bYz,
|
54
|
+
:redirect_uri => 'http://localhost/oauth2/cb')
|
55
|
+
# => "/oauth/token?redirect_uri=http%3A%2F%2Flocalhost%2Foauth%2Fcb&client_secret={client_secret}&grant_type=authorization_code&client_id={client_id}&code=aXW2c6bYz"
|
56
|
+
```
|
57
|
+
|
58
|
+
### Implicit Grant
|
59
|
+
```ruby
|
60
|
+
auth_url = client.implicit.authorization_path(:redirect_uri => 'http://localhost/oauth2/cb')
|
61
|
+
# => "oauth/?redirect_uri=http%3A%2F%2Flocalhost%2Foauth%2Fcb&response_type=token&client_id={client_id}"
|
62
|
+
```
|
63
|
+
|
64
|
+
### Password Credentials
|
65
|
+
```ruby
|
66
|
+
token = client.password.get_token('username', 'password')
|
67
|
+
```
|
68
|
+
|
69
|
+
### Refresh Token
|
70
|
+
```ruby
|
71
|
+
token = client.refresh_token.get_token(refresh_token_value, :params => {:scope => 'abc xyz', :state => 'state'})
|
72
|
+
```
|
73
|
+
|
74
|
+
### Client Credentials
|
75
|
+
```ruby
|
76
|
+
token = client.client_credentials.get_token
|
77
|
+
```
|
78
|
+
|
79
|
+
### Device Code
|
80
|
+
```ruby
|
81
|
+
auth_url = client.device_code.authorization_path(:scope => 'abc xyz', :state => 'state')
|
82
|
+
# => "/oauth/device/code?scope=abc+xyz&state=state&client_id={client_id}"
|
83
|
+
|
84
|
+
# exchange device authorization code for access token
|
85
|
+
token = client.device_code.get_token(device_auth_code)
|
86
|
+
```
|
87
|
+
|
88
|
+
# Client Examples
|
89
|
+
This library comes bundled with two sample implementations of Google and Yammer OAuth clients. These clients are
|
90
|
+
meant to showcase the degree of flexibilty that you get when using this library to interact with other OAuth 2.0
|
91
|
+
providers.
|
92
|
+
|
93
|
+
## Google Client
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
|
97
|
+
google_client = GoogleClient.new(
|
98
|
+
'https://accounts.google.com',
|
99
|
+
'827502413694.apps.googleusercontent.com',
|
100
|
+
'a2nQpcUm2Dgq1chWdAvbXGTk',
|
101
|
+
{
|
102
|
+
:token_path => '/o/oauth2/token',
|
103
|
+
:authorize_path => '/o/oauth2/auth',
|
104
|
+
:device_path => '/o/oauth2/device/code'
|
105
|
+
}
|
106
|
+
)
|
107
|
+
|
108
|
+
```
|
109
|
+
|
110
|
+
### Client-side authorization URL(Implicit grant)
|
111
|
+
```ruby
|
112
|
+
auth_url = google_client.webserver_authorization_url(
|
113
|
+
:scope => 'https://www.googleapis.com/auth/userinfo.email',
|
114
|
+
:state => '/profile',
|
115
|
+
:redirect_uri => 'https://oauth2-login-demo.appspot.com/code',
|
116
|
+
:approval_prompt => 'force')
|
117
|
+
# => https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&state=%2Fprofile&redirect_uri=https%3A%2F%2Foauth2-login-demo.appspot.com%2Ftoken&approval_prompt=force&response_type=token&client_id=812741506391.apps.googleusercontent.com
|
118
|
+
```
|
119
|
+
|
120
|
+
### Server-side authorization URL(Authorization code grant)
|
121
|
+
```ruby
|
122
|
+
auth_url = google_client.clientside_authorization_url(
|
123
|
+
:scope => 'https://www.googleapis.com/auth/userinfo.email',
|
124
|
+
:state => '/profile',
|
125
|
+
:redirect_uri => 'http://localhost/oauth/code',
|
126
|
+
:approval_prompt => 'force')
|
127
|
+
# => https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&state=%2Fprofile&redirect_uri=https%3A%2F%2Foauth2-login-demo.appspot.com%2Fcode&approval_prompt=force&response_type=code&client_id=812741506391.apps.googleusercontent.com
|
128
|
+
|
129
|
+
# exchange authorization code for access token. we will get back a Net::HTTPResponse
|
130
|
+
response = google_client.exchange_auth_code_for_token(
|
131
|
+
:params => {
|
132
|
+
:code => '4/dbB0-UD1cvrQg2EuEFtRtHwPEmvR.IrScsjgB5M4VuJJVnL49Cc8QdUjRdAI',
|
133
|
+
:redirect_uri => 'http://localhost/oauth/token'
|
134
|
+
}
|
135
|
+
)
|
136
|
+
response.inspect
|
137
|
+
# => #<Net::HTTPOK:0x007ff8bc7c1200>
|
138
|
+
|
139
|
+
response.body
|
140
|
+
# => {
|
141
|
+
"access_token" : "ya91.AHES8ZS-oCZnc5yHepnsosFjNln9ZKLuioF6FcMRCGUIzA",
|
142
|
+
"token_type" : "Bearer",
|
143
|
+
"expires_in" : 3600,
|
144
|
+
"id_token" : "eyJhbGciOiJSUzI1NiIsImtpZCI6IjY4ZGM2ZmIxNDQ5OGJmMWRhNjNiMWYyMDA2YmRmMDA2N2Q4MzY",
|
145
|
+
"refresh_token" : "6/Ju8uhi9xOctGEyHRzWwHhaYimfxmY0tiJ_qW3qvjWXM"
|
146
|
+
}
|
147
|
+
```
|
148
|
+
|
149
|
+
## Copyright
|
150
|
+
Copyright (c) 2012 Kevin Mutyaba
|
151
|
+
See [LICENSE][] for details.
|
152
|
+
[license]: https://github.com/tiabas/oauth2-client/blob/master/LICENSE
|
data/Rakefile
ADDED
data/TODO
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
class GoogleClient < OAuth2::Client
|
2
|
+
|
3
|
+
def normalize_scope(scope, sep=' ')
|
4
|
+
unless (scope.is_a?(String) || scope.is_a?(Array))
|
5
|
+
raise ArgumentError.new("Expected scope of type String or Array but was: #{scope.class.name}")
|
6
|
+
end
|
7
|
+
return scope if scope.is_a?(String)
|
8
|
+
scope.join(sep)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Generates the Google URL that the user will be redirected to in order to
|
12
|
+
# authorize your application
|
13
|
+
#
|
14
|
+
# @see https://developers.google.com/accounts/docs/OAuth2UserAgent#formingtheurl
|
15
|
+
#
|
16
|
+
# @params [Hash] additional parameters to be include in URL eg. scope, state, etc
|
17
|
+
#
|
18
|
+
# client = GoogleClient.new('https://accounts.google.com', '827502413694.apps.googleusercontent.com', 'a2nQpcUm2Dgq1chWdAvbXGTk',{
|
19
|
+
# :token_path => '/o/oauth2/token',
|
20
|
+
# :authorize_path => '/o/oauth2/auth',
|
21
|
+
# :device_path => '/o/oauth2/device/code'
|
22
|
+
# })
|
23
|
+
# client.clientside_authorization_url({
|
24
|
+
# :scope => 'https://www.googleapis.com/auth/userinfo.email',
|
25
|
+
# :state => '/profile',
|
26
|
+
# :redirect_uri => 'https://oauth2-login-demo.appspot.com/code',
|
27
|
+
# :approval_prompt => 'force',
|
28
|
+
# })
|
29
|
+
# #=>
|
30
|
+
def clientside_authorization_url(params={})
|
31
|
+
params[:scope] = normalize_scope(params[:scope]) if params[:scope]
|
32
|
+
implicit.token_url(params)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Generates the Google URL that the user will be redirected to in order to
|
36
|
+
# authorize your application
|
37
|
+
#
|
38
|
+
# @see https://developers.google.com/accounts/docs/OAuth2WebServer#formingtheurl
|
39
|
+
#
|
40
|
+
# @params [Hash] additional parameters to be include in URL eg. scope, state, etc
|
41
|
+
#
|
42
|
+
# client = GoogleClient.new('https://accounts.google.com', '827502413694.apps.googleusercontent.com', 'a2nQpcUm2Dgq1chWdAvbXGTk',{
|
43
|
+
# :token_path => '/o/oauth2/token',
|
44
|
+
# :authorize_path => '/o/oauth2/auth',
|
45
|
+
# :device_path => '/o/oauth2/device/code'
|
46
|
+
# })
|
47
|
+
# client.webserver_authorization_url({
|
48
|
+
# :scope => 'https://www.googleapis.com/auth/userinfo.email',
|
49
|
+
# :state => '/profile',
|
50
|
+
# :redirect_uri => 'https://oauth2-login-demo.appspot.com/code',
|
51
|
+
# :approval_prompt => 'force',
|
52
|
+
# })
|
53
|
+
# #=>
|
54
|
+
def webserver_authorization_url(params={})
|
55
|
+
params[:scope] = normalize_scope(params[:scope]) if params[:scope]
|
56
|
+
authorization_code.authorization_url(params)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Generates the Google URL that allows a user to obtain an authorization
|
60
|
+
# code for a given device
|
61
|
+
#
|
62
|
+
# @see https://developers.google.com/accounts/docs/OAuth2ForDevices
|
63
|
+
# def device_authorization_url(params={})
|
64
|
+
# params[:scope] = normalize_scope(params[:scope]) if params[:scope]
|
65
|
+
# device.authorization_url(params)
|
66
|
+
# end
|
67
|
+
|
68
|
+
# Makes a request to google server that will swap your authorization code for an access
|
69
|
+
# token
|
70
|
+
#
|
71
|
+
# @see https://developers.google.com/accounts/docs/OAuth2WebServer#handlingtheresponse
|
72
|
+
#
|
73
|
+
# @params [Hash] additional parameters to be include in URL eg. scope, state, etc
|
74
|
+
#
|
75
|
+
# client = GoogleClient.new('https://accounts.google.com', '827502413694.apps.googleusercontent.com', 'a2nQpcUm2Dgq1chWdAvbXGTk',{
|
76
|
+
# :token_path => '/o/oauth2/token',
|
77
|
+
# :authorize_path => '/o/oauth2/auth',
|
78
|
+
# :device_path => '/o/oauth2/device/code'
|
79
|
+
# })
|
80
|
+
# client.exchange_auth_code_for_token({
|
81
|
+
# :scope => 'https://www.googleapis.com/auth/userinfo.email',
|
82
|
+
# :state => '/profile',
|
83
|
+
# :code => 'G3Y6jU3a',
|
84
|
+
# })
|
85
|
+
# #=>
|
86
|
+
def exchange_auth_code_for_token(opts={})
|
87
|
+
unless (opts[:params] && opts[:params][:code])
|
88
|
+
raise ArgumentError.new("You must include an authorization code as a parameter")
|
89
|
+
end
|
90
|
+
code = opts[:params].delete(:code)
|
91
|
+
authorization_code.get_token(code, opts)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Makes a request to google server that will generate a new access token given that your
|
95
|
+
# application was not deauthorized by the user
|
96
|
+
#
|
97
|
+
# @see https://developers.google.com/accounts/docs/OAuth2InstalledApp#refresh
|
98
|
+
#
|
99
|
+
# @params [Hash] additional parameters to be include in URL eg. state
|
100
|
+
#
|
101
|
+
# client = GoogleClient.new('https://accounts.google.com', '827502413694.apps.googleusercontent.com', 'a2nQpcUm2Dgq1chWdAvbXGTk',{
|
102
|
+
# :token_path => '/o/oauth2/token',
|
103
|
+
# :authorize_path => '/o/oauth2/auth',
|
104
|
+
# :device_path => '/o/oauth2/device/code'
|
105
|
+
# })
|
106
|
+
# client.refresh_access_token({
|
107
|
+
# :state => '/profile',
|
108
|
+
# :refresh_token => '2YotnFZFEjr1zCsicMWpAA'
|
109
|
+
# })
|
110
|
+
# #=>
|
111
|
+
def refresh_access_token(opts={})
|
112
|
+
unless (opts[:params] && opts[:params][:refresh_token])
|
113
|
+
raise ArgumentError.new("You must provide a refresh_token as a parameter")
|
114
|
+
end
|
115
|
+
token = opts[:params].delete(:refresh_token)
|
116
|
+
refresh_token.get_token(token, opts)
|
117
|
+
end
|
118
|
+
|
119
|
+
# @see https://developers.google.com/accounts/docs/OAuth2ForDevices#obtainingacode
|
120
|
+
#
|
121
|
+
# @params [Hash] additional parameters to be include in URL eg. state
|
122
|
+
#
|
123
|
+
# client = GoogleClient.new('https://accounts.google.com', '827502413694.apps.googleusercontent.com', 'a2nQpcUm2Dgq1chWdAvbXGTk',{
|
124
|
+
# :token_path => '/o/oauth2/token',
|
125
|
+
# :authorize_path => '/o/oauth2/auth',
|
126
|
+
# :device_path => '/o/oauth2/device/code'
|
127
|
+
# })
|
128
|
+
# client.device_code({
|
129
|
+
# :state => '/profile',
|
130
|
+
# })
|
131
|
+
# #=>
|
132
|
+
def get_device_code(opts={})
|
133
|
+
opts[:params] ||= {}
|
134
|
+
opts[:params][:scope] = normalize_scope(opts[:params][:scope]) if opts[:params][:scope]
|
135
|
+
device_code.get_code(opts)
|
136
|
+
end
|
137
|
+
|
138
|
+
# @see https://developers.google.com/accounts/docs/OAuth2ForDevices#obtainingatoken
|
139
|
+
#
|
140
|
+
# @params [Hash] additional parameters to be include in URL eg. state
|
141
|
+
#
|
142
|
+
# client = GoogleClient.new('https://accounts.google.com', '827502413694.apps.googleusercontent.com', 'a2nQpcUm2Dgq1chWdAvbXGTk',{
|
143
|
+
# :token_path => '/o/oauth2/token',
|
144
|
+
# :authorize_path => '/o/oauth2/auth',
|
145
|
+
# :device_path => '/o/oauth2/device/code'
|
146
|
+
# })
|
147
|
+
# client.exchange_device_code_for_token({
|
148
|
+
# :state => '/profile',
|
149
|
+
# :code => 'G3Y6jU3a',
|
150
|
+
# })
|
151
|
+
# #=>
|
152
|
+
def exchange_device_code_for_token(opts={})
|
153
|
+
unless (opts[:params] && opts[:params][:code])
|
154
|
+
raise ArgumentError.new("You must include an device code as a parameter")
|
155
|
+
end
|
156
|
+
code = opts[:params].delete(:code)
|
157
|
+
device_code.get_token(code, opts)
|
158
|
+
end
|
159
|
+
end
|