I’m trying to implement an auth layer for HTTP.jl
module ApiClient
import HTTP
import Dates
import JSON
Base.@kwdef struct TokenInfo
access_token::String = ""
expires_at::Dates.DateTime = Dates.now(Dates.UTC) + Dates.Second(0)
token_type::String = "Bearer"
end
struct JWTConfig
client_id::String
client_secret::String
token_url::String
token_info::Ref{TokenInfo}
end
function getToken(jwt_config::JWTConfig)
response = HTTP.post(
jwt_config.token_url,
["Content-Type" => "application/json"],
JSON.json(
Dict(
"grant_type" => "client_credentials",
"client_id" => jwt_config.client_id,
"client_secret" => jwt_config.client_secret,
),
),
)
response_data = JSON.parse(String(response.body))
return TokenInfo(
response_data["access_token"],
Dates.now(Dates.UTC) + Dates.Second(response_data["expires_in"]),
response_data["token_type"],
)
end
function AuthLayer(handler)
# return handler function
return function (req; jwt_config::JWTConfig, kwargs...)
# we add a custom header with stringified auth creds
try
# Try the original request
HTTP.setheader(req, "Authorization" => "Bearer $(jwt_config.token_info[].access_token)")
return handler(req; kwargs...)
catch e
# Check if it's a 401 error
if e isa HTTP.StatusError && e.status == 401
# Refresh the token and retry the request
jwt_config.token_info[] = getToken(jwt_config)
HTTP.setheader(req, "Authorization" => "Bearer $(jwt_config.token_info[].access_token)")
try
return handler(req; kwargs...)
catch
throw(e)
end
else
# If it's not a 401 error, rethrow the exception
throw(e)
end
end
end
end
HTTP.@client [AuthLayer]
end
But somehow this does not work correctly. I can access the token and resource I want, but when I first access the token I get half the response (literary the JSON body is cut off). If I switch on debugging I see, that the HTTP response is weird with many fields doubled, e.g. two times Content-Length: 107
and Content-Length: 26699
.
How can I call the token requresh url when a request fails? Is this not supported?