Skip to content

Commit 482d766

Browse files
committed
Updated validation of audience to support multiple audience values. Added a method to set variables for each claim in the JWT token.
1 parent 8df6e37 commit 482d766

File tree

1 file changed

+71
-26
lines changed

1 file changed

+71
-26
lines changed

lib/jwtverify.lua

Lines changed: 71 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,25 @@ local function dump(o)
5959
end
6060
end
6161

62-
function readAll(file)
62+
-- Loops through array to find the given string.
63+
-- items: array of strings
64+
-- test_str: string to search for
65+
local function contains(items, test_str)
66+
for _,item in pairs(items) do
67+
68+
-- strip whitespace
69+
item = item:gsub("%s+", "")
70+
test_str = test_str:gsub("%s+", "")
71+
72+
if item == test_str then
73+
return true
74+
end
75+
end
76+
77+
return false
78+
end
79+
80+
local function readAll(file)
6381
log("Reading file " .. file)
6482
local f = assert(io.open(file, "rb"))
6583
local content = f:read("*all")
@@ -90,7 +108,6 @@ local function decodeJwt(authorizationHeader)
90108
token.signature = headerFields[4]
91109
token.signaturedecoded = base64.decode(token.signature)
92110

93-
log('Authorization header: ' .. authorizationHeader)
94111
log('Decoded JWT header: ' .. dump(token.headerdecoded))
95112
log('Decoded JWT payload: ' .. dump(token.payloaddecoded))
96113

@@ -137,11 +154,43 @@ local function issuerIsValid(token, expectedIssuer)
137154
return token.payloaddecoded.iss == expectedIssuer
138155
end
139156

140-
local function audienceIsValid(token, expectedAudience)
141-
return token.payloaddecoded.aud == expectedAudience
157+
-- Checks if the audience in the token is listed in the
158+
-- OAUTH_AUDIENCE environment variable. Both the token audience
159+
-- and the environment variable can contain multiple audience values,
160+
-- separated by commas. Each value will be checked.
161+
local function audienceIsValid(token, expectedAudienceParam)
162+
163+
-- Convert OAUTH_AUDIENCE environment variable to a table,
164+
-- even if it contains only one value
165+
local expectedAudiences = expectedAudienceParam
166+
if type(expectedAudiences) == "string" then
167+
-- split multiple values using a space as the delimiter
168+
expectedAudiences = core.tokenize(expectedAudienceParam, " ")
169+
end
170+
171+
-- Convert 'aud' claim to a table, even if it contains only one value
172+
local receivedAudiences = token.payloaddecoded.aud
173+
if type(token.payloaddecoded.aud) == "string" then
174+
receivedAudiences ={}
175+
receivedAudiences[1] = token.payloaddecoded.aud
176+
end
177+
178+
for _, receivedAudience in ipairs(receivedAudiences) do
179+
if contains(expectedAudiences, receivedAudience) then
180+
return true
181+
end
182+
end
183+
184+
return false
185+
end
186+
187+
local function setVariablesFromPayload(txn, decodedPayload)
188+
for key, value in pairs(decodedPayload) do
189+
txn:set_var("txn.oauth." .. key, dump(value))
190+
end
142191
end
143192

144-
function jwtverify(txn)
193+
local function jwtverify(txn)
145194
local pem = config.publicKey
146195
local issuer = config.issuer
147196
local audience = config.audience
@@ -155,6 +204,9 @@ function jwtverify(txn)
155204
goto out
156205
end
157206

207+
-- Set an HAProxy variable for each field in the token payload
208+
setVariablesFromPayload(txn, token.payloaddecoded)
209+
158210
-- 2. Verify the signature algorithm is supported (HS256, HS512, RS256)
159211
if algorithmIsValid(token) == false then
160212
log("Algorithm not valid.")
@@ -197,13 +249,6 @@ function jwtverify(txn)
197249
goto out
198250
end
199251

200-
-- 7. Add scopes to variable
201-
if token.payloaddecoded.scope ~= nil then
202-
txn.set_var(txn, "txn.oauth_scopes", token.payloaddecoded.scope)
203-
else
204-
txn.set_var(txn, "txn.oauth_scopes", "")
205-
end
206-
207252
-- 8. Set authorized variable
208253
log("req.authorized = true")
209254
txn.set_var(txn, "txn.authorized", true)
@@ -220,20 +265,20 @@ end
220265
-- Called after the configuration is parsed.
221266
-- Loads the OAuth public key for validating the JWT signature.
222267
core.register_init(function()
223-
config.issuer = os.getenv("OAUTH_ISSUER")
224-
config.audience = os.getenv("OAUTH_AUDIENCE")
225-
226-
-- when using an RS256 signature
227-
local publicKeyPath = os.getenv("OAUTH_PUBKEY_PATH")
228-
local pem = readAll(publicKeyPath)
229-
config.publicKey = pem
230-
231-
-- when using an HS256 or HS512 signature
232-
config.hmacSecret = os.getenv("OAUTH_HMAC_SECRET")
233-
234-
log("PublicKeyPath: " .. publicKeyPath)
235-
log("Issuer: " .. (config.issuer or "<none>"))
236-
log("Audience: " .. (config.audience or "<none>"))
268+
config.issuer = os.getenv("OAUTH_ISSUER")
269+
config.audience = os.getenv("OAUTH_AUDIENCE")
270+
271+
-- when using an RS256 signature
272+
local publicKeyPath = os.getenv("OAUTH_PUBKEY_PATH")
273+
local pem = readAll(publicKeyPath)
274+
config.publicKey = pem
275+
276+
-- when using an HS256 or HS512 signature
277+
config.hmacSecret = os.getenv("OAUTH_HMAC_SECRET")
278+
279+
log("PublicKeyPath: " .. publicKeyPath)
280+
log("Issuer: " .. (config.issuer or "<none>"))
281+
log("Audience: " .. (config.audience or "<none>"))
237282
end)
238283

239284
-- Called on a request.

0 commit comments

Comments
 (0)