Skip to content

Commit

Permalink
fix: Make Authv2 middleware thread-safe
Browse files Browse the repository at this point in the history
  • Loading branch information
agis committed Feb 24, 2022
1 parent 3434e3f commit 1123911
Showing 1 changed file with 69 additions and 69 deletions.
138 changes: 69 additions & 69 deletions lib/clerk/rack_middleware_v2.rb
Original file line number Diff line number Diff line change
@@ -1,126 +1,126 @@
require "clerk"

module Clerk
class RackMiddlewareV2
class ProxyV2
CACHE_TTL = 60 # seconds
class ProxyV2
CACHE_TTL = 60 # seconds

attr_reader :session_claims, :session_token
attr_reader :session_claims, :session_token

def initialize(session_claims: nil, session_token: nil)
@session_claims = session_claims
@session_token = session_token
@session = nil
end
def initialize(session_claims: nil, session_token: nil)
@session_claims = session_claims
@session_token = session_token
@session = nil
end

def session
return nil if @session_claims.nil?
def session
return nil if @session_claims.nil?

@session ||= verify_session
end
@session ||= verify_session
end

def verify_session
return nil if @session_claims.nil?
def verify_session
return nil if @session_claims.nil?

sdk.sessions.verify_token(@session_claims["sid"], @session_token)
end
sdk.sessions.verify_token(@session_claims["sid"], @session_token)
end

def user
return nil if user_id.nil?
def user
return nil if user_id.nil?

@user ||= fetch_user(user_id)
end
@user ||= fetch_user(user_id)
end

def user_id
return nil if @session_claims.nil?
def user_id
return nil if @session_claims.nil?

@session_claims["sub"]
end
@session_claims["sub"]
end

private
private

def fetch_user(user_id)
cached_fetch("clerk_user:#{user_id}") do
sdk.users.find(user_id)
end
def fetch_user(user_id)
cached_fetch("clerk_user:#{user_id}") do
sdk.users.find(user_id)
end
end

def cached_fetch(key, &block)
if store = Clerk.configuration.middleware_cache_store
store.fetch(key, expires_in: CACHE_TTL, &block)
else
yield
end
def cached_fetch(key, &block)
if store = Clerk.configuration.middleware_cache_store
store.fetch(key, expires_in: CACHE_TTL, &block)
else
yield
end
end

def sdk
@sdk ||= Clerk::SDK.new
end
def sdk
@sdk ||= Clerk::SDK.new
end
end

class RackMiddlewareV2
def initialize(app)
@app = app
end

def call(env)
@env = env
@req = Rack::Request.new(env)
@env["clerk"] = ProxyV2.new
@header_token = @req.env["HTTP_AUTHORIZATION"]
@header_token = @header_token.strip.sub(/\ABearer /, '') if @header_token
@cookie_token = @req.cookies["__session"]
@client_uat = @req.cookies["__client_uat"]
env = env
req = Rack::Request.new(env)
env["clerk"] = Clerk::ProxyV2.new
header_token = req.env["HTTP_AUTHORIZATION"]
header_token = header_token.strip.sub(/\ABearer /, '') if header_token
cookie_token = req.cookies["__session"]
client_uat = req.cookies["__client_uat"]

##########################################################################
# #
# HEADER AUTHENTICATION #
# #
##########################################################################
if @header_token
return signed_out if !sdk.decode_token(@header_token) # malformed JWT
if header_token
return signed_out(env) if !sdk.decode_token(header_token) # malformed JWT

token = verify_token(@header_token)
return signed_in(token, @header_token) if token
token = verify_token(header_token)
return signed_in(env, token, header_token) if token

# Clerk.js should refresh the token and retry
return unknown(interstitial: false)
end

# in cross-origin XHRs the use of Authorization header is mandatory.
if cross_origin_request?(@req)
return signed_out
if cross_origin_request?(req)
return signed_out(env)
end

if development_or_staging? && !browser_request?(@req)
if development_or_staging? && !browser_request?(req)
# the interstitial won't work if the user agent is not a browser, so
# short-circuit and avoid rendering it
#
# We only limit this to dev/stg because we're not yet sure how robust
# this strategy is, yet. In the future, we might enable it for prod too.
return signed_out
return signed_out(env)
end

##########################################################################
# #
# COOKIE AUTHENTICATION #
# #
##########################################################################
if development_or_staging? && (@req.referrer.nil? || cross_origin_request?(@req))
if development_or_staging? && (req.referrer.nil? || cross_origin_request?(req))
return unknown(interstitial: true)
end

if production? && @client_uat.nil?
return signed_out
if production? && client_uat.nil?
return signed_out(env)
end

if @client_uat == "0"
return signed_out
if client_uat == "0"
return signed_out(env)
end

token = verify_token(@cookie_token)
token = verify_token(cookie_token)

if token && token["iat"] && @client_uat && Integer(@client_uat) <= token["iat"]
return signed_in(token, @cookie_token)
if token && token["iat"] && client_uat && Integer(client_uat) <= token["iat"]
return signed_in(env, token, cookie_token)
end

unknown(interstitial: true)
Expand All @@ -129,15 +129,15 @@ def call(env)
private

# Outcome A
def signed_in(claims, token)
@env["clerk"] = ProxyV2.new(session_claims: claims, session_token: token)
def signed_in(env, claims, token)
env["clerk"] = ProxyV2.new(session_claims: claims, session_token: token)

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

# Outcome B
def signed_out
@app.call(@env)
def signed_out(env)
@app.call(env)
end

# Outcome C
Expand Down Expand Up @@ -184,14 +184,14 @@ def verify_token(token)
return false if token.nil? || token.strip.empty?

begin
@session = sdk.verify_token(token)
sdk.verify_token(token)
rescue JWT::DecodeError, JWT::RequiredDependencyError => e
false
end
end

def sdk
@sdk ||= Clerk::SDK.new
Clerk::SDK.new
end
end
end

0 comments on commit 1123911

Please sign in to comment.