haveapi 0.16.3 → 0.18.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ece538baf23fb6e02ef9ad1063930cc7b973e94fc530e4f96fa87f0e19ecd269
4
- data.tar.gz: 73b6713727754b11ce89e3c61017b4374ced36f50c740c9594f757d11685e640
3
+ metadata.gz: fedb085c210494a3173ef597e44f63a280485cc0490bd0c0b74610d26d52e5b4
4
+ data.tar.gz: 372f4a9cb544426ad1e06c3bb60e26a50077b5e3bef70e7b77ac008ca5008aaa
5
5
  SHA512:
6
- metadata.gz: 6db1b965c6fac2ad2669709397e81d028e2e9828707dc6727b65b70cf0ceea5d8c8d38be82659a6c959687336529345b873c6fe2f81b779b83f96a86fe27c5c9
7
- data.tar.gz: a6e64201797808898c504837f4f867bb0f017735e1b6f5558414026e78fb637089482e1479525ba7d1f1885d3c72eb3f165239b3acc1d164de702ef8762c26f1
6
+ metadata.gz: 6ab713fd64ead24d6a21095bda69ddbc536da56fdd26745c0c60721a8c9e5767addafdf7906a3c98a23401472106e83f8d341903291e4c5e3b1c08974c3f12b3
7
+ data.tar.gz: 58b3cfc406d09868675f07b51050c5b914368c1c4ee70dbd9cf919a6f2b7e9124133e0f6f33292822f69e614cd1090e6f605ba5179d0c98802d4b1023e09cc6e
data/haveapi.gemspec CHANGED
@@ -16,13 +16,14 @@ Gem::Specification.new do |s|
16
16
 
17
17
  s.add_runtime_dependency 'require_all', '~> 2.0.0'
18
18
  s.add_runtime_dependency 'json'
19
- s.add_runtime_dependency 'activesupport', '>= 6.0'
20
- s.add_runtime_dependency 'sinatra', '~> 3.0.4'
21
- s.add_runtime_dependency 'tilt', '~> 2.0.10'
22
- s.add_runtime_dependency 'redcarpet', '~> 3.5'
19
+ s.add_runtime_dependency 'activesupport', '>= 7.0'
20
+ s.add_runtime_dependency 'sinatra', '~> 3.1.0'
21
+ s.add_runtime_dependency 'tilt', '~> 2.3.0'
22
+ s.add_runtime_dependency 'redcarpet', '~> 3.6'
23
23
  s.add_runtime_dependency 'rake'
24
24
  s.add_runtime_dependency 'github-markdown'
25
25
  s.add_runtime_dependency 'nesty', '~> 1.0'
26
- s.add_runtime_dependency 'haveapi-client', '~> 0.16.3'
26
+ s.add_runtime_dependency 'haveapi-client', '~> 0.18.0'
27
27
  s.add_runtime_dependency 'mail'
28
+ s.add_runtime_dependency 'rack-oauth2', '~> 2.2.0'
28
29
  end
@@ -13,6 +13,10 @@ module HaveAPI
13
13
  end
14
14
  end
15
15
 
16
+ def self.inherited(subclass)
17
+ subclass.send(:instance_variable_set, '@auth_method', @auth_method)
18
+ end
19
+
16
20
  # @return [Symbol]
17
21
  attr_reader :name
18
22
 
@@ -23,6 +27,12 @@ module HaveAPI
23
27
  setup
24
28
  end
25
29
 
30
+ # Register custom path handlers in sinatra
31
+ # @param sinatra [Sinatra::Base]
32
+ # @param prefix [String]
33
+ def register_routes(sinatra, prefix)
34
+ end
35
+
26
36
  # @return [Module, nil]
27
37
  def resource_module
28
38
  nil
@@ -99,6 +99,8 @@ module HaveAPI::Authentication
99
99
  instance = p.new(@server, v)
100
100
  @instances[v] << instance
101
101
 
102
+ @server.add_auth_routes(v, instance, prefix: instance.name.to_s)
103
+
102
104
  if resource_module = instance.resource_module
103
105
  @server.add_auth_module(
104
106
  v,
@@ -0,0 +1,143 @@
1
+ module HaveAPI::Authentication
2
+ module OAuth2
3
+ # Config passed to the OAuth2 provider
4
+ #
5
+ # Create your own subclass and pass it to {HaveAPI::Authentication::OAuth2.with_config}.
6
+ # The created provider can then be added to authentication chain.
7
+ #
8
+ # In general, it is up to the implementation to provide the authentication flow
9
+ # -- render HTML page in {#render_authorize_page} and then process it in
10
+ # {#handle_post_authorize}. The implementation must also handle generation
11
+ # of all needed tokens, their persistence and validity checking.
12
+ class Config
13
+ def initialize(provider, server, v)
14
+ @provider = provider
15
+ @server = server
16
+ @version = v
17
+ end
18
+
19
+ # Render authorization page
20
+ #
21
+ # This method can be called on both GET and POST requests, e.g. if the user
22
+ # provided incorrect credentials or if there are multiple authentication
23
+ # steps.
24
+ #
25
+ # It should return full HTML page that will be sent to the user. The page
26
+ # usually contains a login form.
27
+ #
28
+ # @param oauth2_request [Rack::OAuth2::Server::Authorize::Request]
29
+ # @param sinatra_params [Hash] request params
30
+ # @param client [Client]
31
+ # @param auth_result [AuthResult, nil]
32
+ # @return [String] HTML
33
+ def render_authorize_page(oauth2_request, sinatra_params, client, auth_result: nil)
34
+
35
+ end
36
+
37
+ # Handle POST requests made from {#render_authorize_page}
38
+ #
39
+ # Process form data and return {AuthResult} or nil. When nil is returned
40
+ # the authorization process is aborted and the user is redirected back
41
+ # to the client.
42
+ #
43
+ # @param sinatra_request [Sinatra::Request]
44
+ # @param sinatra_params [Hash] request params
45
+ # @param oauth2_request [Rack::OAuth2::Server::Authorize::Request]
46
+ # @param client [Client]
47
+ # @return [AuthResult, nil]
48
+ def handle_post_authorize(sinatra_request, sinatra_params, oauth2_request, client)
49
+
50
+ end
51
+
52
+ # Get oauth2 authorization code
53
+ #
54
+ # Called when the authentication is successful and complete. This method
55
+ # must generate and return authorization_code which is then sent to the
56
+ # client. It is up to the API implementation to persist the code.
57
+ #
58
+ # @param auth_res [AuthResult] value returned by {#handle_post_authorize}
59
+ # @return [String]
60
+ def get_authorization_code(auth_res)
61
+
62
+ end
63
+
64
+ # Get access token, its expiration date and optionally a refresh token
65
+ #
66
+ # The client has used the authorization_code returned by {#get_authorization_code}
67
+ # and now requests its access token. It is up to the implementation to create
68
+ # and persist the tokens. The authorization code should be invalidated.
69
+ #
70
+ # @param authorization [Authorization]
71
+ # @param sinatra_request [Sinatra::Request]
72
+ # @return [Array] access token, expiration date and optional refresh token
73
+ def get_tokens(authorization, sinatra_request)
74
+
75
+ end
76
+
77
+ # Refresh access token and optionally generate new refresh token
78
+ #
79
+ # The implementation should invalidate the current tokens and generate
80
+ # and persist new ones.
81
+ #
82
+ # @param authorization [Authorization]
83
+ # @param sinatra_request [Sinatra::Request]
84
+ # @return [Array] access token, expiration date and optional refresh token
85
+ def refresh_tokens(authorization, sinatra_request)
86
+
87
+ end
88
+
89
+ # Find client by ID
90
+ # @param client_id [String]
91
+ # @return [Client, nil]
92
+ def find_client_by_id(client_id)
93
+
94
+ end
95
+
96
+ # Find authorization by code
97
+ # @param client [Client]
98
+ # @param code [String]
99
+ # @return [Authorization, nil]
100
+ def find_authorization_by_code(client, code)
101
+
102
+ end
103
+
104
+ # Find authorization by refresh token
105
+ # @param client [Client]
106
+ # @param refresh_token [String]
107
+ # @return [Authorization, nil]
108
+ def find_authorization_by_refresh_token(client, refresh_token)
109
+
110
+ end
111
+
112
+ # Find user by the bearer token sent in HTTP header or as a query parameter
113
+ # @param sinatra_request [Sinatra::Request]
114
+ # @param access_token [String]
115
+ # @return [Object, nil] user
116
+ def find_user_by_access_token(request, access_token)
117
+
118
+ end
119
+
120
+ # Path to the authorization endpoint on this API
121
+ # @return [String]
122
+ def authorize_path
123
+ @provider.authorize_path
124
+ end
125
+
126
+ # Parameters needed for the authorization process
127
+ #
128
+ # Use these in {#render_authorization_page}, put them e.g. in hidden form
129
+ # fields.
130
+ #
131
+ # @return [Hash<String, String>]
132
+ def oauth2_params(req)
133
+ {
134
+ client_id: req.client_id,
135
+ response_type: req.response_type,
136
+ redirect_uri: req.redirect_uri,
137
+ scope: req.scope.join(' '),
138
+ state: req.state,
139
+ }
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,231 @@
1
+ require 'haveapi/authentication/base'
2
+ require 'rack/oauth2'
3
+
4
+ module HaveAPI::Authentication
5
+ module OAuth2
6
+ # Abstract class describing the client and what methods it must respond to
7
+ class Client
8
+ # @return [String]
9
+ attr_reader :client_id
10
+
11
+ # @return [String]
12
+ attr_reader :redirect_uri
13
+
14
+ # @param client_secret [String]
15
+ # @return [Boolean]
16
+ def check_secret(client_secret)
17
+ raise NotImplementedError
18
+ end
19
+ end
20
+
21
+ # Abstract class describing the authentication result and what methods it must respond to
22
+ class AuthResult
23
+ # True of the user was authenticated
24
+ # @return [Boolean]
25
+ attr_reader :authenticated
26
+
27
+ # True if the authentication process is complete, false if other steps are needed
28
+ # @return [Boolean]
29
+ attr_reader :complete
30
+
31
+ # True if the user asked to cancel the authorization process
32
+ # @return [Boolean]
33
+ attr_reader :cancel
34
+ end
35
+
36
+ # Abstract class describing ongoing authorization and what methods it must respond to
37
+ class Authorization
38
+ # @return [String]
39
+ attr_reader :redirect_uri
40
+
41
+ # @param redirect_uri [String]
42
+ # @return [Boolean]
43
+ def check_code_validity(redirect_uri)
44
+ raise NotImplementedError
45
+ end
46
+ end
47
+
48
+ # OAuth2 authentication and authorization provider
49
+ #
50
+ # Must be configured with {Config} using {OAuth2.with_config}.
51
+ class Provider < Base
52
+ auth_method :oauth2
53
+
54
+ # Configure the OAuth2 provider
55
+ # @param cfg [Config]
56
+ def self.with_config(cfg)
57
+ Module.new do
58
+ define_singleton_method(:new) do |*args|
59
+ Provider.new(*args, cfg)
60
+ end
61
+ end
62
+ end
63
+
64
+ # @return [String]
65
+ attr_reader :authorize_path
66
+
67
+ # @return [Config]
68
+ attr_reader :config
69
+
70
+ def initialize(server, v, cfg)
71
+ @config = cfg.new(self, server, v)
72
+ super(server, v)
73
+ end
74
+
75
+ def register_routes(sinatra, prefix)
76
+ @authorize_path = File.join(prefix, 'authorize')
77
+ @token_path = File.join(prefix, 'token')
78
+ that = self
79
+
80
+ sinatra.get @authorize_path do
81
+ that.authorization_endpoint(self).call(request.env)
82
+ end
83
+
84
+ sinatra.post @authorize_path do
85
+ that.authorization_endpoint(self).call(request.env)
86
+ end
87
+
88
+ sinatra.post @token_path do
89
+ that.token_endpoint(self).call(request.env)
90
+ end
91
+ end
92
+
93
+ def authenticate(request)
94
+ tokens = [
95
+ request['access_token'],
96
+ token_from_header(request)
97
+ ].compact
98
+
99
+ token =
100
+ case tokens.length
101
+ when 0
102
+ nil
103
+ when 1
104
+ tokens.first
105
+ else
106
+ fail 'Too many oauth2 tokens'
107
+ end
108
+
109
+ token && config.find_user_by_access_token(request, token)
110
+ end
111
+
112
+ def token_from_header(request)
113
+ auth_header = Rack::Auth::AbstractRequest.new(request.env)
114
+
115
+ if auth_header.provided? && !auth_header.parts.first.nil? && auth_header.scheme.to_s == 'bearer'
116
+ auth_header.params
117
+ else
118
+ nil
119
+ end
120
+ end
121
+
122
+ def describe
123
+ desc = <<-END
124
+ OAuth2 authorization provider. While OAuth2 is not supported by HaveAPI
125
+ clients, it is possible to use your API as an authentication source.
126
+
127
+ HaveAPI partially implements RFC 6749: authorization response type "code"
128
+ and token grant types "authorization_code" and "refresh_token". Other
129
+ response and grant types are not supported at this time.
130
+
131
+ The access token can be passed as bearer token according to RFC 6750.
132
+ END
133
+
134
+ {
135
+ description: desc,
136
+ authorize_path: @authorize_path,
137
+ token_path: @token_path,
138
+ }
139
+ end
140
+
141
+ def authorization_endpoint(handler)
142
+ Rack::OAuth2::Server::Authorize.new do |req, res|
143
+ client = config.find_client_by_id(req.client_id)
144
+ req.bad_request! if client.nil?
145
+
146
+ res.redirect_uri = req.verify_redirect_uri!(client.redirect_uri)
147
+
148
+ if req.post?
149
+ auth_res = config.handle_post_authorize(handler.request, handler.params, req, client)
150
+
151
+ if auth_res.nil?
152
+ # Authentication failed
153
+ req.access_denied!
154
+ elsif auth_res.cancel
155
+ # Cancel the process
156
+ req.access_denied!
157
+ elsif auth_res.authenticated && auth_res.complete
158
+ # Authentication was successful
159
+ case req.response_type
160
+ when :code
161
+ res.code = config.get_authorization_code(auth_res)
162
+ when :token
163
+ req.unsupported_response_type!
164
+ end
165
+
166
+ res.approve!
167
+ elsif auth_res.authenticated && !auth_res.complete
168
+ # Continue with another authentication step
169
+ res.content_type = 'text/html'
170
+ res.write(config.render_authorize_page(req, handler.params, client, auth_result: auth_res))
171
+ else
172
+ # Authentication failed, report errors and let the user retry
173
+ res.content_type = 'text/html'
174
+ res.write(config.render_authorize_page(req, handler.params, client, auth_result: auth_res))
175
+ end
176
+ else
177
+ res.content_type = 'text/html'
178
+ res.write(config.render_authorize_page(req, handler.params, client))
179
+ end
180
+ end
181
+ end
182
+
183
+ def token_endpoint(handler)
184
+ Rack::OAuth2::Server::Token.new do |req, res|
185
+ client = config.find_client_by_id(req.client_id)
186
+ req.invalid_client! if client.nil? || !client.check_secret(req.client_secret)
187
+
188
+ res.access_token =
189
+ case req.grant_type
190
+ when :authorization_code
191
+ authorization = config.find_authorization_by_code(client, req.code)
192
+
193
+ if authorization.nil? || authorization.check_code_validity(req.redirect_uri)
194
+ req.invalid_grant!
195
+ end
196
+
197
+ access_token, expires_at, refresh_token = config.get_tokens(authorization, handler.request)
198
+
199
+ bearer_token = Rack::OAuth2::AccessToken::Bearer.new(
200
+ access_token: access_token,
201
+ expires_in: expires_at - Time.now,
202
+ )
203
+ bearer_token.refresh_token = refresh_token if refresh_token
204
+ bearer_token
205
+
206
+ when :password
207
+ req.unsupported_grant_type!
208
+
209
+ when :client_credentials
210
+ req.unsupported_grant_type!
211
+
212
+ when :refresh_token
213
+ config.find_authorization_by_refresh_token(client, req.refresh_token)
214
+
215
+ access_token, expires_at, refresh_token = config.refresh_tokens(authorization, handler.request)
216
+
217
+ bearer_token = Rack::OAuth2::AccessToken::Bearer.new(
218
+ access_token: access_token,
219
+ expires_in: expires_at - Time.now,
220
+ )
221
+ bearer_token.refresh_token = refresh_token if refresh_token
222
+ bearer_token
223
+
224
+ else
225
+ req.unsupported_grant_type!
226
+ end
227
+ end
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,9 @@
1
+ module HaveAPI::Authentication
2
+ module OAuth2
3
+ # Configure the oauth2 provider
4
+ # @param cfg [Config]
5
+ def self.with_config(cfg)
6
+ Provider.with_config(cfg)
7
+ end
8
+ end
9
+ end
@@ -79,5 +79,28 @@ module HaveAPI
79
79
  def version_url
80
80
  File.join(base_url, "v#{version}", '/')
81
81
  end
82
+
83
+ protected
84
+ # @param password [Boolean] include password parameter
85
+ # @return [Hash<String, String>] parameter => example value
86
+ def auth_token_credentials(desc, password: true)
87
+ passwords = %i(password pass passwd)
88
+ params = desc[:resources][:token][:actions]['request'][:input][:parameters].keys - %i(lifetime interval)
89
+
90
+ unless password
91
+ params.reject! { |param| passwords.include?(param) }
92
+ end
93
+
94
+ Hash[params.map do |param|
95
+ value =
96
+ if passwords.include?(param)
97
+ 'secret'
98
+ else
99
+ param
100
+ end
101
+
102
+ [param, value]
103
+ end]
104
+ end
82
105
  end
83
106
  end
@@ -12,8 +12,6 @@ module HaveAPI::ClientExamples
12
12
  end
13
13
 
14
14
  def auth(method, desc)
15
- login = {user: 'user', password: 'password', lifetime: 'fixed'}
16
-
17
15
  case method
18
16
  when :basic
19
17
  <<END
@@ -30,6 +28,8 @@ $ curl --request OPTIONS \\
30
28
  END
31
29
 
32
30
  when :token
31
+ login = auth_token_credentials(desc).merge(lifetime: 'fixed')
32
+
33
33
  <<END
34
34
  # Acquire the token
35
35
  $ curl --request POST \\
@@ -42,6 +42,9 @@ $ curl --request OPTIONS \\
42
42
  --header '#{desc[:http_header]}: thetoken' \\
43
43
  '#{base_url}'
44
44
  END
45
+
46
+ when :oauth2
47
+ '# See RFC 6749 for authorization process and RFC 6750 for access token usage.'
45
48
  end
46
49
  end
47
50
 
@@ -34,6 +34,9 @@ Password: secret
34
34
  # Note that the file system can read config file from haveapi-client, so if
35
35
  # you set up authentication there, the file system will use it.
36
36
  END
37
+
38
+ when :oauth2
39
+ '# OAuth2 is not supported by haveapi-fs.'
37
40
  end
38
41
  end
39
42
 
@@ -28,12 +28,33 @@ Authorization: Basic dXNlcjpzZWNyZXQ=
28
28
  END
29
29
 
30
30
  when :token
31
+ login = auth_token_credentials(desc).merge(lifetime: 'fixed')
32
+
31
33
  <<END
32
34
  POST /_auth/token/tokens HTTP/1.1
33
35
  Host: #{host}
34
36
  Content-Type: application/json
35
37
 
36
- #{JSON.pretty_generate({token: {user: 'user', password: 'secret', lifetime: 'fixed'}})}
38
+ #{JSON.pretty_generate({token: login})}
39
+ END
40
+
41
+ when :oauth2
42
+ <<END
43
+ # 1) Request authorization code
44
+ GET #{desc[:authorize_path]}?response_type=code&client_id=$client_id&state=$state&redirect_uri=$client_redirect_uri HTTP/1.1
45
+ Host: #{host}
46
+
47
+ # 2) The user logs in using this API
48
+
49
+ # 3) The API then redirects the user back to the client application
50
+ GET $client_redirect_uri?code=$authorization_code&state=$state
51
+ Host: client-application
52
+
53
+ # 4) The client application requests access token
54
+ POST #{desc[:token_path]}
55
+ Content-Type: application/x-www-form-urlencoded
56
+
57
+ grant_type=authorization_code&code=$authorization_code&redirect_uri=$client_redirect_uri&client_id=$client_id&client_secret=$client_secret
37
58
  END
38
59
  end
39
60
  end
@@ -21,7 +21,7 @@ END
21
21
  #{init}
22
22
 
23
23
  api.authenticate("basic", {
24
- username: "user",
24
+ user: "user",
25
25
  password: "secret"
26
26
  }, function (client, status) {
27
27
  console.log("Authenticated?", status);
@@ -34,8 +34,7 @@ END
34
34
 
35
35
  // Request a new token
36
36
  api.authenticate("token", {
37
- username: "user",
38
- password: "secret"
37
+ #{auth_token_credentials(desc).map { |k, v| "#{k}: \"#{v}\"" }.join(",\n ")}
39
38
  }, function (client, status) {
40
39
  console.log("Authenticated?", status);
41
40
 
@@ -50,6 +49,9 @@ api.authenticate("token", {
50
49
  console.log("Authenticated?", status);
51
50
  });
52
51
  END
52
+
53
+ when :oauth2
54
+ '// OAuth2 is not supported by HaveAPI JavaScript client.'
53
55
  end
54
56
  end
55
57
 
@@ -18,7 +18,7 @@ END
18
18
  <<END
19
19
  #{init}
20
20
 
21
- $api->authenticate("basic", ["username" => "user", "password" => "secret"]);
21
+ $api->authenticate("basic", ["user" => "user", "password" => "secret"]);
22
22
  END
23
23
 
24
24
  when :token
@@ -26,13 +26,16 @@ END
26
26
  #{init}
27
27
 
28
28
  // Get token using username and password
29
- $api->authenticate("token", ["username" => "user", "password" => "secret"]);
29
+ $api->authenticate("token", [#{auth_token_credentials(desc).map { |k, v| "\"#{k}\" => \"#{v}\"" }.join(', ')}]);
30
30
 
31
31
  echo "Token = ".$api->getAuthenticationProvider()->getToken();
32
32
 
33
33
  // Next time, the client can authenticate using the token directly
34
34
  $api->authenticate("token", ["token" => $savedToken]);
35
35
  END
36
+
37
+ when :oauth2
38
+ '// OAuth2 is not supported by HaveAPI PHP client.'
36
39
  end
37
40
  end
38
41
 
@@ -20,10 +20,10 @@ module HaveAPI::ClientExamples
20
20
  when :basic
21
21
  <<END
22
22
  # Provide credentials on command line
23
- #{init} --auth basic --username user --password secret
23
+ #{init} --auth basic --user user --password secret
24
24
 
25
25
  # If username or password isn't provided, the user is asked on stdin
26
- #{init} --auth basic --username user
26
+ #{init} --auth basic --user user
27
27
  Password: secret
28
28
  END
29
29
 
@@ -33,13 +33,16 @@ END
33
33
  # Note that the client always has to call some action. APIs should provide
34
34
  # an action to get information about the current user, so that's what we're
35
35
  # calling now.
36
- #{init} --auth token --username user --save user current
36
+ #{init} --auth token #{auth_token_credentials(desc, password: false).map { |k, v| "--#{k} #{v}" }.join(' ')} --save user current
37
37
  Password: secret
38
38
 
39
39
  # Now the token is read from disk and the user does not have to provide username
40
40
  # nor password and be authenticated
41
41
  #{init} user current
42
42
  END
43
+
44
+ when :oauth2
45
+ '# OAuth2 is not supported by HaveAPI Ruby CLI.'
43
46
  end
44
47
  end
45
48
 
@@ -21,7 +21,7 @@ END
21
21
  <<END
22
22
  #{init}
23
23
 
24
- client.authenticate(:basic, username: "user", password: "secret")
24
+ client.authenticate(:basic, user: "user", password: "secret")
25
25
  END
26
26
 
27
27
  when :token
@@ -29,13 +29,16 @@ END
29
29
  #{init}
30
30
 
31
31
  # Get token using username and password
32
- client.authenticate(:token, username: "user", password: "secret")
32
+ client.authenticate(:token, #{auth_token_credentials(desc).map { |k, v| "#{k}: \"#{v}\"" }.join(', ')})
33
33
 
34
34
  puts "Token = \#{client.auth.token}"
35
35
 
36
36
  # Next time, the client can authenticate using the token directly
37
37
  client.authenticate(:token, token: saved_token)
38
38
  END
39
+
40
+ when :oauth2
41
+ '# OAuth2 is not supported by HaveAPI Ruby client.'
39
42
  end
40
43
  end
41
44
 
@@ -576,6 +576,13 @@ module HaveAPI
576
576
  "#{@root}v#{v}/"
577
577
  end
578
578
 
579
+ # @param v [String] API version
580
+ # @param provider [Authentication::Base]
581
+ # @param prefix [String]
582
+ def add_auth_routes(v, provider, prefix: '')
583
+ provider.register_routes(@sinatra, "#{@root}_auth/#{prefix}")
584
+ end
585
+
579
586
  def add_auth_module(v, name, mod, prefix: '')
580
587
  @routes[v] ||= {authentication: {name => {resources: {}}}}
581
588
 
@@ -1,4 +1,4 @@
1
1
  module HaveAPI
2
2
  PROTOCOL_VERSION = '2.0'
3
- VERSION = '0.16.3'
3
+ VERSION = '0.18.0'
4
4
  end
@@ -10,6 +10,13 @@
10
10
  <dt>Query parameter:</dt>
11
11
  <dd><%= info[:query_parameter] %></dd>
12
12
  </dl>
13
+ <% elsif name == :oauth2 %>
14
+ <dl>
15
+ <dt>Authorize path:</dt>
16
+ <dd><%= info[:authorize_path] %></dd>
17
+ <dt>Token path:</dt>
18
+ <dd><%= info[:token_path] %></dd>
19
+ </dl>
13
20
  <% end %>
14
21
 
15
22
  <% if info[:resources] %>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: haveapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.3
4
+ version: 0.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Skokan
@@ -44,56 +44,56 @@ dependencies:
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '6.0'
47
+ version: '7.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '6.0'
54
+ version: '7.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: sinatra
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 3.0.4
61
+ version: 3.1.0
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 3.0.4
68
+ version: 3.1.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: tilt
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 2.0.10
75
+ version: 2.3.0
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 2.0.10
82
+ version: 2.3.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: redcarpet
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '3.5'
89
+ version: '3.6'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '3.5'
96
+ version: '3.6'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +142,14 @@ dependencies:
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: 0.16.3
145
+ version: 0.18.0
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: 0.16.3
152
+ version: 0.18.0
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: mail
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rack-oauth2
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - "~>"
172
+ - !ruby/object:Gem::Version
173
+ version: 2.2.0
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - "~>"
179
+ - !ruby/object:Gem::Version
180
+ version: 2.2.0
167
181
  description: Framework for creating self-describing APIs
168
182
  email: jakub.skokan@vpsfree.cz
169
183
  executables: []
@@ -193,6 +207,9 @@ files:
193
207
  - lib/haveapi/authentication/base.rb
194
208
  - lib/haveapi/authentication/basic/provider.rb
195
209
  - lib/haveapi/authentication/chain.rb
210
+ - lib/haveapi/authentication/oauth2.rb
211
+ - lib/haveapi/authentication/oauth2/config.rb
212
+ - lib/haveapi/authentication/oauth2/provider.rb
196
213
  - lib/haveapi/authentication/token.rb
197
214
  - lib/haveapi/authentication/token/action_config.rb
198
215
  - lib/haveapi/authentication/token/action_request.rb
@@ -315,7 +332,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
315
332
  - !ruby/object:Gem::Version
316
333
  version: '0'
317
334
  requirements: []
318
- rubygems_version: 3.3.20
335
+ rubygems_version: 3.4.13
319
336
  signing_key:
320
337
  specification_version: 4
321
338
  summary: Framework for creating self-describing APIs