Plugin = {
	name = "JWT Decoder",
	description = [[
Decodes JWTs found in request headers and exposes the decoded payload inline.

For every request header whose value looks like a JWT (three dot-separated
base64url segments), the base64url-encoded payload is decoded and added back to
the request as a new header:

```
Authorization: Bearer eyJ...   ->   X-JWT-Decoded-Authorization: {"sub":"123",...}
X-Auth-Token:  eyJ...          ->   X-JWT-Decoded-X-Auth-Token:  {"sub":"123",...}
```

A leading `Bearer ` prefix is stripped from any header value before decoding,
so custom authorization headers (`Authorization-Test`, `X-Auth-Token`, ...)
are handled too. The decoded header is added to the outbound request, so it is visible
in the intercept, history and replay views (and forwarded upstream).

Pure Lua, no external dependencies.
  ]],
	on_request = { sync = true },
}

-- Reverse lookup table: base64url character -> 6-bit value (built once at load time).
local B64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
local B64_DEC = {}
for i = 1, #B64_ALPHABET do
	B64_DEC[B64_ALPHABET:sub(i, i)] = i - 1
end

-- Decode an unpadded base64url string. Returns the decoded string, or nil on invalid input.
local function b64url_decode(s)
	if #s % 4 == 1 then
		return nil
	end
	local out, acc, bits = {}, 0, 0
	for c in s:gmatch(".") do
		local v = B64_DEC[c]
		if not v then
			return nil
		end
		acc, bits = acc * 64 + v, bits + 6
		if bits >= 8 then
			bits = bits - 8
			out[#out + 1] = string.char(math.floor(acc / 2 ^ bits))
			acc = acc % 2 ^ bits
		end
	end
	return table.concat(out)
end

-- If value is a JWT, return its decoded payload
local function decode_jwt_payload(value)
	-- Three dot-separated base64url segments; the signature may be empty (alg:none).
	local payload = value:match("^[A-Za-z0-9_-]+%.([A-Za-z0-9_-]+)%.[A-Za-z0-9_-]*$")
	if not payload or #payload > 8192 then
		return nil
	end
	local decoded = b64url_decode(payload)
	if not decoded then
		return nil
	end
	-- Only accept plausible JSON payloads.
	if decoded:match("^%s*{") == nil then
		return nil
	end
	decoded = decoded:gsub("[%c]+", " ")
	return decoded
end

function on_request(req)
	for name, value in pairs(req.headers) do
		local token = value:gsub("^[Bb][Ee][Aa][Rr][Ee][Rr]%s+", "")
		local payload = decode_jwt_payload(token)
		if payload then
			req:set_header("X-JWT-Decoded-" .. name, payload)
		end
	end
end
