Skip to content

Commit 173e809

Browse files
Feature: Add support for runtime variable in auth_jwt_enabled directive (#152)
Co-authored-by: Josh McCullough <[email protected]>
1 parent 070587e commit 173e809

File tree

3 files changed

+178
-132
lines changed

3 files changed

+178
-132
lines changed

src/ngx_http_auth_jwt_module.c

Lines changed: 116 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ typedef struct
2626
{
2727
ngx_str_t loginurl;
2828
ngx_str_t key;
29-
ngx_flag_t enabled;
29+
ngx_http_complex_value_t *enabled;
3030
ngx_flag_t redirect;
3131
ngx_str_t jwt_location;
3232
ngx_str_t algorithm;
@@ -83,8 +83,8 @@ static ngx_command_t auth_jwt_directives[] = {
8383
NULL},
8484

8585
{ngx_string("auth_jwt_enabled"),
86-
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_FLAG,
87-
ngx_conf_set_flag_slot,
86+
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
87+
ngx_http_set_complex_value_slot,
8888
NGX_HTTP_LOC_CONF_OFFSET,
8989
offsetof(auth_jwt_conf_t, enabled),
9090
NULL},
@@ -207,9 +207,7 @@ static void *create_conf(ngx_conf_t *cf)
207207
else
208208
{
209209
// ngx_str_t fields are initialized by the ngx_palloc call above -- only need to init flags and arrays here
210-
conf->enabled = NGX_CONF_UNSET;
211-
conf->redirect = NGX_CONF_UNSET;
212-
conf->validate_sub = NGX_CONF_UNSET;
210+
conf->enabled = NULL;
213211
conf->redirect = NGX_CONF_UNSET;
214212
conf->validate_sub = NGX_CONF_UNSET;
215213
conf->extract_var_claims = NULL;
@@ -236,20 +234,12 @@ static char *merge_conf(ngx_conf_t *cf, void *parent, void *child)
236234
merge_array(cf->pool, &conf->extract_request_claims, prev->extract_request_claims, sizeof(ngx_str_t));
237235
merge_array(cf->pool, &conf->extract_response_claims, prev->extract_response_claims, sizeof(ngx_str_t));
238236

239-
if (conf->enabled == NGX_CONF_UNSET)
240-
{
241-
conf->enabled = prev->enabled == NGX_CONF_UNSET ? 0 : prev->enabled;
242-
}
243-
244-
if (conf->redirect == NGX_CONF_UNSET)
245-
{
246-
conf->redirect = prev->redirect == NGX_CONF_UNSET ? 0 : prev->redirect;
237+
if (conf->enabled == NULL) {
238+
conf->enabled = prev->enabled;
247239
}
248240

249-
if (conf->use_keyfile == NGX_CONF_UNSET)
250-
{
251-
conf->use_keyfile = prev->use_keyfile == NGX_CONF_UNSET ? 0 : prev->use_keyfile;
252-
}
241+
ngx_conf_merge_off_value(conf->redirect, prev->redirect, 0);
242+
ngx_conf_merge_off_value(conf->use_keyfile, prev->use_keyfile, 0);
253243

254244
// If the usage of the keyfile is specified, check if the key_path is also configured
255245
if (conf->use_keyfile == 1)
@@ -461,109 +451,138 @@ static auth_jwt_ctx_t *get_or_init_jwt_module_ctx(ngx_http_request_t *r, auth_jw
461451
static auth_jwt_ctx_t *get_request_jwt_ctx(ngx_http_request_t *r)
462452
{
463453
auth_jwt_conf_t *jwtcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_jwt_module);
454+
ngx_int_t enabled = 1;
464455

465-
if (!jwtcf->enabled)
466-
{
467-
return NULL;
456+
if (jwtcf->enabled != NULL) {
457+
ngx_str_t cv;
458+
if (ngx_http_complex_value(r, jwtcf->enabled, &cv) == NGX_OK && cv.len > 0) {
459+
if (ngx_strncmp(cv.data, "off", 3) == 0) {
460+
enabled = 0;
461+
}
462+
}
468463
}
469464

470-
auth_jwt_ctx_t *ctx = get_or_init_jwt_module_ctx(r, jwtcf);
471-
472-
if (ctx == NULL)
473-
{
465+
if (!enabled) {
474466
return NULL;
475467
}
476-
else if (ctx->validation_status != NGX_AGAIN)
477-
{
478-
// we already validated and extacted everything we care about, so we just return the already-complete context
479-
return ctx;
480-
}
481-
482-
char *jwtPtr = get_jwt(r, jwtcf->jwt_location);
468+
else {
469+
auth_jwt_ctx_t *ctx = get_or_init_jwt_module_ctx(r, jwtcf);
483470

484-
if (jwtPtr == NULL)
485-
{
486-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to find a JWT");
487-
ctx->validation_status = NGX_ERROR;
488-
return ctx;
489-
}
490-
else
491-
{
492-
ngx_str_t algorithm = jwtcf->algorithm;
493-
int keyLength;
494-
u_char *key;
495-
jwt_t *jwt = NULL;
496-
497-
if (algorithm.len == 0 || (algorithm.len == 5 && ngx_strncmp(algorithm.data, "HS", 2) == 0))
471+
if (ctx == NULL) {
472+
return NULL;
473+
}
474+
else if (ctx->validation_status != NGX_AGAIN)
498475
{
499-
keyLength = jwtcf->key.len / 2;
500-
key = ngx_palloc(r->pool, keyLength);
476+
// we already validated and extacted everything we care about, so we just return the already-complete context
477+
return ctx;
478+
}
479+
else {
480+
char *jwtPtr = get_jwt(r, jwtcf->jwt_location);
501481

502-
if (0 != hex_to_binary((char *)jwtcf->key.data, key, jwtcf->key.len))
482+
if (jwtPtr == NULL)
503483
{
504-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to turn hex key into binary");
484+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to find a JWT");
505485
ctx->validation_status = NGX_ERROR;
486+
506487
return ctx;
507488
}
508-
}
509-
else if (algorithm.len == 5 && (ngx_strncmp(algorithm.data, "RS", 2) == 0 || ngx_strncmp(algorithm.data, "ES", 2) == 0))
510-
{
511-
if (jwtcf->use_keyfile == 1)
512-
{
513-
keyLength = jwtcf->_keyfile.len;
514-
key = (u_char *)jwtcf->_keyfile.data;
515-
}
516489
else
517490
{
518-
keyLength = jwtcf->key.len;
519-
key = jwtcf->key.data;
520-
}
521-
}
522-
else
523-
{
524-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unsupported algorithm %s", algorithm);
525-
ctx->validation_status = NGX_ERROR;
526-
return ctx;
527-
}
491+
ngx_str_t algorithm = jwtcf->algorithm;
492+
int keyLength;
493+
u_char *key;
494+
jwt_t *jwt = NULL;
528495

529-
if (jwt_decode(&jwt, jwtPtr, key, keyLength) != 0 || !jwt)
530-
{
531-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to parse JWT");
532-
ctx->validation_status = NGX_ERROR;
533-
}
534-
else if (validate_alg(jwtcf, jwt) != 0)
535-
{
536-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid algorithm specified");
537-
ctx->validation_status = NGX_ERROR;
538-
}
539-
else if (validate_exp(jwtcf, jwt) != 0)
540-
{
541-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the JWT has expired");
542-
ctx->validation_status = NGX_ERROR;
543-
}
544-
else if (validate_sub(jwtcf, jwt) != 0)
545-
{
546-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the JWT does not contain a subject");
547-
ctx->validation_status = NGX_ERROR;
548-
}
549-
else
550-
{
551-
extract_request_claims(r, jwtcf, jwt);
552-
extract_response_claims(r, jwtcf, jwt);
553-
ctx->validation_status = extract_var_claims(r, jwtcf, jwt, ctx);
554-
}
496+
if (algorithm.len == 0 || (algorithm.len == 5 && ngx_strncmp(algorithm.data, "HS", 2) == 0))
497+
{
498+
keyLength = jwtcf->key.len / 2;
499+
key = ngx_palloc(r->pool, keyLength);
555500

556-
jwt_free(jwt);
557-
return ctx;
501+
if (0 != hex_to_binary((char *)jwtcf->key.data, key, jwtcf->key.len))
502+
{
503+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to turn hex key into binary");
504+
ctx->validation_status = NGX_ERROR;
505+
506+
return ctx;
507+
}
508+
}
509+
else if (algorithm.len == 5 && (ngx_strncmp(algorithm.data, "RS", 2) == 0 || ngx_strncmp(algorithm.data, "ES", 2) == 0))
510+
{
511+
if (jwtcf->use_keyfile == 1)
512+
{
513+
keyLength = jwtcf->_keyfile.len;
514+
key = (u_char *)jwtcf->_keyfile.data;
515+
}
516+
else
517+
{
518+
keyLength = jwtcf->key.len;
519+
key = jwtcf->key.data;
520+
}
521+
}
522+
else
523+
{
524+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unsupported algorithm %s", algorithm);
525+
ctx->validation_status = NGX_ERROR;
526+
527+
return ctx;
528+
}
529+
530+
if (jwt_decode(&jwt, jwtPtr, key, keyLength) != 0 || !jwt)
531+
{
532+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to parse JWT");
533+
ctx->validation_status = NGX_ERROR;
534+
}
535+
else if (validate_alg(jwtcf, jwt) != 0)
536+
{
537+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid algorithm specified");
538+
ctx->validation_status = NGX_ERROR;
539+
}
540+
else if (validate_exp(jwtcf, jwt) != 0)
541+
{
542+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the JWT has expired");
543+
ctx->validation_status = NGX_ERROR;
544+
}
545+
else if (validate_sub(jwtcf, jwt) != 0)
546+
{
547+
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the JWT does not contain a subject");
548+
ctx->validation_status = NGX_ERROR;
549+
}
550+
else
551+
{
552+
extract_request_claims(r, jwtcf, jwt);
553+
extract_response_claims(r, jwtcf, jwt);
554+
ctx->validation_status = extract_var_claims(r, jwtcf, jwt, ctx);
555+
}
556+
557+
jwt_free(jwt);
558+
559+
return ctx;
560+
}
561+
}
558562
}
559563
}
560564

561565
static ngx_int_t handle_request(ngx_http_request_t *r)
562566
{
563567
auth_jwt_conf_t *jwtcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_jwt_module);
568+
569+
// Only activate JWT logic if key or keyfile_path is set
570+
if (jwtcf->key.len == 0 && jwtcf->keyfile_path.len == 0) {
571+
return NGX_DECLINED;
572+
}
573+
564574
auth_jwt_ctx_t *ctx = get_request_jwt_ctx(r);
565575

566-
if (!jwtcf->enabled)
576+
ngx_int_t enabled = 1;
577+
if (jwtcf->enabled != NULL) {
578+
ngx_str_t cv;
579+
if (ngx_http_complex_value(r, jwtcf->enabled, &cv) == NGX_OK && cv.len > 0) {
580+
if (ngx_strncmp(cv.data, "off", 3) == 0) {
581+
enabled = 0;
582+
}
583+
}
584+
}
585+
if (!enabled)
567586
{
568587
return NGX_DECLINED;
569588
}

test/etc/nginx/conf.d/test.conf

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
error_log /var/log/nginx/debug.log debug;
22
access_log /var/log/nginx/access.log;
33

4-
log_format extractTest 'Log extract test sub: $jwt_claim_sub';
4+
log_format extract_test 'Log extract test sub: $jwt_claim_sub';
55

66
server {
77
listen %{PORT};
@@ -468,8 +468,21 @@ vXjq39xtcIBRTO1c2zs=
468468
auth_jwt_validate_sub on;
469469
auth_jwt_extract_var_claims sub;
470470

471-
access_log /var/log/nginx/test_access.log extractTest;
471+
access_log /var/log/nginx/test_access.log extract_test;
472472

473473
return 200 "Log extract test sub: $jwt_claim_sub";
474474
}
475+
476+
location /enabled/variable {
477+
auth_jwt_enabled $jwt_enabled;
478+
479+
alias /usr/share/nginx/html/;
480+
try_files index.html =404;
481+
}
482+
}
483+
484+
map $http_test_auth_enabled $jwt_enabled {
485+
default on;
486+
on on;
487+
off off;
475488
}

0 commit comments

Comments
 (0)