module ShopifyAPI::Auth::Oauth

Constants

NONCE_LENGTH

Public Class Methods

begin_auth(shop:, redirect_path:, is_online: true, scope_override: nil) click to toggle source
# File lib/shopify_api/auth/oauth.rb, line 22
def begin_auth(shop:, redirect_path:, is_online: true, scope_override: nil)
  scope = if scope_override.nil?
    ShopifyAPI::Context.scope
  elsif scope_override.is_a?(ShopifyAPI::Auth::AuthScopes)
    scope_override
  else
    ShopifyAPI::Auth::AuthScopes.new(scope_override)
  end

  unless Context.setup?
    raise Errors::ContextNotSetupError, "ShopifyAPI::Context not setup, please call ShopifyAPI::Context.setup"
  end
  raise Errors::UnsupportedOauthError, "Cannot perform OAuth for private apps." if Context.private?

  state = SecureRandom.alphanumeric(NONCE_LENGTH)

  cookie = SessionCookie.new(value: state, expires: Time.now + 60)

  query = {
    client_id: ShopifyAPI::Context.api_key,
    scope: scope.to_s,
    redirect_uri: "#{ShopifyAPI::Context.host}#{redirect_path}",
    state: state,
    "grant_options[]": is_online ? "per-user" : "",
  }

  query_string = URI.encode_www_form(query)

  auth_route = "https://#{shop}/admin/oauth/authorize?#{query_string}"
  { auth_route: auth_route, cookie: cookie }
end
validate_auth_callback(cookies:, auth_query:) click to toggle source
# File lib/shopify_api/auth/oauth.rb, line 60
def validate_auth_callback(cookies:, auth_query:)
  unless Context.setup?
    raise Errors::ContextNotSetupError, "ShopifyAPI::Context not setup, please call ShopifyAPI::Context.setup"
  end
  raise Errors::InvalidOauthError, "Invalid OAuth callback." unless Utils::HmacValidator.validate(auth_query)
  raise Errors::UnsupportedOauthError, "Cannot perform OAuth for private apps." if Context.private?

  state = cookies[SessionCookie::SESSION_COOKIE_NAME]
  raise Errors::NoSessionCookieError unless state

  raise Errors::InvalidOauthError,
    "Invalid state in OAuth callback." unless state == auth_query.state

  null_session = Auth::Session.new(shop: auth_query.shop)
  body = { client_id: Context.api_key, client_secret: Context.api_secret_key, code: auth_query.code }

  client = Clients::HttpClient.new(session: null_session, base_path: "/admin/oauth")
  response = begin
    client.request(
      Clients::HttpRequest.new(
        http_method: :post,
        path: "access_token",
        body: body,
        body_type: "application/json",
      ),
    )
  rescue ShopifyAPI::Errors::HttpResponseError => e
    raise Errors::RequestAccessTokenError,
      "Cannot complete OAuth process. Received a #{e.code} error while requesting access token."
  end

  session_params = T.cast(response.body, T::Hash[String, T.untyped]).to_h
  session = Session.from(shop: auth_query.shop,
    access_token_response: Oauth::AccessTokenResponse.from_hash(session_params))

  cookie = if Context.embedded?
    SessionCookie.new(
      value: "",
      expires: Time.now,
    )
  else
    SessionCookie.new(
      value: session.id,
      expires: session.online? ? session.expires : nil,
    )
  end

  { session: session, cookie: cookie }
end