1515-- limitations under the License.
1616--
1717local core = require (" apisix.core" )
18- local jwt = require (" resty.jwt" )
1918local consumer_mod = require (" apisix.consumer" )
19+ local resty_random = require (" resty.random" )
2020local new_tab = require (" table.new" )
2121local auth_utils = require (" apisix.utils.auth" )
2222
2323local ngx_decode_base64 = ngx .decode_base64
24+ local ngx_encode_base64 = ngx .encode_base64
2425local ngx = ngx
26+ local ngx_time = ngx .time
2527local sub_str = string.sub
2628local table_insert = table.insert
2729local table_concat = table.concat
2830local ngx_re_gmatch = ngx .re .gmatch
2931local plugin_name = " jwt-auth"
3032local schema_def = require (" apisix.schema_def" )
31-
33+ local jwt_parser = require ( " apisix.plugins.jwt-auth.parser " )
3234
3335local schema = {
3436 type = " object" ,
@@ -60,6 +62,15 @@ local schema = {
6062 },
6163 realm = schema_def .get_realm_schema (" jwt" ),
6264 anonymous_consumer = schema_def .anonymous_consumer_schema ,
65+ claims_to_verify = {
66+ type = " array" ,
67+ items = {
68+ type = " string" ,
69+ enum = {" exp" ," nbf" },
70+ },
71+ uniqueItems = true ,
72+ default = {" exp" , " nbf" },
73+ },
6374 },
6475}
6576
@@ -77,7 +88,21 @@ local consumer_schema = {
7788 },
7889 algorithm = {
7990 type = " string" ,
80- enum = {" HS256" , " HS512" , " RS256" , " ES256" },
91+ enum = {
92+ " HS256" ,
93+ " HS384" ,
94+ " HS512" ,
95+ " RS256" ,
96+ " RS384" ,
97+ " RS512" ,
98+ " ES256" ,
99+ " ES384" ,
100+ " ES512" ,
101+ " PS256" ,
102+ " PS384" ,
103+ " PS512" ,
104+ " EdDSA" ,
105+ },
81106 default = " HS256"
82107 },
83108 exp = {type = " integer" , minimum = 1 , default = 86400 },
@@ -97,7 +122,7 @@ local consumer_schema = {
97122 {
98123 properties = {
99124 algorithm = {
100- enum = {" HS256" , " HS512" },
125+ enum = {" HS256" , " HS384 " , " HS512" },
101126 default = " HS256"
102127 },
103128 },
@@ -106,7 +131,18 @@ local consumer_schema = {
106131 properties = {
107132 public_key = {type = " string" },
108133 algorithm = {
109- enum = {" RS256" , " ES256" },
134+ enum = {
135+ " RS256" ,
136+ " RS384" ,
137+ " RS512" ,
138+ " ES256" ,
139+ " ES384" ,
140+ " ES512" ,
141+ " PS256" ,
142+ " PS384" ,
143+ " PS512" ,
144+ " EdDSA" ,
145+ },
110146 },
111147 },
112148 required = {" public_key" },
@@ -141,15 +177,24 @@ function _M.check_schema(conf, schema_type)
141177 return false , err
142178 end
143179
180+ local is_hs_alg = conf .algorithm :sub (1 , 2 ) == " HS"
144181 if (conf .algorithm == " HS256" or conf .algorithm == " HS512" ) and not conf .secret then
145182 return false , " property \" secret\" is required " ..
146183 " when \" algorithm\" is \" HS256\" or \" HS512\" "
184+ end
185+
186+ if is_hs_alg and not conf .secret then
187+ conf .secret = ngx_encode_base64 (resty_random .bytes (32 , true ))
147188 elseif conf .base64_secret then
148189 if ngx_decode_base64 (conf .secret ) == nil then
149190 return false , " base64_secret required but the secret is not in base64 format"
150191 end
151192 end
152193
194+ if not is_hs_alg and not conf .public_key then
195+ return false , " missing valid public key"
196+ end
197+
153198 return true
154199end
155200
@@ -232,15 +277,48 @@ local function get_secret(conf)
232277 return secret
233278end
234279
235- local function get_auth_secret (auth_conf )
236- if not auth_conf .algorithm or auth_conf .algorithm == " HS256"
237- or auth_conf .algorithm == " HS512" then
238- return get_secret (auth_conf )
239- elseif auth_conf .algorithm == " RS256" or auth_conf .algorithm == " ES256" then
240- return auth_conf .public_key
280+
281+ local function get_real_payload (key , auth_conf , payload )
282+ local real_payload = {
283+ key = key ,
284+ exp = ngx_time () + auth_conf .exp
285+ }
286+ if payload then
287+ local extra_payload = core .json .decode (payload )
288+ core .table .merge (real_payload , extra_payload )
289+ end
290+ return real_payload
291+ end
292+
293+
294+ local function get_auth_secret (consumer )
295+ if not consumer .auth_conf .algorithm or consumer .auth_conf .algorithm :sub (1 , 2 ) == " HS" then
296+ return get_secret (consumer .auth_conf )
297+ else
298+ return consumer .auth_conf .public_key
299+ end
300+ end
301+
302+
303+ local function gen_jwt_header (consumer )
304+ local x5c
305+ if consumer .auth_conf .algorithm and consumer .auth_conf .algorithm :sub (1 , 2 ) ~= " HS" then
306+ local public_key = consumer .auth_conf .public_key
307+ if not public_key then
308+ core .log .error (" failed to sign jwt, err: missing public key" )
309+ core .response .exit (503 , " failed to sign jwt" )
310+ end
311+ x5c = {public_key }
241312 end
313+
314+ return {
315+ typ = " JWT" ,
316+ alg = consumer .auth_conf .algorithm ,
317+ x5c = x5c
318+ }
242319end
243320
321+
244322local function find_consumer (conf , ctx )
245323 -- fetch token and hide credentials if necessary
246324 local jwt_token , err = fetch_jwt_token (conf , ctx )
@@ -249,19 +327,19 @@ local function find_consumer(conf, ctx)
249327 return nil , nil , " Missing JWT token in request"
250328 end
251329
252- local jwt_obj = jwt :load_jwt (jwt_token )
253- core .log .info (" jwt object: " , core .json .delay_encode (jwt_obj ))
254- if not jwt_obj .valid then
255- err = " JWT token invalid: " .. jwt_obj .reason
330+ local jwt , err = jwt_parser .new (jwt_token )
331+ if not jwt then
332+ err = " JWT token invalid: " .. err
256333 if auth_utils .is_running_under_multi_auth (ctx ) then
257334 return nil , nil , err
258335 end
259336 core .log .warn (err )
260337 return nil , nil , " JWT token invalid"
261338 end
339+ core .log .debug (" parsed jwt object: " , core .json .delay_encode (jwt , true ))
262340
263341 local key_claim_name = conf .key_claim_name
264- local user_key = jwt_obj .payload and jwt_obj .payload [key_claim_name ]
342+ local user_key = jwt .payload and jwt .payload [key_claim_name ]
265343 if not user_key then
266344 return nil , nil , " missing user key in JWT token"
267345 end
@@ -272,7 +350,7 @@ local function find_consumer(conf, ctx)
272350 return nil , nil , " Invalid user key in JWT token"
273351 end
274352
275- local auth_secret , err = get_auth_secret (consumer . auth_conf )
353+ local auth_secret , err = get_auth_secret (consumer )
276354 if not auth_secret then
277355 err = " failed to retrieve secrets, err: " .. err
278356 if auth_utils .is_running_under_multi_auth (ctx ) then
@@ -281,23 +359,24 @@ local function find_consumer(conf, ctx)
281359 core .log .error (err )
282360 return nil , nil , " failed to verify jwt"
283361 end
284- local claim_specs = jwt :get_default_validation_options (jwt_obj )
285- claim_specs .lifetime_grace_period = consumer .auth_conf .lifetime_grace_period
286362
287- jwt_obj = jwt :verify_jwt_obj (auth_secret , jwt_obj , claim_specs )
288- core .log .info (" jwt object: " , core .json .delay_encode (jwt_obj ))
363+ -- Now verify the JWT signature
364+ if not jwt :verify_signature (auth_secret ) then
365+ core .log .warn (" failed to verify jwt: signature mismatch: " , jwt .signature )
366+ return nil , nil , " failed to verify jwt"
367+ end
289368
290- if not jwt_obj . verified then
291- err = " failed to verify jwt: " .. jwt_obj . reason
292- if auth_utils . is_running_under_multi_auth ( ctx ) then
293- return nil , nil , err
294- end
295- core .log .warn ( err )
369+ -- Verify the JWT registered claims
370+ local ok , err = jwt :verify_claims ( conf . claims_to_verify , {
371+ lifetime_grace_period = consumer . auth_conf . lifetime_grace_period
372+ })
373+ if not ok then
374+ core .log .error ( " failed to verify jwt: " , err )
296375 return nil , nil , " failed to verify jwt"
297376 end
298377
299378 if conf .store_in_ctx then
300- ctx .jwt_auth_payload = jwt_obj .payload
379+ ctx .jwt_auth_payload = jwt .payload
301380 end
302381
303382 return consumer , consumer_conf
0 commit comments