Class: Rubino::API::Operations::OAuth::Providers::CallbackOperation
- Inherits:
-
Object
- Object
- Rubino::API::Operations::OAuth::Providers::CallbackOperation
- Defined in:
- lib/rubino/api/operations/oauth/providers/callback_operation.rb
Overview
POST /v1/oauth/providers/:id/callback
Client posts back code + state + code_verifier (plus expected_state it kept from connect). We constant-time-compare state, exchange the code, fetch account info, upsert by (provider, account_id) with encrypted tokens, and return the connection. Exchange outcomes bump oauth_token_exchanges_total ok|error.
Class Method Summary collapse
Instance Method Summary collapse
- #call(request) ⇒ Object
-
#initialize(registry: ::Rubino::OAuth::Registry, repository: nil) ⇒ CallbackOperation
constructor
Accepts an alternate provider registry and connection repository for tests.
Constructor Details
#initialize(registry: ::Rubino::OAuth::Registry, repository: nil) ⇒ CallbackOperation
Accepts an alternate provider registry and connection repository for tests.
26 27 28 29 |
# File 'lib/rubino/api/operations/oauth/providers/callback_operation.rb', line 26 def initialize(registry: ::Rubino::OAuth::Registry, repository: nil) @registry = registry @repository = repository end |
Class Method Details
.call(request) ⇒ Object
21 22 23 |
# File 'lib/rubino/api/operations/oauth/providers/callback_operation.rb', line 21 def self.call(request) new.call(request) end |
Instance Method Details
#call(request) ⇒ Object
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/rubino/api/operations/oauth/providers/callback_operation.rb', line 31 def call(request) id = request.params.fetch("id") provider = @registry.fetch(id) attrs = request.validate!(Schemas::CallbackProvider) unless Rack::Utils.secure_compare(attrs[:state], attrs[:expected_state]) raise ValidationError, "state mismatch" end token = begin provider.exchange_code( code: attrs[:code], redirect_uri: attrs[:redirect_uri], code_verifier: attrs[:code_verifier] ) rescue StandardError => e ::Rubino::Metrics.counter(:oauth_token_exchanges_total, provider: provider.id, outcome: "error").increment raise UpstreamError.new("token exchange failed: #{e.class.name}", service: provider.id) end ::Rubino::Metrics.counter(:oauth_token_exchanges_total, provider: provider.id, outcome: "ok").increment info = provider.fetch_account_info(token[:access_token]) connection = repository.upsert( provider: provider.id, account_id: info[:account_id], account_email: info[:account_email], access_token: token[:access_token], refresh_token: token[:refresh_token], expires_at: token[:expires_at], scopes: token[:scopes], metadata: info[:metadata] || {} ) [201, Serializer.call(connection)] end |