mirror of
https://github.com/status-im/eth-rpc-proxy.git
synced 2026-08-30 19:21:09 +00:00
119 lines
4.1 KiB
Lua
119 lines
4.1 KiB
Lua
local json = require("cjson")
|
|
local auth_config = require("auth.auth_config")
|
|
local auth_utils = require("utils.auth_utils")
|
|
|
|
-- Extract JWT token from Authorization header or query parameters
|
|
local token, token_source = auth_utils.extract_jwt_token()
|
|
|
|
if not token then
|
|
ngx.status = 401
|
|
ngx.exit(401)
|
|
end
|
|
|
|
-- If token was extracted from query parameters, remove them from the request
|
|
-- since no other query params are expected, we can safely clear all args
|
|
if token_source == "query" then
|
|
ngx.req.set_uri_args({})
|
|
end
|
|
|
|
-- Get configuration values dynamically
|
|
local requests_per_token = auth_config.get_requests_per_token()
|
|
local token_expiry_minutes = auth_config.get_token_expiry_minutes()
|
|
|
|
-- Use token as cache key
|
|
local cache_key = "jwt_valid:" .. token
|
|
local usage_key = "jwt_usage:" .. token
|
|
|
|
-- Check if token is in cache (previously validated by Go service)
|
|
local cached_result = ngx.shared.jwt_tokens:get(cache_key)
|
|
|
|
if cached_result then
|
|
-- Token is cached as valid, now check and increment usage
|
|
|
|
-- Get current usage count
|
|
local current_usage = ngx.shared.jwt_tokens:get(usage_key) or 0
|
|
|
|
-- Check if limit exceeded
|
|
if current_usage >= requests_per_token then
|
|
ngx.log(ngx.WARN, "Rate limit exceeded for cached token: ", current_usage, "/", requests_per_token)
|
|
ngx.header["X-RateLimit-Limit"] = tostring(requests_per_token)
|
|
ngx.header["X-RateLimit-Remaining"] = "0"
|
|
ngx.header["X-Cache-Status"] = "HIT"
|
|
ngx.status = 401
|
|
ngx.exit(401)
|
|
end
|
|
|
|
-- Increment usage counter
|
|
local new_usage = current_usage + 1
|
|
local usage_ttl = (token_expiry_minutes * 60) + 60 -- Token expiry + 1 minute buffer
|
|
local success = ngx.shared.jwt_tokens:set(usage_key, new_usage, usage_ttl)
|
|
|
|
if not success then
|
|
ngx.log(ngx.WARN, "Failed to update usage counter for token")
|
|
end
|
|
|
|
-- Set rate limit headers
|
|
ngx.header["X-RateLimit-Limit"] = tostring(requests_per_token)
|
|
ngx.header["X-RateLimit-Remaining"] = tostring(requests_per_token - new_usage)
|
|
ngx.header["X-Cache-Status"] = "HIT"
|
|
|
|
ngx.status = 200
|
|
ngx.exit(200)
|
|
end
|
|
|
|
-- Cache miss - validate with Go service
|
|
-- Get current auth service URL for logging
|
|
local current_url = auth_config.get_go_auth_service_url()
|
|
|
|
-- Create subrequest to Go auth service
|
|
-- Always use Bearer token format for internal verification
|
|
local auth_header_for_go = "Bearer " .. token
|
|
local res = ngx.location.capture("/_auth_go_verify", {
|
|
method = ngx.HTTP_GET,
|
|
headers = {
|
|
["Authorization"] = auth_header_for_go
|
|
}
|
|
})
|
|
|
|
if res.status == 200 then
|
|
-- Token is valid, cache it and initialize usage counter
|
|
|
|
-- Cache the valid token for the duration of token expiry
|
|
local cache_ttl = token_expiry_minutes * 60 -- Convert minutes to seconds
|
|
local cache_success = ngx.shared.jwt_tokens:set(cache_key, "valid", cache_ttl)
|
|
if not cache_success then
|
|
ngx.log(ngx.WARN, "Failed to cache valid JWT token")
|
|
end
|
|
|
|
-- Initialize usage counter (this request counts as first usage)
|
|
-- Usage counter TTL should be longer than cache TTL to prevent inconsistencies
|
|
local usage_ttl = cache_ttl + 60 -- Extra 1 minute buffer
|
|
local usage_success = ngx.shared.jwt_tokens:set(usage_key, 1, usage_ttl)
|
|
if not usage_success then
|
|
ngx.log(ngx.WARN, "Failed to initialize usage counter")
|
|
end
|
|
|
|
-- Set rate limit headers
|
|
ngx.header["X-RateLimit-Limit"] = tostring(requests_per_token)
|
|
ngx.header["X-RateLimit-Remaining"] = tostring(requests_per_token - 1)
|
|
ngx.header["X-Cache-Status"] = "MISS"
|
|
|
|
ngx.status = 200
|
|
ngx.exit(200)
|
|
|
|
elseif res.status == 429 then
|
|
-- Rate limit exceeded at Go service level
|
|
ngx.log(ngx.WARN, "Rate limit exceeded at Go service")
|
|
ngx.header["X-RateLimit-Limit"] = tostring(requests_per_token)
|
|
ngx.header["X-RateLimit-Remaining"] = "0"
|
|
ngx.header["X-Cache-Status"] = "MISS"
|
|
ngx.status = 401
|
|
ngx.exit(401)
|
|
|
|
else
|
|
-- Token is invalid
|
|
ngx.log(ngx.WARN, "JWT validation failed at Go service: ", res.status)
|
|
ngx.header["X-Cache-Status"] = "MISS"
|
|
ngx.status = 401
|
|
ngx.exit(401)
|
|
end |