Skip to content

Commit

Permalink
Merge pull request #1884 from Shopify/readd_deprecated_jwt_files
Browse files Browse the repository at this point in the history
Re-add deprecated files to prevent major release
  • Loading branch information
lizkenyon authored Jul 24, 2024
2 parents ead986d + 4511d3a commit 747917c
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 6 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ PATH
shopify_app (22.2.1)
activeresource
addressable (~> 2.7)
jwt (>= 2.2.3)
rails (> 5.2.1)
redirect_safely (~> 1.0)
shopify_api (>= 14.3.0, < 15.0)
Expand Down Expand Up @@ -269,7 +270,6 @@ PLATFORMS

DEPENDENCIES
byebug
jwt (>= 2.2.3)
minitest
mocha
pry
Expand Down
4 changes: 4 additions & 0 deletions lib/shopify_app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,14 @@ def self.use_webpacker?
# managers
require "shopify_app/managers/webhooks_manager"

# middleware
require "shopify_app/middleware/jwt_middleware"

# session
require "shopify_app/session/in_memory_session_store"
require "shopify_app/session/in_memory_shop_session_store"
require "shopify_app/session/in_memory_user_session_store"
require "shopify_app/session/jwt"
require "shopify_app/session/null_user_session_store"
require "shopify_app/session/session_repository"
require "shopify_app/session/session_storage"
Expand Down
10 changes: 6 additions & 4 deletions lib/shopify_app/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def initialize
@webhooks_manager_queue_name = Rails.application.config.active_job.queue_name
@disable_webpacker = ENV["SHOPIFY_APP_DISABLE_WEBPACKER"].present?

log_callback_controller_method_deprecation
log_v23_deprecations
end

def login_url
Expand Down Expand Up @@ -163,16 +163,18 @@ def post_authenticate_tasks

private

def log_callback_controller_method_deprecation
def log_v23_deprecations
return unless Rails.env.development?

# TODO: Remove this before releasing v23.0.0
message = <<~EOS
================================================
=> Upcoming deprecation in v23.0:
=> Upcoming changes in v23.0:
* 'CallbackController::perform_after_authenticate_job' and related methods 'install_webhooks', 'perform_after_authenticate_job'
* will be deprecated from CallbackController in the next major release. If you need to customize
* are deprecated and will be removed from CallbackController in the next major release. If you need to customize
* post authentication tasks, see https://github.com/Shopify/shopify_app/blob/main/docs/shopify_app/authentication.md#post-authenticate-tasks
* ShopifyApp::JWTMiddleware will be removed, use ShopifyApp::WithShopifyIdToken instead.
================================================
EOS
puts message
Expand Down
4 changes: 4 additions & 0 deletions lib/shopify_app/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ class Engine < Rails::Engine
engine_name "shopify_app"
isolate_namespace ShopifyApp

initializer "shopify_app.middleware" do |app|
app.config.middleware.insert_after(::Rack::Runtime, ShopifyApp::JWTMiddleware)
end

initializer "shopify_app.assets.precompile" do |app|
app.config.assets.precompile += [
"shopify_app/redirect.js",
Expand Down
48 changes: 48 additions & 0 deletions lib/shopify_app/middleware/jwt_middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

module ShopifyApp
class JWTMiddleware
TOKEN_REGEX = /^Bearer (.+)$/
ID_TOKEN_QUERY_PARAM = "id_token"

def initialize(app)
@app = app
end

def call(env)
return call_next(env) unless ShopifyApp.configuration.embedded_app?

token = token_from_authorization_header(env) || token_from_query_string(env)
return call_next(env) unless token

set_env_variables(token, env)
call_next(env)
end

private

def call_next(env)
@app.call(env)
end

def token_from_authorization_header(env)
env["HTTP_AUTHORIZATION"]&.match(TOKEN_REGEX)&.[](1)
end

def token_from_query_string(env)
Rack::Utils.parse_nested_query(env["QUERY_STRING"])[ID_TOKEN_QUERY_PARAM]
end

def set_env_variables(token, env)
jwt = ShopifyAPI::Auth::JwtPayload.new(token)

env["jwt.token"] = token
env["jwt.shopify_domain"] = jwt.shopify_domain
env["jwt.shopify_user_id"] = jwt.shopify_user_id
env["jwt.expire_at"] = jwt.expire_at
rescue ShopifyAPI::Errors::InvalidJwtTokenError
# ShopifyApp::JWT did not raise any exceptions, ensuring behaviour does not change
nil
end
end
end
73 changes: 73 additions & 0 deletions lib/shopify_app/session/jwt.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# frozen_string_literal: true

module ShopifyApp
class JWT
WARN_EXCEPTIONS = [
::JWT::DecodeError,
::JWT::ExpiredSignature,
::JWT::ImmatureSignature,
::JWT::VerificationError,
::ShopifyApp::InvalidAudienceError,
::ShopifyApp::InvalidDestinationError,
::ShopifyApp::MismatchedHostsError,
]

def initialize(token)
warn_deprecation
@token = token
set_payload
end

def shopify_domain
@payload && ShopifyApp::Utils.sanitize_shop_domain(@payload["dest"])
end

def shopify_user_id
@payload["sub"].to_i if @payload && @payload["sub"]
end

def expire_at
@payload["exp"].to_i if @payload && @payload["exp"]
end

private

def set_payload
payload, _ = parse_token_data(ShopifyApp.configuration&.secret, ShopifyApp.configuration&.old_secret)
@payload = validate_payload(payload)
rescue *WARN_EXCEPTIONS
nil
end

def parse_token_data(secret, old_secret)
::JWT.decode(@token, secret, true, { algorithm: "HS256" })
rescue ::JWT::VerificationError
raise unless old_secret

::JWT.decode(@token, old_secret, true, { algorithm: "HS256" })
end

def validate_payload(payload)
dest_host = ShopifyApp::Utils.sanitize_shop_domain(payload["dest"])
iss_host = ShopifyApp::Utils.sanitize_shop_domain(payload["iss"])
api_key = ShopifyApp.configuration.api_key

raise ::ShopifyApp::InvalidAudienceError,
"'aud' claim does not match api_key" unless payload["aud"] == api_key
raise ::ShopifyApp::InvalidDestinationError, "'dest' claim host not a valid shopify host" unless dest_host

raise ::ShopifyApp::MismatchedHostsError,
"'dest' claim host does not match 'iss' claim host" unless dest_host == iss_host

payload
end

def warn_deprecation
message = <<~EOS
"ShopifyApp::JWT will be deprecated, use ShopifyAPI::Auth::JwtPayload to parse JWT token instead."
EOS

ShopifyApp::Logger.deprecated(message, "23.0.0")
end
end
end
3 changes: 2 additions & 1 deletion shopify_app.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ Gem::Specification.new do |s|
s.add_runtime_dependency("redirect_safely", "~> 1.0")
s.add_runtime_dependency("shopify_api", ">= 14.3.0", "< 15.0")
s.add_runtime_dependency("sprockets-rails", ">= 2.0.0")
# Deprecated: move to development dependencies when releasing v23
s.add_runtime_dependency("jwt", ">= 2.2.3")

s.add_development_dependency("byebug")
s.add_development_dependency("jwt", ">= 2.2.3")
s.add_development_dependency("minitest")
s.add_development_dependency("mocha")
s.add_development_dependency("pry")
Expand Down

0 comments on commit 747917c

Please sign in to comment.