Skip to content

Commit b2ec2bb

Browse files
rename auth_jwt_authorization_type to auth_jwt_location and support pulling JWT from any header (#90)
1 parent b888c93 commit b2ec2bb

File tree

5 files changed

+81
-56
lines changed

5 files changed

+81
-56
lines changed

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ MAJ=$(echo ${NGINX_VERSION} | cut -f1 -d.)
3636
MIN=$(echo ${NGINX_VERSION} | cut -f2 -d.)
3737
REV=$(echo ${NGINX_VERSION} | cut -f3 -d.)
3838

39-
# NGINX 1.23.0+ changes `cookies` to `cookie`
39+
# NGINX 1.23.0+ changes cookies to use a linked list, and renames `cookies` to `cookie`
4040
if [ "${MAJ}" -gt 1 ] || [ "${MAJ}" -eq 1 -a "${MIN}" -ge 23 ]; then
4141
BUILD_FLAGS="${BUILD_FLAGS} --with-cc-opt='-DNGX_LINKED_LIST_COOKIES=1'"
4242
fi

README.md

+4-3
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ This module requires several new `nginx.conf` directives, which can be specified
2121
| `auth_jwt_loginurl` | The URL to redirect to if `auth_jwt_redirect` is enabled and authentication fails. |
2222
| `auth_jwt_enabled` | Set to "on" to enable JWT checking. |
2323
| `auth_jwt_algorithm` | The algorithm to use. One of: HS256, HS384, HS512, RS256, RS384, RS512 |
24-
| `auth_jwt_validation_type` | Indicates where the JWT is located in the request -- see below. |
24+
| `auth_jwt_location` | Indicates where the JWT is located in the request -- see below. |
2525
| `auth_jwt_validate_sub` | Set to "on" to validate the `sub` claim (e.g. user id) in the JWT. |
2626
| `auth_jwt_extract_request_claims` | Set to a space-delimited list of claims to extract from the JWT and set as request headers. These will be accessible via e.g: `$http_jwt_sub` |
2727
| `auth_jwt_extract_response_claims` | Set to a space-delimited list of claims to extract from the JWT and set as response headers. These will be accessible via e.g: `$sent_http_jwt_sub` |
@@ -67,10 +67,11 @@ auth_jwt_redirect off;
6767
```
6868
## JWT Locations
6969

70-
By default, the authorization header is used to provide a JWT for validation. However, you may use the `auth_jwt_validation_type` configuration to specify the name of a cookie that provides the JWT:
70+
By default, the`Authorization` header is used to provide a JWT for validation. However, you may use the `auth_jwt_location` directive to specify the name of the header or cookie which provides the JWT:
7171

7272
```nginx
73-
auth_jwt_validation_type COOKIE=jwt;
73+
auth_jwt_location HEADER=auth-token; # get the JWT from the "auth-token" header
74+
auth_jwt_location COOKIE=auth-token; # get the JWT from the "auth-token" cookie
7475
```
7576

7677
## `sub` Validation

src/ngx_http_auth_jwt_module.c

+29-30
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ typedef struct
2727
ngx_str_t key;
2828
ngx_flag_t enabled;
2929
ngx_flag_t redirect;
30-
ngx_str_t validation_type;
30+
ngx_str_t jwt_location;
3131
ngx_str_t algorithm;
3232
ngx_flag_t validate_sub;
3333
ngx_array_t *extract_request_claims;
@@ -51,9 +51,9 @@ static void extract_response_claims(ngx_http_request_t *r, auth_jwt_conf_t *jwtc
5151
static ngx_int_t free_jwt_and_redirect(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf, jwt_t *jwt);
5252
static ngx_int_t redirect(ngx_http_request_t *r, auth_jwt_conf_t *jwtcf);
5353
static ngx_int_t load_public_key(ngx_conf_t *cf, auth_jwt_conf_t *conf);
54-
static char *get_jwt(ngx_http_request_t *r, ngx_str_t validation_type);
54+
static char *get_jwt(ngx_http_request_t *r, ngx_str_t jwt_location);
5555

56-
static char *JWT_HEADER_PREFIX = "JWT-";
56+
static const char *JWT_HEADER_PREFIX = "JWT-";
5757

5858
static ngx_command_t auth_jwt_directives[] = {
5959
{ngx_string("auth_jwt_loginurl"),
@@ -84,11 +84,11 @@ static ngx_command_t auth_jwt_directives[] = {
8484
offsetof(auth_jwt_conf_t, redirect),
8585
NULL},
8686

87-
{ngx_string("auth_jwt_validation_type"),
87+
{ngx_string("auth_jwt_location"),
8888
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
8989
ngx_conf_set_str_slot,
9090
NGX_HTTP_LOC_CONF_OFFSET,
91-
offsetof(auth_jwt_conf_t, validation_type),
91+
offsetof(auth_jwt_conf_t, jwt_location),
9292
NULL},
9393

9494
{ngx_string("auth_jwt_algorithm"),
@@ -208,7 +208,7 @@ static char *merge_conf(ngx_conf_t *cf, void *parent, void *child)
208208

209209
ngx_conf_merge_str_value(conf->loginurl, prev->loginurl, "");
210210
ngx_conf_merge_str_value(conf->key, prev->key, "");
211-
ngx_conf_merge_str_value(conf->validation_type, prev->validation_type, "");
211+
ngx_conf_merge_str_value(conf->jwt_location, prev->jwt_location, "HEADER=Authorization");
212212
ngx_conf_merge_str_value(conf->algorithm, prev->algorithm, "HS256");
213213
ngx_conf_merge_str_value(conf->keyfile_path, prev->keyfile_path, "");
214214
ngx_conf_merge_off_value(conf->validate_sub, prev->validate_sub, 0);
@@ -311,7 +311,7 @@ static ngx_int_t handle_request(ngx_http_request_t *r)
311311
}
312312
else
313313
{
314-
char *jwtPtr = get_jwt(r, jwtcf->validation_type);
314+
char *jwtPtr = get_jwt(r, jwtcf->jwt_location);
315315

316316
if (jwtPtr == NULL)
317317
{
@@ -608,51 +608,50 @@ static ngx_int_t load_public_key(ngx_conf_t *cf, auth_jwt_conf_t *conf)
608608
}
609609
}
610610

611-
static char *get_jwt(ngx_http_request_t *r, ngx_str_t validation_type)
611+
static char *get_jwt(ngx_http_request_t *r, ngx_str_t jwt_location)
612612
{
613+
static const char *HEADER_PREFIX = "HEADER=";
614+
static const char *BEARER_PREFIX = "Bearer ";
615+
static const char *COOKIE_PREFIX = "COOKIE=";
613616
char *jwtPtr = NULL;
614617

615-
ngx_log_debug(NGX_LOG_DEBUG, r->connection->log, 0, "validation_type.len %d", validation_type.len);
618+
ngx_log_debug(NGX_LOG_DEBUG, r->connection->log, 0, "jwt_location.len %d", jwt_location.len);
616619

617-
if (validation_type.len == 0 || (validation_type.len == sizeof("AUTHORIZATION") - 1 && ngx_strncmp(validation_type.data, "AUTHORIZATION", sizeof("AUTHORIZATION") - 1) == 0))
620+
if (jwt_location.len > sizeof(HEADER_PREFIX) && ngx_strncmp(jwt_location.data, HEADER_PREFIX, sizeof(HEADER_PREFIX) - 1) == 0)
618621
{
619-
static const ngx_str_t authorizationHeaderName = ngx_string("Authorization");
620-
const ngx_table_elt_t *authorizationHeader = search_headers_in(r, authorizationHeaderName.data, authorizationHeaderName.len);
622+
ngx_table_elt_t *jwtHeaderVal;
621623

622-
if (authorizationHeader != NULL)
623-
{
624-
ngx_int_t bearer_length = authorizationHeader->value.len - (sizeof("Bearer ") - 1);
624+
jwt_location.data += sizeof(HEADER_PREFIX) - 1;
625+
jwt_location.len -= sizeof(HEADER_PREFIX) - 1;
625626

626-
ngx_log_debug(NGX_LOG_DEBUG, r->connection->log, 0, "Found authorization header len %d", authorizationHeader->value.len);
627+
jwtHeaderVal = search_headers_in(r, jwt_location.data, jwt_location.len);
627628

628-
if (bearer_length > 0)
629+
if (jwtHeaderVal != NULL)
630+
{
631+
if (ngx_strncmp(jwtHeaderVal->value.data, BEARER_PREFIX, sizeof(BEARER_PREFIX) - 1) == 0)
629632
{
630-
ngx_str_t authorizationHeaderStr;
631-
632-
authorizationHeaderStr.data = authorizationHeader->value.data + sizeof("Bearer ") - 1;
633-
authorizationHeaderStr.len = bearer_length;
634-
635-
jwtPtr = ngx_str_t_to_char_ptr(r->pool, authorizationHeaderStr);
636-
637-
ngx_log_debug(NGX_LOG_DEBUG, r->connection->log, 0, "Authorization header: %s", jwtPtr);
633+
jwtHeaderVal->value.data += sizeof(BEARER_PREFIX) - 1;
634+
jwtHeaderVal->value.len -= sizeof(BEARER_PREFIX) - 1;
638635
}
636+
637+
jwtPtr = ngx_str_t_to_char_ptr(r->pool, jwtHeaderVal->value);
639638
}
640639
}
641-
else if (validation_type.len > sizeof("COOKIE=") && ngx_strncmp(validation_type.data, "COOKIE=", sizeof("COOKIE=") - 1) == 0)
640+
else if (jwt_location.len > sizeof(COOKIE_PREFIX) && ngx_strncmp(jwt_location.data, COOKIE_PREFIX, sizeof(COOKIE_PREFIX) - 1) == 0)
642641
{
643642
bool has_cookie = false;
644643
ngx_str_t jwtCookieVal;
645644

646-
validation_type.data += sizeof("COOKIE=") - 1;
647-
validation_type.len -= sizeof("COOKIE=") - 1;
645+
jwt_location.data += sizeof(COOKIE_PREFIX) - 1;
646+
jwt_location.len -= sizeof(COOKIE_PREFIX) - 1;
648647

649648
#ifndef NGX_LINKED_LIST_COOKIES
650-
if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &validation_type, &jwtCookieVal) != NGX_DECLINED)
649+
if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &jwt_location, &jwtCookieVal) != NGX_DECLINED)
651650
{
652651
has_cookie = true;
653652
}
654653
#else
655-
if (ngx_http_parse_multi_header_lines(r, r->headers_in.cookie, &validation_type, &jwtCookieVal) != NULL)
654+
if (ngx_http_parse_multi_header_lines(r, r->headers_in.cookie, &jwt_location, &jwtCookieVal) != NULL)
656655
{
657656
has_cookie = true;
658657
}

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

+28-18
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ server {
1717
location /secure/cookie/default {
1818
auth_jwt_enabled on;
1919
auth_jwt_redirect on;
20-
auth_jwt_validation_type COOKIE=jwt;
20+
auth_jwt_location COOKIE=jwt;
2121

2222
alias /usr/share/nginx/html/;
2323
try_files index.html =404;
@@ -27,7 +27,7 @@ server {
2727
auth_jwt_enabled on;
2828
auth_jwt_redirect on;
2929
auth_jwt_validate_sub on;
30-
auth_jwt_validation_type COOKIE=jwt;
30+
auth_jwt_location COOKIE=jwt;
3131

3232
alias /usr/share/nginx/html/;
3333
try_files index.html =404;
@@ -36,7 +36,7 @@ server {
3636
location /secure/cookie/default/no-redirect {
3737
auth_jwt_enabled on;
3838
auth_jwt_redirect off;
39-
auth_jwt_validation_type COOKIE=jwt;
39+
auth_jwt_location COOKIE=jwt;
4040

4141
alias /usr/share/nginx/html/;
4242
try_files index.html =404;
@@ -45,7 +45,7 @@ server {
4545
location /secure/cookie/hs256 {
4646
auth_jwt_enabled on;
4747
auth_jwt_redirect on;
48-
auth_jwt_validation_type COOKIE=jwt;
48+
auth_jwt_location COOKIE=jwt;
4949
auth_jwt_algorithm HS256;
5050

5151
alias /usr/share/nginx/html/;
@@ -55,7 +55,7 @@ server {
5555
location /secure/cookie/hs384 {
5656
auth_jwt_enabled on;
5757
auth_jwt_redirect on;
58-
auth_jwt_validation_type COOKIE=jwt;
58+
auth_jwt_location COOKIE=jwt;
5959
auth_jwt_algorithm HS384;
6060

6161
alias /usr/share/nginx/html/;
@@ -65,7 +65,7 @@ server {
6565
location /secure/cookie/hs512 {
6666
auth_jwt_enabled on;
6767
auth_jwt_redirect on;
68-
auth_jwt_validation_type COOKIE=jwt;
68+
auth_jwt_location COOKIE=jwt;
6969
auth_jwt_algorithm HS512;
7070

7171
alias /usr/share/nginx/html/;
@@ -75,7 +75,7 @@ server {
7575
location /secure/auth-header/default {
7676
auth_jwt_enabled on;
7777
auth_jwt_redirect on;
78-
auth_jwt_validation_type AUTHORIZATION;
78+
auth_jwt_location HEADER=Authorization;
7979

8080
alias /usr/share/nginx/html/;
8181
try_files index.html =404;
@@ -84,7 +84,7 @@ server {
8484
location /secure/auth-header/default/no-redirect {
8585
auth_jwt_enabled on;
8686
auth_jwt_redirect off;
87-
auth_jwt_validation_type AUTHORIZATION;
87+
auth_jwt_location HEADER=Authorization;
8888

8989
alias /usr/share/nginx/html/;
9090
try_files index.html =404;
@@ -93,7 +93,7 @@ server {
9393
location /secure/auth-header/rs256 {
9494
auth_jwt_enabled on;
9595
auth_jwt_redirect on;
96-
auth_jwt_validation_type AUTHORIZATION;
96+
auth_jwt_location HEADER=Authorization;
9797
auth_jwt_key "-----BEGIN PUBLIC KEY-----
9898
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwtpMAM4l1H995oqlqdMh
9999
uqNuffp4+4aUCwuFE9B5s9MJr63gyf8jW0oDr7Mb1Xb8y9iGkWfhouZqNJbMFry+
@@ -111,7 +111,7 @@ BwIDAQAB
111111
location /secure/auth-header/rs256/file {
112112
auth_jwt_enabled on;
113113
auth_jwt_redirect on;
114-
auth_jwt_validation_type AUTHORIZATION;
114+
auth_jwt_location HEADER=Authorization;
115115
auth_jwt_algorithm RS256;
116116
auth_jwt_use_keyfile on;
117117
auth_jwt_keyfile_path "/etc/nginx/rsa-key.conf";
@@ -123,7 +123,7 @@ BwIDAQAB
123123
location /secure/auth-header/rs384/file {
124124
auth_jwt_enabled on;
125125
auth_jwt_redirect on;
126-
auth_jwt_validation_type AUTHORIZATION;
126+
auth_jwt_location HEADER=Authorization;
127127
auth_jwt_algorithm RS384;
128128
auth_jwt_use_keyfile on;
129129
auth_jwt_keyfile_path "/etc/nginx/rsa-key.conf";
@@ -135,7 +135,7 @@ BwIDAQAB
135135
location /secure/auth-header/rs512/file {
136136
auth_jwt_enabled on;
137137
auth_jwt_redirect on;
138-
auth_jwt_validation_type AUTHORIZATION;
138+
auth_jwt_location HEADER=Authorization;
139139
auth_jwt_algorithm RS512;
140140
auth_jwt_use_keyfile on;
141141
auth_jwt_keyfile_path "/etc/nginx/rsa-key.conf";
@@ -144,10 +144,20 @@ BwIDAQAB
144144
try_files index.html =404;
145145
}
146146

147+
location /secure/custom-header/hs256 {
148+
auth_jwt_enabled on;
149+
auth_jwt_redirect on;
150+
auth_jwt_location HEADER=Auth-Token;
151+
auth_jwt_algorithm HS256;
152+
153+
alias /usr/share/nginx/html/;
154+
try_files index.html =404;
155+
}
156+
147157
location /secure/extract-claim/request/sub {
148158
auth_jwt_enabled on;
149159
auth_jwt_redirect off;
150-
auth_jwt_validation_type AUTHORIZATION;
160+
auth_jwt_location HEADER=Authorization;
151161
auth_jwt_extract_request_claims sub;
152162

153163
add_header "Test" "sub=$http_jwt_sub";
@@ -159,7 +169,7 @@ BwIDAQAB
159169
location /secure/extract-claim/request/name-1 {
160170
auth_jwt_enabled on;
161171
auth_jwt_redirect off;
162-
auth_jwt_validation_type AUTHORIZATION;
172+
auth_jwt_location HEADER=Authorization;
163173
auth_jwt_extract_request_claims firstName lastName;
164174

165175
add_header "Test" "$http_jwt_firstname $http_jwt_lastname";
@@ -171,7 +181,7 @@ BwIDAQAB
171181
location /secure/extract-claim/request/name-2 {
172182
auth_jwt_enabled on;
173183
auth_jwt_redirect off;
174-
auth_jwt_validation_type AUTHORIZATION;
184+
auth_jwt_location HEADER=Authorization;
175185
auth_jwt_extract_request_claims firstName;
176186
auth_jwt_extract_request_claims lastName;
177187

@@ -184,7 +194,7 @@ BwIDAQAB
184194
location /secure/extract-claim/response/sub {
185195
auth_jwt_enabled on;
186196
auth_jwt_redirect off;
187-
auth_jwt_validation_type AUTHORIZATION;
197+
auth_jwt_location HEADER=Authorization;
188198
auth_jwt_extract_response_claims sub;
189199

190200
add_header "Test" "sub=$sent_http_jwt_sub";
@@ -196,7 +206,7 @@ BwIDAQAB
196206
location /secure/extract-claim/response/name-1 {
197207
auth_jwt_enabled on;
198208
auth_jwt_redirect off;
199-
auth_jwt_validation_type AUTHORIZATION;
209+
auth_jwt_location HEADER=Authorization;
200210
auth_jwt_extract_response_claims firstName lastName;
201211

202212
add_header "Test" "$sent_http_jwt_firstname $sent_http_jwt_lastname";
@@ -208,7 +218,7 @@ BwIDAQAB
208218
location /secure/extract-claim/response/name-2 {
209219
auth_jwt_enabled on;
210220
auth_jwt_redirect off;
211-
auth_jwt_validation_type AUTHORIZATION;
221+
auth_jwt_location HEADER=Authorization;
212222
auth_jwt_extract_response_claims firstName;
213223
auth_jwt_extract_response_claims lastName;
214224

test/test.sh

+19-4
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,15 @@ main() {
109109
-p '/secure/auth-header/default' \
110110
-c '302'
111111

112-
run_test -n 'when auth enabled with default algorithm with no redirect and Authorization header missing Bearer, should return 401' \
112+
run_test -n 'when auth enabled with default algorithm with no redirect and Authorization header missing Bearer, should return 200' \
113113
-p '/secure/auth-header/default/no-redirect' \
114-
-c '401' \
115-
-x '--header "Authorization: X"'
114+
-c '200' \
115+
-x "--header \"Authorization: ${JWT_HS256_VALID}\""
116+
117+
run_test -n 'when auth enabled with default algorithm with no redirect and Authorization header with Bearer, should return 200' \
118+
-p '/secure/auth-header/default/no-redirect' \
119+
-c '200' \
120+
-x "--header \"Authorization: Bearer ${JWT_HS256_VALID}\""
116121

117122
run_test -n 'when auth enabled with default algorithm and no JWT cookie, returns 302' \
118123
-p '/secure/cookie/default' \
@@ -143,7 +148,7 @@ main() {
143148
-x ' --cookie "jwt=${JWT_HS256_MISSING_EMAIL}"'
144149

145150
run_test -n 'when auth enabled with HS256 algorithm and valid JWT cookie, returns 200' \
146-
-p '/secure/cookie/hs256/' \
151+
-p '/secure/cookie/hs256' \
147152
-c '200' \
148153
-x '--cookie "jwt=${JWT_HS256_VALID}"'
149154

@@ -182,6 +187,16 @@ main() {
182187
-c '200' \
183188
-x '--header "Authorization: Bearer ${JWT_RS256_VALID}"'
184189

190+
run_test -n 'when auth enabled with HS256 algorithm and valid JWT in custom header without bearer, returns 200' \
191+
-p '/secure/custom-header/hs256/' \
192+
-c '200' \
193+
-x '--header "Auth-Token: ${JWT_HS256_VALID}"'
194+
195+
run_test -n 'when auth enabled with HS256 algorithm and valid JWT in custom header with bearer, returns 200' \
196+
-p '/secure/custom-header/hs256/' \
197+
-c '200' \
198+
-x '--header "Auth-Token: Bearer ${JWT_HS256_VALID}"'
199+
185200
run_test -n 'extracts single claim to request header' \
186201
-p '/secure/extract-claim/request/sub' \
187202
-r '^Test: sub=some-long-uuid$' \

0 commit comments

Comments
 (0)