Skip to content

Commit

Permalink
Extract session creation from access token response
Browse files Browse the repository at this point in the history
  • Loading branch information
gbzodek committed Jan 2, 2024
1 parent ae60871 commit 00c0511
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 66 deletions.
35 changes: 2 additions & 33 deletions lib/shopify_api/auth/oauth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ def validate_auth_callback(cookies:, auth_query:)
end

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

cookie = if Context.embedded?
SessionCookie.new(
Expand All @@ -105,38 +106,6 @@ def validate_auth_callback(cookies:, auth_query:)

{ session: session, cookie: cookie }
end

private

sig { params(session_params: T::Hash[String, T.untyped], shop: String).returns(Session) }
def create_new_session(session_params, shop)
session_params = session_params.to_h { |k, v| [k.to_sym, v] }

scope = session_params[:scope]

is_online = !session_params[:associated_user].nil?

if is_online
associated_user = AssociatedUser.new(session_params[:associated_user].to_h { |k, v| [k.to_sym, v] })
expires = Time.now + session_params[:expires_in].to_i
associated_user_scope = session_params[:associated_user_scope]
id = "#{shop}_#{associated_user.id}"
else
id = "offline_#{shop}"
end

Session.new(
id: id,
shop: shop,
access_token: session_params[:access_token],
scope: scope,
is_online: is_online,
associated_user_scope: associated_user_scope,
associated_user: associated_user,
expires: expires,
shopify_session_id: session_params[:session],
)
end
end
end
end
Expand Down
37 changes: 37 additions & 0 deletions lib/shopify_api/auth/oauth/access_token_response.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# typed: strict
# frozen_string_literal: true

module ShopifyAPI
module Auth
module Oauth
class AccessTokenResponse < T::Struct
extend T::Sig

const :access_token, String
const :scope, String
const :session, T.nilable(String)
const :expires_in, T.nilable(Integer)
const :associated_user, T.nilable(AssociatedUser)
const :associated_user_scope, T.nilable(String)

sig { returns(T::Boolean) }
def online_token?
!associated_user.nil?
end

alias_method :eql?, :==
sig { params(other: T.nilable(AccessTokenResponse)).returns(T::Boolean) }
def ==(other)
return false unless other

access_token == other.access_token &&
scope == other.scope &&
session == other.session &&
expires_in == other.expires_in &&
associated_user == other.associated_user &&
associated_user_scope == other.associated_user_scope
end
end
end
end
end
26 changes: 26 additions & 0 deletions lib/shopify_api/auth/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,32 @@ def temp(shop:, access_token:, &blk)
end
end

sig { params(shop: String, access_token_response: Oauth::AccessTokenResponse).returns(Session) }
def for(shop:, access_token_response:)
is_online = access_token_response.online_token?

if is_online
associated_user = T.must(access_token_response.associated_user)
expires = Time.now + access_token_response.expires_in.to_i
associated_user_scope = access_token_response.associated_user_scope
id = "#{shop}_#{associated_user.id}"
else
id = "offline_#{shop}"
end

new(
id: id,
shop: shop,
access_token: access_token_response.access_token,
scope: access_token_response.scope,
is_online: is_online,
associated_user_scope: associated_user_scope,
associated_user: associated_user,
expires: expires,
shopify_session_id: access_token_response.session,
)
end

sig { params(str: String).returns(Session) }
def deserialize(str)
Oj.load(str)
Expand Down
34 changes: 1 addition & 33 deletions lib/shopify_api/auth/token_exchange.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,42 +68,10 @@ def exchange_token(shop:, session_token:, requested_token_type:)
end

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

session
end

private

sig { params(session_params: T::Hash[String, T.untyped], shop: String).returns(Session) }
def create_new_session(session_params, shop)
session_params = session_params.to_h { |k, v| [k.to_sym, v] }

scope = session_params[:scope]

is_online = !session_params[:associated_user].nil?

if is_online
associated_user = AssociatedUser.new(session_params[:associated_user].to_h { |k, v| [k.to_sym, v] })
expires = Time.now + session_params[:expires_in].to_i
associated_user_scope = session_params[:associated_user_scope]
id = "#{shop}_#{associated_user.id}"
else
id = "offline_#{shop}"
end

Session.new(
id: id,
shop: shop,
access_token: session_params[:access_token],
scope: scope,
is_online: is_online,
associated_user_scope: associated_user_scope,
associated_user: associated_user,
expires: expires,
shopify_session_id: session_params[:session],
)
end
end
end
end
Expand Down
43 changes: 43 additions & 0 deletions test/auth/oauth/access_token_response_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# typed: true
# frozen_string_literal: true

require_relative "../../test_helper"

module ShopifyAPITest
module Auth
module Oauth
class AccessTokenResponseTest < Test::Unit::TestCase
def test_online_token_is_false_when_no_associated_user
token_response = ShopifyAPI::Auth::Oauth::AccessTokenResponse.new(
access_token: "token",
scope: "scope1, scope2",
)

assert(!token_response.online_token?)
end

def test_online_token_is_true_when_associated_user_is_present
associated_user = ShopifyAPI::Auth::AssociatedUser.new(
id: 902541635,
first_name: "first",
last_name: "last",
email: "firstlast@example.com",
email_verified: true,
account_owner: true,
locale: "en",
collaborator: false,
)
token_response = ShopifyAPI::Auth::Oauth::AccessTokenResponse.new(
access_token: "token",
scope: "scope1, scope2",
expires_in: 1000,
associated_user_scope: "scope1",
associated_user: associated_user,
)

assert(token_response.online_token?)
end
end
end
end
end
64 changes: 64 additions & 0 deletions test/auth/session_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,70 @@ def test_temp_with_block_var
assert_equal(session, ShopifyAPI::Context.active_session)
end

def test_for_with_offline_access_token_response
shop = "test-shop"
response = ShopifyAPI::Auth::Oauth::AccessTokenResponse.new(
access_token: "token",
scope: "scope1, scope2",
session: "session",
)
expected_session = ShopifyAPI::Auth::Session.new(
id: "offline_#{shop}",
shop: shop,
access_token: response.access_token,
scope: response.scope,
is_online: false,
associated_user_scope: nil,
associated_user: nil,
expires: nil,
shopify_session_id: response.session,
)

session = ShopifyAPI::Auth::Session.for(shop: shop, access_token_response: response)

assert_equal(expected_session, session)
end

def test_for_with_online_access_token_response
shop = "test-shop"
associated_user = ShopifyAPI::Auth::AssociatedUser.new(
id: 902541635,
first_name: "first",
last_name: "last",
email: "firstlast@example.com",
email_verified: true,
account_owner: true,
locale: "en",
collaborator: false,
)
response = ShopifyAPI::Auth::Oauth::AccessTokenResponse.new(
access_token: "token",
scope: "scope1, scope2",
session: "session",
expires_in: 1000,
associated_user: associated_user,
associated_user_scope: "scope1",
)
time_now = Time.now
expected_session = ShopifyAPI::Auth::Session.new(
id: "#{shop}_#{associated_user.id}",
shop: shop,
access_token: response.access_token,
scope: response.scope,
is_online: false,
associated_user_scope: response.associated_user_scope,
associated_user: associated_user,
expires: time_now + response.expires_in,
shopify_session_id: response.session,
)

session = Time.stub(:now, time_now) do
ShopifyAPI::Auth::Session.for(shop: shop, access_token_response: response)
end

assert_equal(expected_session, session)
end

def teardown
ShopifyAPI::Context.deactivate_session
end
Expand Down
4 changes: 4 additions & 0 deletions test/auth/token_exchange_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def setup
@offline_token_response = {
access_token: SecureRandom.alphanumeric(10),
scope: "scope1,scope2",
session: SecureRandom.alphanumeric(10),
}
@online_token_response = {
access_token: SecureRandom.alphanumeric(10),
Expand All @@ -50,6 +51,7 @@ def setup
locale: "en",
collaborator: false,
},
session: SecureRandom.alphanumeric(10),
}

@expected_associated_user = ShopifyAPI::Auth::AssociatedUser.new(
Expand Down Expand Up @@ -143,6 +145,7 @@ def test_exchange_token_offline_token
scope: @offline_token_response[:scope],
is_online: false,
expires: nil,
shopify_session_id: @offline_token_response[:session],
)

session = ShopifyAPI::Auth::TokenExchange.exchange_token(
Expand All @@ -169,6 +172,7 @@ def test_exchange_token_online_token
associated_user_scope: @online_token_response[:associated_user_scope],
expires: @stubbed_time_now + @online_token_response[:expires_in].to_i,
associated_user: @expected_associated_user,
shopify_session_id: @online_token_response[:session],
)

session = Time.stub(:now, @stubbed_time_now) do
Expand Down

0 comments on commit 00c0511

Please sign in to comment.