-
Notifications
You must be signed in to change notification settings - Fork 468
/
oauth.rb
112 lines (94 loc) · 3.94 KB
/
oauth.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# typed: strict
# frozen_string_literal: true
module ShopifyAPI
module Auth
module Oauth
extend T::Sig
NONCE_LENGTH = 15
class << self
extend T::Sig
sig do
params(
shop: String,
redirect_path: String,
is_online: T.nilable(T::Boolean),
scope_override: T.nilable(T.any(ShopifyAPI::Auth::AuthScopes, T::Array[String], String)),
).returns(T::Hash[Symbol, T.any(String, SessionCookie)])
end
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
sig do
params(
cookies: T::Hash[String, String],
auth_query: AuthQuery,
).returns(T::Hash[Symbol, T.any(Session, SessionCookie)])
end
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
end
end
end
end