oauth2-cli 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +70 -0
- data/bin/oauth2-cli +144 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 76187c5b942695b8ee0626735c7a762f76dba841
|
4
|
+
data.tar.gz: bf31d9f78aee657bfd091d96c40b81bf8bfa72a0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0dcb7367f6597f3311c5e07982e8655f234fc3100a7160193af810cc0e71c050d4b71de2589a14e2935d69fd2035e33115e9d8b6383b8db2c9ba27079603309c
|
7
|
+
data.tar.gz: e7bbd220f94a2e494eada6131b22481d8cef9db51ff7ff7311c229813c3686c8eb73533535a289cfa995fab884f097a4c7091a4ef6acb27311be4ac14746ef68
|
data/README.md
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
## oauth2-cli
|
2
|
+
|
3
|
+
Command line utility to get an OAuth access token for three-legged flows where you authorise an application to access your account. Inspired by [oauth2-cli](https://github.com/dcarley/oauth2-cli) written in Go.
|
4
|
+
|
5
|
+
The reason for rewriting this in Ruby was the difficulty to debug the OAuth2 flow when things don't go as expected. Go's OAuth2 library is pretty opaque and trying to figure out why for some services the client_id is not passed when sending the the token request after obtaining the authorisation code has been fruitless - even though sending the request via curl using the produced authorisation code proved successful.
|
6
|
+
|
7
|
+
The purpose for this tool is to obtain access and refresh tokens for applications which run as services (i.e daemons) where doing the OAuth2 flow is a bit difficult.
|
8
|
+
|
9
|
+
## Install
|
10
|
+
|
11
|
+
```bash
|
12
|
+
gem install oauth2-cli
|
13
|
+
```
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
For services which validate the callback URL, you must use `http://127.0.0.1:8000/oauth/callback` in your OAuth2 application. Bear in mind that `8000` is the default port which may be changed via CLI argument. Adapt as necessary.
|
18
|
+
|
19
|
+
The `oauth2-cli` script has a built in help:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
oauth2-cli -h
|
23
|
+
Usage: oauth2-cli --auth AUTHORISATION_URL --token TOKEN_URL --id CLIENT_ID --secret CLIENT_SECRET
|
24
|
+
|
25
|
+
-a, --auth AUTHORISATION_URL Authorisation URL (required)
|
26
|
+
-t, --token TOKEN_URL Token URL (required)
|
27
|
+
-i, --id CLIENT_ID Client ID (required)
|
28
|
+
-s, --secret CLIENT_SECRET Client secret (required)
|
29
|
+
-o, --scope SCOPE1,SCOPE2,etc OAuth2 scope to authorise (not used if not specified)
|
30
|
+
-e, --separator OAuth2 scope separator character (defaults to space) n.b the scope arg is always passed as array and joined with the separator char for the request
|
31
|
+
-p, --port 8000 Callback port (defaults to 8000)
|
32
|
+
-d, --debug Turn on OAuth2 library debug and WEBrick log
|
33
|
+
```
|
34
|
+
|
35
|
+
The scope separator character has been implemented for services which interpret the OAuth2 spec a tad different - for example Facebook is using comma separated values for scope.
|
36
|
+
|
37
|
+
## Examples
|
38
|
+
|
39
|
+
```bash
|
40
|
+
# request tokens for accessing Netatmo Weather station
|
41
|
+
oauth2-cli -a https://api.netatmo.com/oauth2/authorize -t https://api.netatmo.com/oauth2/token -i CLIENT_ID -s CLIENT_SECRET -o read_station
|
42
|
+
|
43
|
+
Go to URL: https://api.netatmo.com/oauth2/authorize?access_type=offline&client_id=CLIENT_ID&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000%2Foauth%2Fcallback&response_type=code&scope=read_station&state=ewcxkqpsfrinhgvyamzbouljtd
|
44
|
+
|
45
|
+
Starting server - use Ctrl+C to stop
|
46
|
+
|
47
|
+
{"scope"=>["read_station"],
|
48
|
+
"expire_in"=>10800,
|
49
|
+
:access_token=>"ACCESS_TOKEN",
|
50
|
+
:refresh_token=>"REFRESH_TOKEN",
|
51
|
+
:expires_at=>TIMESTAMP}
|
52
|
+
|
53
|
+
^C
|
54
|
+
|
55
|
+
# request tokens for accessing Awair air-quality monitor
|
56
|
+
oauth2-cli -a https://oauth-login.awair.is -t https://oauth2.awair.is/v2/token -i CLIENT_ID -s CLIENT_SECRET
|
57
|
+
|
58
|
+
Go to URL: https://oauth-login.awair.is?access_type=offline&client_id=CLIENT_ID&redirect_uri=http%3A%2F%2F127.0.0.1%3A8000%2Foauth%2Fcallback&response_type=code&state=jtiznuypwqfhbvmradlgkeoxcs
|
59
|
+
|
60
|
+
Starting server - use Ctrl+C to stop
|
61
|
+
|
62
|
+
{"token_type"=>"Bearer",
|
63
|
+
:access_token=>"ACCESS_TOKEN",
|
64
|
+
:refresh_token=>"REFRESH_TOKEN",
|
65
|
+
:expires_at=>nil}
|
66
|
+
|
67
|
+
^C
|
68
|
+
```
|
69
|
+
|
70
|
+
The output hash from the token request may be used by Ruby's [OAuth2](https://github.com/oauth-xx/oauth2) library to recreate an `AccessToken` object using the `from_hash` class method. You have to implement your own token refresh capabilities when the access token expires for the OAuth2 library doesn't provide such functionality.
|
data/bin/oauth2-cli
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require 'oauth2'
|
5
|
+
require 'webrick'
|
6
|
+
require 'optparse'
|
7
|
+
|
8
|
+
options = {}
|
9
|
+
required = []
|
10
|
+
|
11
|
+
src = File.basename($PROGRAM_NAME)
|
12
|
+
state = ('a'..'z').to_a.sample(32).join
|
13
|
+
path = '/oauth/callback'
|
14
|
+
|
15
|
+
optp = OptionParser.new do |opts|
|
16
|
+
opts.banner = "Usage: #{src} --auth AUTHORISATION_URL --token TOKEN_URL "\
|
17
|
+
"--id CLIENT_ID --secret CLIENT_SECRET\n\n"
|
18
|
+
|
19
|
+
required << :auth
|
20
|
+
desc_auth = 'Authorisation URL (required)'
|
21
|
+
opts.on('-a', '--auth AUTHORISATION_URL', desc_auth) do |opt|
|
22
|
+
options[:auth] = opt
|
23
|
+
end
|
24
|
+
|
25
|
+
required << :token
|
26
|
+
opts.on('-t', '--token TOKEN_URL', 'Token URL (required)') do |opt|
|
27
|
+
options[:token] = opt
|
28
|
+
end
|
29
|
+
|
30
|
+
required << :id
|
31
|
+
opts.on('-i', '--id CLIENT_ID', 'Client ID (required)') do |opt|
|
32
|
+
options[:id] = opt
|
33
|
+
end
|
34
|
+
|
35
|
+
required << :secret
|
36
|
+
opts.on('-s', '--secret CLIENT_SECRET', 'Client secret (required)') do |opt|
|
37
|
+
options[:secret] = opt
|
38
|
+
end
|
39
|
+
|
40
|
+
options[:scope] = []
|
41
|
+
desc_scope = 'OAuth2 scope to authorise (not used if not specified)'
|
42
|
+
opts.on('-o', '--scope SCOPE1,SCOPE2,etc', Array, desc_scope) do |opt|
|
43
|
+
options[:scope] = opt
|
44
|
+
end
|
45
|
+
|
46
|
+
options[:separator] = ' '
|
47
|
+
desc_separator = 'OAuth2 scope separator character (defaults to space) n.b '\
|
48
|
+
'the scope arg is always passed as array and joined with the separator '\
|
49
|
+
'char for the request'
|
50
|
+
opts.on('-e', '--separator', desc_separator) do |opt|
|
51
|
+
options[:separator] = opt
|
52
|
+
end
|
53
|
+
|
54
|
+
options[:port] = 8000
|
55
|
+
desc_port = 'Callback port (defaults to 8000)'
|
56
|
+
opts.on('-p', '--port 8000', Integer, desc_port) do |opt|
|
57
|
+
options[:port] = opt
|
58
|
+
end
|
59
|
+
|
60
|
+
options[:debug] = false
|
61
|
+
opts.on('-d', '--debug', 'Turn on OAuth2 library debug and WEBrick log') do
|
62
|
+
ENV['OAUTH_DEBUG'] = 'true'
|
63
|
+
options[:debug] = true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
optp.parse!
|
68
|
+
|
69
|
+
required.each do |arg|
|
70
|
+
next unless options[arg].nil?
|
71
|
+
|
72
|
+
STDERR.puts "Error: missing required argument #{arg}. "\
|
73
|
+
"See #{src} -h for help."
|
74
|
+
exit 1
|
75
|
+
end
|
76
|
+
|
77
|
+
redirect_uri = "http://127.0.0.1:#{options[:port]}#{path}"
|
78
|
+
client = OAuth2::Client.new(
|
79
|
+
options[:id],
|
80
|
+
options[:secret],
|
81
|
+
authorize_url: options[:auth],
|
82
|
+
token_url: options[:token]
|
83
|
+
)
|
84
|
+
|
85
|
+
code_args = {
|
86
|
+
access_type: 'offline',
|
87
|
+
redirect_uri: redirect_uri,
|
88
|
+
state: state
|
89
|
+
}
|
90
|
+
|
91
|
+
unless options[:scope].empty?
|
92
|
+
code_args[:scope] = options[:scope].join(options[:separator])
|
93
|
+
end
|
94
|
+
|
95
|
+
url = client.auth_code.authorize_url(code_args)
|
96
|
+
|
97
|
+
puts ''
|
98
|
+
puts "Go to URL: #{url}"
|
99
|
+
puts ''
|
100
|
+
|
101
|
+
puts 'Starting server - use Ctrl+C to stop'
|
102
|
+
puts ''
|
103
|
+
|
104
|
+
server_options = {
|
105
|
+
Port: options[:port]
|
106
|
+
}
|
107
|
+
|
108
|
+
unless options[:debug]
|
109
|
+
server_options[:Logger] = WEBrick::Log.new(File.open(File::NULL, 'w'))
|
110
|
+
server_options[:AccessLog] = []
|
111
|
+
end
|
112
|
+
|
113
|
+
server = WEBrick::HTTPServer.new(server_options)
|
114
|
+
|
115
|
+
server.mount_proc('/') do |req, res|
|
116
|
+
unless req.path == path
|
117
|
+
res.status = 403
|
118
|
+
res.body = "Invalid callback path - expecting #{path}"
|
119
|
+
next
|
120
|
+
end
|
121
|
+
|
122
|
+
unless req.query['state'] == state
|
123
|
+
res.status = 400
|
124
|
+
res.body = 'Invalid state in callback'
|
125
|
+
next
|
126
|
+
end
|
127
|
+
|
128
|
+
token = client.auth_code.get_token(
|
129
|
+
req.query['code'],
|
130
|
+
redirect_uri: redirect_uri
|
131
|
+
)
|
132
|
+
|
133
|
+
pp token.to_hash
|
134
|
+
puts ''
|
135
|
+
|
136
|
+
res.status = 200
|
137
|
+
res.body = 'You may now close this tab'
|
138
|
+
end
|
139
|
+
|
140
|
+
trap('INT') do
|
141
|
+
server.shutdown
|
142
|
+
end
|
143
|
+
|
144
|
+
server.start
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: oauth2-cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ștefan Rusu
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2019-01-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: oauth2
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: jeweler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.62'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.62'
|
55
|
+
description: CLI utility to get OAuth token for three-leggged flows
|
56
|
+
email: saltwaterc@gmail.com
|
57
|
+
executables:
|
58
|
+
- oauth2-cli
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files:
|
61
|
+
- README.md
|
62
|
+
files:
|
63
|
+
- README.md
|
64
|
+
- bin/oauth2-cli
|
65
|
+
homepage: https://github.com/SaltwaterC/oauth2-cli
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.6.14
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: CLI utility to get OAuth token
|
89
|
+
test_files: []
|