Skip to content

Commit

Permalink
add option to not extract sub -- fixes #66 (#70)
Browse files Browse the repository at this point in the history
* add option to not extract sub

* move email variables into block where they are used

* cleanup
  • Loading branch information
JoshMcCullough authored Jun 8, 2022
1 parent 60b6f4b commit aa024c5
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 29 deletions.
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ which can be specified in on the `main` `server` or `location` level.
auth_jwt_key "00112233445566778899AABBCCDDEEFF00112233445566778899AABBCCDDEEFF"; # see docs for format based on algorithm
auth_jwt_loginurl "https://yourdomain.com/loginpage";
auth_jwt_enabled on;
auth_jwt_algorithm HS256; # or RS256
auth_jwt_algorithm HS256; # or RS256
auth_jwt_extract_sub on; # or off
auth_jwt_validate_email on; # or off
auth_jwt_use_keyfile off; # or on
auth_jwt_use_keyfile off; # or on
auth_jwt_keyfile_path "/app/pub_key";
```

Expand Down Expand Up @@ -87,9 +88,17 @@ auth_jwt_validation_type COOKIE=rampartjwt;
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.

```
auth_jwt_extract_sub
```
By default, the module will attempt to extract the `sub` claim (e.g. the user's id) from the JWT. If successful, the
value will be set in the `x-userid` HTTP header. An error will be logged if this option is enabled and the JWT does not
contain the `sub` claim.

```
auth_jwt_validate_email off;
```
By default, the module will attempt to validate the email address field of the JWT, then set the x-email header of the
session, and will log an error if it isn't found. To disable this behavior, for instance if you are using a different
user identifier property such as 'sub', set `auth_jwt_validate_email` to the value `off`.
user identifier property such as `sub`, set `auth_jwt_validate_email` to the value `off`. _Note that this flag may be
renamed to `auth_jwt_extract_email` in a future release._
55 changes: 32 additions & 23 deletions src/ngx_http_auth_jwt_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ typedef struct {
ngx_flag_t auth_jwt_redirect;
ngx_str_t auth_jwt_validation_type;
ngx_str_t auth_jwt_algorithm;
ngx_flag_t auth_jwt_extract_sub;
ngx_flag_t auth_jwt_validate_email;
ngx_str_t auth_jwt_keyfile_path;
ngx_flag_t auth_jwt_use_keyfile;
Expand Down Expand Up @@ -84,6 +85,13 @@ static ngx_command_t ngx_http_auth_jwt_commands[] = {
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_algorithm),
NULL },

{ ngx_string("auth_jwt_extract_sub"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_extract_sub),
NULL },

{ ngx_string("auth_jwt_validate_email"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
Expand Down Expand Up @@ -152,10 +160,6 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
jwt_t *jwt = NULL;
int jwtParseReturnCode;
jwt_alg_t alg;
const char* sub;
const char* email;
ngx_str_t sub_t;
ngx_str_t email_t;
time_t exp;
time_t now;
ngx_str_t auth_jwt_algorithm;
Expand All @@ -175,6 +179,7 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
}

jwtCookieValChrPtr = getJwt(r, jwtcf->auth_jwt_validation_type);

if (jwtCookieValChrPtr == NULL)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to find a jwt");
Expand All @@ -184,6 +189,7 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
// convert key from hex to binary, if a symmetric key

auth_jwt_algorithm = jwtcf->auth_jwt_algorithm;

if (auth_jwt_algorithm.len == 0 || (auth_jwt_algorithm.len == sizeof("HS256") - 1 && ngx_strncmp(auth_jwt_algorithm.data, "HS256", sizeof("HS256") - 1)==0))
{
keylen = jwtcf->auth_jwt_key.len / 2;
Expand Down Expand Up @@ -218,6 +224,7 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)

// validate the jwt
jwtParseReturnCode = jwt_decode(&jwt, jwtCookieValChrPtr, keyBinary, keylen);

if (jwtParseReturnCode != 0)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to parse jwt");
Expand All @@ -226,6 +233,7 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)

// validate the algorithm
alg = jwt_get_alg(jwt);

if (alg != JWT_ALG_HS256 && alg != JWT_ALG_RS256)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "invalid algorithm in jwt %d", alg);
Expand All @@ -235,45 +243,51 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
// validate the exp date of the JWT
exp = (time_t)jwt_get_grant_int(jwt, "exp");
now = time(NULL);

if (exp < now)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the jwt has expired");
goto redirect;
}

// extract the userid
sub = jwt_get_grant(jwt, "sub");
if (sub == NULL)
if (jwtcf->auth_jwt_extract_sub == 1)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the jwt does not contain a subject");
}
else
{
sub_t = ngx_char_ptr_to_str_t(r->pool, (char *)sub);
set_custom_header_in_headers_out(r, &useridHeaderName, &sub_t);
const char* sub = jwt_get_grant(jwt, "sub");

if (sub == NULL)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the jwt does not contain a subject");
}
else
{
ngx_str_t sub_t = ngx_char_ptr_to_str_t(r->pool, (char *)sub);

set_custom_header_in_headers_out(r, &useridHeaderName, &sub_t);
}
}

if (jwtcf->auth_jwt_validate_email == 1)
{
email = jwt_get_grant(jwt, "emailAddress");
const char* email = jwt_get_grant(jwt, "emailAddress");

if (email == NULL)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the jwt does not contain an email address");
}
else
{
email_t = ngx_char_ptr_to_str_t(r->pool, (char *)email);
ngx_str_t email_t = ngx_char_ptr_to_str_t(r->pool, (char *)email);

set_custom_header_in_headers_out(r, &emailHeaderName, &email_t);
}
}

jwt_free(jwt);


return NGX_OK;

redirect:

if (jwt)
{
jwt_free(jwt);
Expand Down Expand Up @@ -303,7 +317,6 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
uintptr_t escaped_len;

loginlen = jwtcf->auth_jwt_loginurl.len;

scheme = (r->connection->ssl) ? "https" : "http";
server = r->headers_in.server;

Expand All @@ -318,15 +331,11 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
uri.data = ngx_palloc(r->pool, request_uri_var->len);
uri.len = request_uri_var->len;
ngx_memcpy(uri.data, request_uri_var->data, request_uri_var->len);

// ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "found uri with querystring %s", ngx_str_t_to_char_ptr(r->pool, uri));
}
else
{
// fallback to the querystring without params
uri = r->uri;

// ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "fallback to querystring without params");
}

// escape the URI
Expand All @@ -350,8 +359,6 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
ngx_memcpy(return_url+return_url_idx, uri_escaped.data, uri_escaped.len);
return_url_idx += uri_escaped.len;
r->headers_out.location->value.data = (u_char *)return_url;

// ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "return_url: %s", ngx_str_t_to_char_ptr(r->pool, r->headers_out.location->value));
}
else
{
Expand Down Expand Up @@ -403,6 +410,7 @@ ngx_http_auth_jwt_create_loc_conf(ngx_conf_t *cf)
// set the flag to unset
conf->auth_jwt_enabled = (ngx_flag_t) -1;
conf->auth_jwt_redirect = (ngx_flag_t) -1;
conf->auth_jwt_extract_sub = (ngx_flag_t) -1;
conf->auth_jwt_validate_email = (ngx_flag_t) -1;
conf->auth_jwt_use_keyfile = (ngx_flag_t) -1;

Expand Down Expand Up @@ -453,6 +461,7 @@ ngx_http_auth_jwt_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_str_value(conf->auth_jwt_validation_type, prev->auth_jwt_validation_type, "");
ngx_conf_merge_str_value(conf->auth_jwt_algorithm, prev->auth_jwt_algorithm, "HS256");
ngx_conf_merge_str_value(conf->auth_jwt_keyfile_path, prev->auth_jwt_keyfile_path, "");
ngx_conf_merge_off_value(conf->auth_jwt_extract_sub, prev->auth_jwt_extract_sub, 1);
ngx_conf_merge_off_value(conf->auth_jwt_validate_email, prev->auth_jwt_validate_email, 1);

if (conf->auth_jwt_enabled == ((ngx_flag_t) -1))
Expand Down
5 changes: 2 additions & 3 deletions test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ test_jwt () {
local extra=$4

cmd="curl -X GET -o /dev/null --silent --head --write-out '%{http_code}' http://nginx:8000$path -H 'cache-control: no-cache' $extra"


test=$( eval ${cmd} )
if [ "$test" -eq "$expect" ];then

if [ "$test" -eq "$expect" ]; then
echo -e "${GREEN}${name}: passed (${test})${NONE}";
else
echo -e "${RED}${name}: failed (${test})${NONE}";
Expand Down

0 comments on commit aa024c5

Please sign in to comment.