Class: SpreeSquare::OauthClient

Inherits:
Object
  • Object
show all
Defined in:
app/services/spree_square/oauth_client.rb

Overview

The OAuth half of talking to Square — separate from SpreeSquare::Client (which makes authenticated Catalog/Orders/Payments calls with a token already in hand). This class is how that token gets obtained, refreshed, and revoked in the first place.

Scopes requested cover every API spree_square actually calls elsewhere (Client#catalog/orders/payments/inventory/locations/webhooks) — keep this list in sync if a new Square API surface gets used.

Defined Under Namespace

Classes: ConfigurationError

Constant Summary collapse

SCOPES =
%w[
  MERCHANT_PROFILE_READ
  ITEMS_READ
  INVENTORY_READ
  INVENTORY_WRITE
  ORDERS_READ
  ORDERS_WRITE
  PAYMENTS_WRITE
  PAYMENTS_READ
].freeze

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeOauthClient

Returns a new instance of OauthClient.

Raises:



35
36
37
38
39
40
# File 'app/services/spree_square/oauth_client.rb', line 35

def initialize
  @application_id = ENV['SQUARE_APPLICATION_ID'].presence
  @application_secret = ENV['SQUARE_APPLICATION_SECRET'].presence
  raise ConfigurationError, 'SQUARE_APPLICATION_ID is not set' if @application_id.blank?
  raise ConfigurationError, 'SQUARE_APPLICATION_SECRET is not set' if @application_secret.blank?
end

Class Method Details

.authorize_urlObject



30
# File 'app/services/spree_square/oauth_client.rb', line 30

def self.authorize_url(...) = new.authorize_url(...)

.exchange_codeObject



31
# File 'app/services/spree_square/oauth_client.rb', line 31

def self.exchange_code(...) = new.exchange_code(...)

.refreshObject



32
# File 'app/services/spree_square/oauth_client.rb', line 32

def self.refresh(...) = new.refresh(...)

.revokeObject



33
# File 'app/services/spree_square/oauth_client.rb', line 33

def self.revoke(...) = new.revoke(...)

Instance Method Details

#authorize_url(redirect_uri:, state:) ⇒ Object

The URL to send the merchant's browser to. session: false forces Square to show its account chooser even if the browser is already signed in to a Square account — without it, a staff member testing this on a shared machine could silently connect the wrong account.



46
47
48
49
50
51
52
53
54
55
# File 'app/services/spree_square/oauth_client.rb', line 46

def authorize_url(redirect_uri:, state:)
  params = {
    client_id: @application_id,
    scope: SCOPES.join(' '),
    session: false,
    state: state,
    redirect_uri: redirect_uri
  }
  "#{base_url}/oauth2/authorize?#{params.to_query}"
end

#exchange_code(code:, redirect_uri:) ⇒ Object



57
58
59
60
61
62
63
64
65
# File 'app/services/spree_square/oauth_client.rb', line 57

def exchange_code(code:, redirect_uri:)
  oauth_api.obtain_token(
    client_id: @application_id,
    client_secret: @application_secret,
    code: code,
    grant_type: 'authorization_code',
    redirect_uri: redirect_uri
  )
end

#refresh(credential) ⇒ Object

Code-flow refresh (vs. PKCE) returns the same refresh token back — Square's docs call this out explicitly, so callers should always save whatever comes back here rather than assuming the old one still works.



70
71
72
73
74
75
76
77
# File 'app/services/spree_square/oauth_client.rb', line 70

def refresh(credential)
  oauth_api.obtain_token(
    client_id: @application_id,
    client_secret: @application_secret,
    refresh_token: credential.refresh_token,
    grant_type: 'refresh_token'
  )
end

#revoke(credential) ⇒ Object

Square's RevokeToken endpoint doesn't use the normal Bearer <token> scheme every other call in this extension uses — it requires Authorization: Client <application_secret> instead, which the SDK's Square::Client can't produce (its Authorization header is fixed to Bearer at construction). Raw HTTP here, deliberately not routed through the SDK.

Raises:

  • (Square::Errors::ResponseError.subclass_for_code(response.code.to_i))


85
86
87
88
89
90
91
92
93
94
95
96
# File 'app/services/spree_square/oauth_client.rb', line 85

def revoke(credential)
  uri = URI("#{base_url}/oauth2/revoke")
  request = Net::HTTP::Post.new(uri)
  request['Content-Type'] = 'application/json'
  request['Authorization'] = "Client #{@application_secret}"
  request.body = { client_id: @application_id, access_token: credential.access_token }.to_json

  response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
  return if response.is_a?(Net::HTTPSuccess)

  raise Square::Errors::ResponseError.subclass_for_code(response.code.to_i).new(response.body, code: response.code.to_i)
end