Skip to content

Commit a2c658f

Browse files
committed
I don't know how complete this is...
I was working on this a long time ago and I hadn't finished it or tested it at all... I wanted to commit it before this was lost.
1 parent 95b2e1e commit a2c658f

File tree

1 file changed

+188
-38
lines changed

1 file changed

+188
-38
lines changed

src/ngx_http_auth_jwt_module.c

Lines changed: 188 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
/*
22
* Tesla Government
33
* @author joefitz
4+
*
5+
* To compile you may need to export paths to include openssl:
6+
*
7+
* export LIBRARY_PATH=/usr/local/Cellar/openssl/1.0.2j/include
8+
* export LD_LIBRARY_PATH=/usr/local/Cellar/openssl/1.0.2j/include
9+
* export C_INCLUDE_PATH=/usr/local/Cellar/openssl/1.0.2j/include
410
*/
511

612
#include <ngx_config.h>
@@ -10,10 +16,19 @@
1016

1117
#include <jansson.h>
1218

19+
#define TOKEN_PERIOD_MINUTES 20
20+
1321
typedef struct {
1422
ngx_str_t auth_jwt_loginurl;
1523
ngx_str_t auth_jwt_key;
1624
ngx_flag_t auth_jwt_enabled;
25+
ngx_str_t auth_jwt_cookie_name;
26+
ngx_str_t auth_jwt_cookie_legacy_name;
27+
ngx_flag_t auth_jwt_cookie_reissue_enabled;
28+
ngx_str_t auth_jwt_cookie_reissue_domain;
29+
ngx_flag_t auth_jwt_cookie_reissue_secure;
30+
ngx_flag_t auth_jwt_cookie_reissue_httponly;
31+
1732
} ngx_http_auth_jwt_loc_conf_t;
1833

1934
static ngx_int_t ngx_http_auth_jwt_init(ngx_conf_t *cf);
@@ -38,15 +53,57 @@ static ngx_command_t ngx_http_auth_jwt_commands[] = {
3853
NGX_HTTP_LOC_CONF_OFFSET,
3954
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_key),
4055
NULL },
41-
56+
4257
{ ngx_string("auth_jwt_enabled"),
4358
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
4459
ngx_conf_set_flag_slot,
4560
NGX_HTTP_LOC_CONF_OFFSET,
4661
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_enabled),
4762
NULL },
4863

49-
ngx_null_command
64+
{ ngx_string("auth_jwt_cookie_name"),
65+
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
66+
ngx_conf_set_str_slot,
67+
NGX_HTTP_LOC_CONF_OFFSET,
68+
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_cookie_name),
69+
NULL },
70+
71+
{ ngx_string("auth_jwt_cookie_legacy_name"),
72+
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
73+
ngx_conf_set_str_slot,
74+
NGX_HTTP_LOC_CONF_OFFSET,
75+
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_cookie_legacy_name),
76+
NULL },
77+
78+
{ ngx_string("auth_jwt_cookie_reissue_enabled"),
79+
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
80+
ngx_conf_set_flag_slot,
81+
NGX_HTTP_LOC_CONF_OFFSET,
82+
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_cookie_reissue_enabled),
83+
NULL },
84+
85+
{ ngx_string("auth_jwt_cookie_reissue_domain"),
86+
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
87+
ngx_conf_set_str_slot,
88+
NGX_HTTP_LOC_CONF_OFFSET,
89+
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_cookie_reissue_domain),
90+
NULL },
91+
92+
{ ngx_string("auth_jwt_cookie_reissue_secure"),
93+
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
94+
ngx_conf_set_flag_slot,
95+
NGX_HTTP_LOC_CONF_OFFSET,
96+
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_cookie_reissue_secure),
97+
NULL },
98+
99+
{ ngx_string("auth_jwt_cookie_reissue_httponly"),
100+
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
101+
ngx_conf_set_flag_slot,
102+
NGX_HTTP_LOC_CONF_OFFSET,
103+
offsetof(ngx_http_auth_jwt_loc_conf_t, auth_jwt_cookie_reissue_httponly),
104+
NULL },
105+
106+
ngx_null_command
50107
};
51108

52109

@@ -84,19 +141,20 @@ ngx_module_t ngx_http_auth_jwt_module = {
84141
static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
85142
{
86143
ngx_int_t n;
87-
ngx_str_t jwtCookieName = ngx_string("rampartjwt");
88-
ngx_str_t passportKeyCookieName = ngx_string("PassportKey");
144+
// ngx_str_t jwtCookieName = ngx_string("rampartjwt");
145+
// ngx_str_t passportKeyCookieName = ngx_string("PassportKey");
89146
ngx_str_t jwtCookieVal;
90147
char* jwtCookieValChrPtr;
91148
char* return_url;
92149
ngx_http_auth_jwt_loc_conf_t *jwtcf;
93150
u_char *keyBinary;
94-
jwt_t *jwt;
151+
jwt_t *jwt = NULL;
95152
int jwtParseReturnCode;
96153
jwt_alg_t alg;
97154
time_t exp;
98155
time_t now;
99-
156+
double expseconds_from_now;
157+
100158
jwtcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_jwt_module);
101159

102160
if (!jwtcf->auth_jwt_enabled)
@@ -110,11 +168,11 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
110168

111169
// get the cookie
112170
// TODO: the cookie name could be passed in dynamicallly
113-
n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &jwtCookieName, &jwtCookieVal);
171+
n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &jwtcf->auth_jwt_cookie_name, &jwtCookieVal);
114172
if (n == NGX_DECLINED)
115173
{
116174
// if we can't find the first cookie, check the legacy ___location
117-
n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &passportKeyCookieName, &jwtCookieVal);
175+
n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &jwtcf->auth_jwt_cookie_legacy_name, &jwtCookieVal);
118176
if (n == NGX_DECLINED)
119177
{
120178
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to obtain a jwt cookie");
@@ -158,11 +216,78 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
158216
// validate the exp date of the JWT
159217
exp = (time_t)jwt_get_grant_int(jwt, "exp");
160218
now = time(NULL);
161-
if (exp < now)
219+
expseconds_from_now = difftime(exp, now);
220+
if (expseconds_from_now < 0)
162221
{
163222
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the jwt has expired");
164223
goto redirect;
165224
}
225+
226+
if (jwtcf->auth_jwt_cookie_reissue_enabled)
227+
{
228+
// if the token is not in the first minute of use, renew it.
229+
if (expseconds_from_now < ((TOKEN_PERIOD_MINUTES - 1) * 60))
230+
{
231+
struct tm* newexp_tm;
232+
time_t newexp;
233+
char *newJwtCookieValChrPtr;
234+
235+
// create a new JWT to return for the cookies
236+
// you could gamble with time arithmetric, but this is the proper way to do it
237+
newexp_tm = localtime(&now);
238+
newexp_tm->tm_min += TOKEN_PERIOD_MINUTES;
239+
newexp = mktime(newexp_tm);
240+
jwt_add_grant_int(jwt, "exp", newexp);
241+
newJwtCookieValChrPtr = jwt_encode_str(jwt);
242+
243+
u_char * cookie;
244+
u_char * p;
245+
int len;
246+
247+
len = jwtcf->auth_jwt_cookie_name.len + sizeof("=") - 1 + strlen(newJwtCookieValChrPtr);
248+
249+
// currently we are not expiring our cookies
250+
// len += sizeof("; expires=") - 1 + sizeof("Mon, 01 Sep 1970 00:00:00 GMT") - 1;
251+
252+
len += sizeof("; Domain=") - 1;
253+
len += sizeof(jwtcf->auth_jwt_cookie_reissue_domain.len);
254+
len += sizeof("; Path=/"); // TODO: Make this configurable
255+
if (jwtcf->auth_jwt_cookie_reissue_secure)
256+
{
257+
len += sizeof("; Secure") - 1;
258+
}
259+
if (jwtcf->auth_jwt_cookie_reissue_httponly)
260+
{
261+
len += sizeof("; HttpOnly") - 1;
262+
}
263+
264+
cookie = ngx_pnalloc(r->pool, len);
265+
p = ngx_copy(cookie, jwtcf->auth_jwt_cookie_name.data, jwtcf->auth_jwt_cookie_name.len);
266+
*p++ = '=';
267+
p = ngx_copy(p, newJwtCookieValChrPtr, strlen(newJwtCookieValChrPtr));
268+
269+
p = ngx_copy(p, "; Domain=", sizeof("; Domain=") - 1);
270+
p = ngx_copy(p, jwtcf->auth_jwt_cookie_reissue_domain.data, jwtcf->auth_jwt_cookie_reissue_domain.len);
271+
272+
p = ngx_copy(p, "; Path=/", sizeof("; Path=/") - 1);
273+
274+
if (jwtcf->auth_jwt_cookie_reissue_secure)
275+
{
276+
p = ngx_copy(p, "; Secure", sizeof("; Secure") - 1);
277+
}
278+
if (jwtcf->auth_jwt_cookie_reissue_httponly)
279+
{
280+
p = ngx_copy(p, "; HttpOnly", sizeof("; HttpOnly") - 1);
281+
}
282+
283+
// this memory was malloc in call to jwt_encode_str
284+
free(newJwtCookieValChrPtr);
285+
newJwtCookieValChrPtr = NULL;
286+
}
287+
}
288+
289+
jwt_free(jwt);
290+
jwt = NULL;
166291

167292
return NGX_OK;
168293

@@ -201,8 +326,8 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
201326
if(request_uri_var && !request_uri_var->not_found && request_uri_var->valid)
202327
{
203328
// ideally we would like the uri with the querystring parameters
204-
uri.data = ngx_palloc(r->pool, request_uri_var->len);
205-
uri.len = request_uri_var->len;
329+
uri.data = ngx_palloc(r->pool, request_uri_var->len);
330+
uri.len = request_uri_var->len;
206331
ngx_memcpy(uri.data, request_uri_var->data, request_uri_var->len);
207332
}
208333
else
@@ -211,21 +336,20 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
211336
uri = r->uri;
212337
}
213338

214-
r->headers_out.___location->value.len = loginlen + sizeof("?return_url=") - 1 + strlen(scheme) + sizeof("://") - 1 + server.len + uri.len;
215-
return_url = ngx_alloc(r->headers_out.___location->value.len, r->connection->log);
216-
ngx_memcpy(return_url, jwtcf->auth_jwt_loginurl.data, jwtcf->auth_jwt_loginurl.len);
217-
int return_url_idx = jwtcf->auth_jwt_loginurl.len;
218-
ngx_memcpy(return_url+return_url_idx, "?return_url=", sizeof("?return_url=") - 1);
219-
return_url_idx += sizeof("?return_url=") - 1;
220-
ngx_memcpy(return_url+return_url_idx, scheme, strlen(scheme));
221-
return_url_idx += strlen(scheme);
222-
ngx_memcpy(return_url+return_url_idx, "://", sizeof("://") - 1);
223-
return_url_idx += sizeof("://") - 1;
224-
ngx_memcpy(return_url+return_url_idx, server.data, server.len);
225-
return_url_idx += server.len;
226-
ngx_memcpy(return_url+return_url_idx, uri.data, uri.len);
227-
return_url_idx += uri.len;
228-
r->headers_out.___location->value.data = (u_char *)return_url;
339+
{
340+
void * return_url_p;
341+
342+
r->headers_out.___location->value.len = loginlen + sizeof("?return_url=") - 1 + strlen(scheme) + sizeof("://") - 1 + server.len + uri.len;
343+
return_url = ngx_alloc(r->headers_out.___location->value.len, r->connection->log);
344+
return_url_p = return_url;
345+
return_url_p = ngx_copy(return_url_p, jwtcf->auth_jwt_loginurl.data, jwtcf->auth_jwt_loginurl.len);
346+
return_url_p = ngx_copy(return_url_p, "?return_url=", sizeof("?return_url=") - 1);
347+
return_url_p = ngx_copy(return_url_p, scheme, strlen(scheme));
348+
return_url_p = ngx_copy(return_url_p, "://", sizeof("://") - 1);
349+
return_url_p = ngx_copy(return_url_p, server.data, server.len);
350+
return_url_p = ngx_copy(return_url_p, uri.data, uri.len);
351+
r->headers_out.___location->value.data = (u_char *)return_url;
352+
}
229353

230354
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "redirect for get request");
231355
}
@@ -236,6 +360,12 @@ static ngx_int_t ngx_http_auth_jwt_handler(ngx_http_request_t *r)
236360
r->headers_out.___location->value.data = jwtcf->auth_jwt_loginurl.data;
237361
}
238362

363+
if (jwt != NULL)
364+
{
365+
jwt_free(jwt);
366+
jwt = NULL;
367+
}
368+
239369
return NGX_HTTP_MOVED_TEMPORARILY;
240370
}
241371

@@ -269,12 +399,15 @@ ngx_http_auth_jwt_create_loc_conf(ngx_conf_t *cf)
269399
{
270400
return NULL;
271401
}
272-
402+
273403
// set the flag to unset
274404
conf->auth_jwt_enabled = (ngx_flag_t) -1;
405+
conf->auth_jwt_cookie_reissue_enabled = (ngx_flag_t) -1;
406+
conf->auth_jwt_cookie_reissue_secure = (ngx_flag_t) -1;
407+
conf->auth_jwt_cookie_reissue_httponly = (ngx_flag_t) -1;
275408

276409
ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Created Location Configuration");
277-
410+
278411
return conf;
279412
}
280413

@@ -285,21 +418,38 @@ ngx_http_auth_jwt_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
285418
ngx_http_auth_jwt_loc_conf_t *prev = parent;
286419
ngx_http_auth_jwt_loc_conf_t *conf = child;
287420

421+
ngx_conf_merge_str_value(conf->auth_jwt_cookie_name, prev->auth_jwt_cookie_name, "");
422+
ngx_conf_merge_str_value(conf->auth_jwt_cookie_legacy_name, prev->auth_jwt_cookie_legacy_name, "");
288423
ngx_conf_merge_str_value(conf->auth_jwt_loginurl, prev->auth_jwt_loginurl, "");
289424
ngx_conf_merge_str_value(conf->auth_jwt_key, prev->auth_jwt_key, "");
290-
291-
292-
if (conf->auth_jwt_enabled == ((ngx_flag_t) -1))
425+
ngx_conf_merge_str_value(conf->auth_jwt_cookie_reissue_domain, prev->auth_jwt_cookie_reissue_domain, "");
426+
427+
if (conf->auth_jwt_enabled == ((ngx_flag_t) -1))
293428
{
294-
conf->auth_jwt_enabled = (prev->auth_jwt_enabled == ((ngx_flag_t) -1)) ? 0 : prev->auth_jwt_enabled;
429+
conf->auth_jwt_enabled = (prev->auth_jwt_enabled == ((ngx_flag_t) -1)) ? 0 : prev->auth_jwt_enabled;
295430
}
296-
431+
432+
if (conf->auth_jwt_cookie_reissue_enabled == ((ngx_flag_t) -1))
433+
{
434+
conf->auth_jwt_cookie_reissue_enabled = (prev->auth_jwt_cookie_reissue_enabled == ((ngx_flag_t) -1)) ? 0 : prev->auth_jwt_cookie_reissue_enabled;
435+
}
436+
437+
if (conf->auth_jwt_cookie_reissue_secure == ((ngx_flag_t) -1))
438+
{
439+
conf->auth_jwt_cookie_reissue_secure = (prev->auth_jwt_cookie_reissue_secure == ((ngx_flag_t) -1)) ? 0 : prev->auth_jwt_cookie_reissue_secure;
440+
}
441+
442+
if (conf->auth_jwt_cookie_reissue_httponly == ((ngx_flag_t) -1))
443+
{
444+
conf->auth_jwt_cookie_reissue_httponly = (prev->auth_jwt_cookie_reissue_httponly == ((ngx_flag_t) -1)) ? 0 : prev->auth_jwt_cookie_reissue_httponly;
445+
}
446+
297447
ngx_conf_log_error(NGX_LOG_DEBUG, cf, 0, "Merged Location Configuration");
298448

299449
// ngx_conf_log_error(NGX_LOG_ERR, cf, 0, "Key: %s, Enabled: %d",
300450
// conf->auth_jwt_key.data,
301451
// conf->auth_jwt_enabled);
302-
return NGX_CONF_OK;
452+
return NGX_CONF_OK;
303453
}
304454

305455
static int
@@ -314,13 +464,13 @@ hex_char_to_binary( char ch, char* ret )
314464
*ret = ( ch - 'A' ) + 10;
315465
else
316466
return *ret = 0;
317-
return 1;
467+
return 1;
318468
}
319469

320470
static int
321471
hex_to_binary( const char* str, u_char* buf, int len ) {
322-
u_char
323-
*cpy = buf;
472+
u_char
473+
*cpy = buf;
324474
char
325475
low,
326476
high;
@@ -337,6 +487,6 @@ hex_to_binary( const char* str, u_char* buf, int len ) {
337487

338488
*cpy++ = low | (high << 4);
339489
}
340-
return 0;
490+
return 0;
341491
}
342492

0 commit comments

Comments
 (0)