Class: HaveAPI::Authentication::OAuth2::Provider

Inherits:
Base
  • Object
show all
Defined in:
lib/haveapi/authentication/oauth2/provider.rb

Overview

OAuth2 authentication and authorization provider

Must be configured with Config using with_config.

Instance Attribute Summary collapse

Attributes inherited from Base

#name

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Base

auth_method, inherited, #resource_module

Constructor Details

#initialize(server, v, cfg) ⇒ Provider

Returns a new instance of Provider.



70
71
72
73
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 70

def initialize(server, v, cfg)
  @config = cfg.new(self, server, v)
  super(server, v)
end

Instance Attribute Details

#authorize_pathString (readonly)

Returns:

  • (String)


65
66
67
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 65

def authorize_path
  @authorize_path
end

#configConfig (readonly)

Returns:



68
69
70
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 68

def config
  @config
end

Class Method Details

.with_config(cfg) ⇒ Object

Configure the OAuth2 provider

Parameters:



56
57
58
59
60
61
62
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 56

def self.with_config(cfg)
  Module.new do
    define_singleton_method(:new) do |*args|
      Provider.new(*args, cfg)
    end
  end
end

Instance Method Details

#authenticate(request) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 93

def authenticate(request)
  tokens = [
    request['access_token'],
    token_from_header(request)
  ].compact

  token =
    case tokens.length
    when 0
      nil
    when 1
      tokens.first
    else
      fail 'Too many oauth2 tokens'
    end

  token && config.find_user_by_access_token(request, token)
end

#authorization_endpoint(handler) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 141

def authorization_endpoint(handler)
  Rack::OAuth2::Server::Authorize.new do |req, res|
    client = config.find_client_by_id(req.client_id)
    req.bad_request! if client.nil?

    res.redirect_uri = req.verify_redirect_uri!(client.redirect_uri)

    if req.post?
      auth_res = config.(handler.request, handler.params, req, client)

      if auth_res.nil?
        # Authentication failed
        req.access_denied!
      elsif auth_res.cancel
        # Cancel the process
        req.access_denied!
      elsif auth_res.authenticated && auth_res.complete
        # Authentication was successful
        case req.response_type
        when :code
          res.code = config.get_authorization_code(auth_res)
        when :token
          req.unsupported_response_type!
        end

        res.approve!
      elsif auth_res.authenticated && !auth_res.complete
        # Continue with another authentication step
        res.content_type = 'text/html'
        res.write(config.render_authorize_page(req, handler.params, client, auth_result: auth_res))
      else
        # Authentication failed, report errors and let the user retry
        res.content_type = 'text/html'
        res.write(config.render_authorize_page(req, handler.params, client, auth_result: auth_res))
      end
    else
      res.content_type = 'text/html'
      res.write(config.render_authorize_page(req, handler.params, client))
    end
  end
end

#describeObject



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 122

def describe
  desc = "  OAuth2 authorization provider. While OAuth2 is not supported by HaveAPI\n  clients, it is possible to use your API as an authentication source.\n\n  HaveAPI partially implements RFC 6749: authorization response type \"code\"\n  and token grant types \"authorization_code\" and \"refresh_token\". Other\n  response and grant types are not supported at this time.\n\n  The access token can be passed as bearer token according to RFC 6750.\n  END\n\n  {\n    description: desc,\n    authorize_path: @authorize_path,\n    token_path: @token_path,\n  }\nend\n"

#register_routes(sinatra, prefix) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 75

def register_routes(sinatra, prefix)
  @authorize_path = File.join(prefix, 'authorize')
  @token_path = File.join(prefix, 'token')
  that = self

  sinatra.get @authorize_path do
    that.authorization_endpoint(self).call(request.env)
  end

  sinatra.post @authorize_path do
    that.authorization_endpoint(self).call(request.env)
  end

  sinatra.post @token_path do
    that.token_endpoint(self).call(request.env)
  end
end

#token_endpoint(handler) ⇒ Object



183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 183

def token_endpoint(handler)
  Rack::OAuth2::Server::Token.new do |req, res|
    client = config.find_client_by_id(req.client_id)
    req.invalid_client! if client.nil? || !client.check_secret(req.client_secret)

    res.access_token =
      case req.grant_type
      when :authorization_code
        authorization = config.find_authorization_by_code(client, req.code)

        if authorization.nil? || authorization.check_code_validity(req.redirect_uri)
          req.invalid_grant!
        end

        access_token, expires_at, refresh_token = config.get_tokens(authorization, handler.request)

        bearer_token = Rack::OAuth2::AccessToken::Bearer.new(
          access_token: access_token,
          expires_in: expires_at - Time.now,
        )
        bearer_token.refresh_token = refresh_token if refresh_token
        bearer_token

      when :password
        req.unsupported_grant_type!

      when :client_credentials
        req.unsupported_grant_type!

      when :refresh_token
        config.find_authorization_by_refresh_token(client, req.refresh_token)

        access_token, expires_at, refresh_token = config.refresh_tokens(authorization, handler.request)

        bearer_token = Rack::OAuth2::AccessToken::Bearer.new(
          access_token: access_token,
          expires_in: expires_at - Time.now,
        )
        bearer_token.refresh_token = refresh_token if refresh_token
        bearer_token

      else
        req.unsupported_grant_type!
      end
  end
end

#token_from_header(request) ⇒ Object



112
113
114
115
116
117
118
119
120
# File 'lib/haveapi/authentication/oauth2/provider.rb', line 112

def token_from_header(request)
  auth_header = Rack::Auth::AbstractRequest.new(request.env)

  if auth_header.provided? && !auth_header.parts.first.nil? && auth_header.scheme.to_s == 'bearer'
    auth_header.params
  else
    nil
  end
end