haveapi 0.16.3 → 0.18.0

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