@@ -53,6 +53,8 @@ static char *merge_location_configuration(ngx_conf_t *main_config, void *parent,
53
53
static ngx_int_t introspection_response_handler (ngx_http_request_t * request , void * data ,
54
54
ngx_int_t introspection_subrequest_status_code );
55
55
56
+ static ngx_int_t write_error_response (ngx_http_request_t * request , ngx_int_t status , phantom_token_configuration_t * module_location_config );
57
+
56
58
/**
57
59
* Adds a WWW-Authenticate header to the given request's output headers that conforms to <a href="https://tools.ietf.org/html/rfc6750">RFC 6750</>.
58
60
*
@@ -75,8 +77,7 @@ static ngx_int_t introspection_response_handler(ngx_http_request_t *request, voi
75
77
*
76
78
* @see <a href="https://tools.ietf.org/html/rfc6750">RFC 6750</a>
77
79
*/
78
- static ngx_int_t set_www_authenticate_header (ngx_http_request_t * request , ngx_str_t realm ,
79
- ngx_str_t space_separated_scopes , char * error_code );
80
+ static ngx_int_t set_www_authenticate_header (ngx_http_request_t * request , phantom_token_configuration_t * module_location_config , char * error_code );
80
81
81
82
/**
82
83
* Sets the base-64-encoded client ID and secret in the module's configuration setting structure.
@@ -287,20 +288,19 @@ static ngx_int_t handler(ngx_http_request_t *request)
287
288
}
288
289
else if (module_context -> status == NGX_HTTP_NO_CONTENT )
289
290
{
290
- return set_www_authenticate_header (request , module_location_config -> realm ,
291
- module_location_config -> space_separated_scopes , NULL );
291
+ return set_www_authenticate_header (request , module_location_config , NULL );
292
292
}
293
293
else if (module_context -> status == NGX_HTTP_SERVICE_UNAVAILABLE )
294
294
{
295
- return NGX_HTTP_SERVICE_UNAVAILABLE ;
295
+ return write_error_response ( request , NGX_HTTP_SERVICE_UNAVAILABLE , module_location_config ) ;
296
296
}
297
297
else if (module_context -> status >= NGX_HTTP_INTERNAL_SERVER_ERROR || module_context -> status == NGX_HTTP_NOT_FOUND
298
298
|| module_context -> status == NGX_HTTP_UNAUTHORIZED || module_context -> status == NGX_HTTP_FORBIDDEN )
299
299
{
300
- return NGX_HTTP_BAD_GATEWAY ;
300
+ return write_error_response ( request , NGX_HTTP_BAD_GATEWAY , module_location_config ) ;
301
301
}
302
302
303
- return NGX_HTTP_INTERNAL_SERVER_ERROR ;
303
+ return write_error_response ( request , NGX_HTTP_INTERNAL_SERVER_ERROR , module_location_config ) ;
304
304
}
305
305
306
306
ngx_log_debug0 (NGX_LOG_DEBUG_HTTP , request -> connection -> log , 0 ,
@@ -314,8 +314,7 @@ static ngx_int_t handler(ngx_http_request_t *request)
314
314
{
315
315
ngx_log_debug0 (NGX_LOG_DEBUG_HTTP , request -> connection -> log , 0 , "Authorization header not present" );
316
316
317
- return set_www_authenticate_header (request , module_location_config -> realm ,
318
- module_location_config -> space_separated_scopes , NULL );
317
+ return set_www_authenticate_header (request , module_location_config , NULL );
319
318
}
320
319
321
320
u_char * bearer_token_pos ;
@@ -328,8 +327,7 @@ static ngx_int_t handler(ngx_http_request_t *request)
328
327
ngx_log_debug0 (NGX_LOG_DEBUG_HTTP , request -> connection -> log , 0 ,
329
328
"Authorization header does not contain a bearer token" );
330
329
331
- return set_www_authenticate_header (request , module_location_config -> realm ,
332
- module_location_config -> space_separated_scopes , NULL );
330
+ return set_www_authenticate_header (request , module_location_config , NULL );
333
331
}
334
332
335
333
bearer_token_pos += BEARER_SIZE ;
@@ -361,7 +359,7 @@ static ngx_int_t handler(ngx_http_request_t *request)
361
359
if (ngx_http_subrequest (request , & module_location_config -> introspection_endpoint , NULL , & introspection_request ,
362
360
introspection_request_callback , NGX_HTTP_SUBREQUEST_WAITED ) != NGX_OK )
363
361
{
364
- return NGX_HTTP_INTERNAL_SERVER_ERROR ;
362
+ write_error_response ( request , NGX_HTTP_INTERNAL_SERVER_ERROR , module_location_config ) ;
365
363
}
366
364
367
365
// extract access token from header
@@ -490,8 +488,7 @@ static ngx_int_t handler(ngx_http_request_t *request)
490
488
return NGX_AGAIN ;
491
489
}
492
490
493
- static ngx_int_t set_www_authenticate_header (ngx_http_request_t * request , ngx_str_t realm ,
494
- ngx_str_t space_separated_scopes , char * error_code )
491
+ static ngx_int_t set_www_authenticate_header (ngx_http_request_t * request , phantom_token_configuration_t * module_location_config , char * error_code )
495
492
{
496
493
request -> headers_out .www_authenticate = ngx_list_push (& request -> headers_out .headers );
497
494
@@ -516,20 +513,20 @@ static ngx_int_t set_www_authenticate_header(ngx_http_request_t *request, ngx_st
516
513
static const size_t ERROR_CODE_PREFIX_SIZE = sizeof (ERROR_CODE_PREFIX ) - 1 ;
517
514
518
515
size_t bearer_data_size = BEARER_SIZE + sizeof ('\0' ); // Add one for the nul byte
519
- bool realm_provided = realm .len > 0 ;
520
- bool scopes_provided = space_separated_scopes .len > 0 ;
516
+ bool realm_provided = module_location_config -> realm .len > 0 ;
517
+ bool scopes_provided = module_location_config -> space_separated_scopes .len > 0 ;
521
518
bool error_code_provided = error_code != NULL ;
522
519
bool append_one_comma = false, append_two_commas = false;
523
520
size_t error_code_len = 0 ;
524
521
525
522
if (realm_provided )
526
523
{
527
- bearer_data_size += REALM_PREFIX_SIZE + realm .len + TOKEN_SUFFIX_SIZE ;
524
+ bearer_data_size += REALM_PREFIX_SIZE + module_location_config -> realm .len + TOKEN_SUFFIX_SIZE ;
528
525
}
529
526
530
527
if (scopes_provided )
531
528
{
532
- bearer_data_size += SCOPE_PREFIX_SIZE + space_separated_scopes .len + TOKEN_SUFFIX_SIZE ;
529
+ bearer_data_size += SCOPE_PREFIX_SIZE + module_location_config -> space_separated_scopes .len + TOKEN_SUFFIX_SIZE ;
533
530
}
534
531
535
532
if (error_code_provided )
@@ -562,7 +559,7 @@ static ngx_int_t set_www_authenticate_header(ngx_http_request_t *request, ngx_st
562
559
if (realm_provided )
563
560
{
564
561
p = ngx_cpymem (p , REALM_PREFIX , REALM_PREFIX_SIZE );
565
- p = ngx_cpymem (p , realm .data , realm .len );
562
+ p = ngx_cpymem (p , module_location_config -> realm .data , module_location_config -> realm .len );
566
563
p = ngx_cpymem (p , TOKEN_SUFFIX , TOKEN_SUFFIX_SIZE );
567
564
568
565
if (append_one_comma )
@@ -574,7 +571,7 @@ static ngx_int_t set_www_authenticate_header(ngx_http_request_t *request, ngx_st
574
571
if (scopes_provided )
575
572
{
576
573
p = ngx_cpymem (p , SCOPE_PREFIX , SCOPE_PREFIX_SIZE );
577
- p = ngx_cpymem (p , space_separated_scopes .data , space_separated_scopes .len );
574
+ p = ngx_cpymem (p , module_location_config -> space_separated_scopes .data , module_location_config -> space_separated_scopes .len );
578
575
p = ngx_cpymem (p , TOKEN_SUFFIX , TOKEN_SUFFIX_SIZE );
579
576
580
577
if (append_one_comma || append_two_commas )
@@ -607,7 +604,7 @@ static ngx_int_t set_www_authenticate_header(ngx_http_request_t *request, ngx_st
607
604
608
605
assert (request -> headers_out .www_authenticate -> value .len <= bearer_data_size );
609
606
610
- return NGX_HTTP_UNAUTHORIZED ;
607
+ return write_error_response ( request , NGX_HTTP_UNAUTHORIZED , module_location_config ) ;
611
608
}
612
609
613
610
static ngx_int_t introspection_response_handler (ngx_http_request_t * request , void * data ,
@@ -811,3 +808,73 @@ static char* set_client_credential_configuration_slot(ngx_conf_t *config_setting
811
808
812
809
return "invalid_client_credential" ;
813
810
}
811
+
812
+ /*
813
+ * Add the error response as a JSON object that is easier to handle than the default HTML response that NGINX returns
814
+ * http://nginx.org/en/docs/dev/development_guide.html#http_response_body
815
+ */
816
+ static ngx_int_t write_error_response (ngx_http_request_t * request , ngx_int_t status , phantom_token_configuration_t * module_location_config )
817
+ {
818
+ ngx_int_t rc ;
819
+ ngx_str_t code ;
820
+ ngx_str_t message ;
821
+ u_char json_error_data [256 ];
822
+ ngx_chain_t output ;
823
+ ngx_buf_t * body = NULL ;
824
+ const char * error_format = NULL ;
825
+ size_t error_len = 0 ;
826
+
827
+ if (request -> method == NGX_HTTP_HEAD )
828
+ {
829
+ return status ;
830
+ }
831
+
832
+ body = ngx_calloc_buf (request -> pool );
833
+ if (body == NULL )
834
+ {
835
+ ngx_log_error (NGX_LOG_WARN , request -> connection -> log , 0 , "Failed to allocate memory for error body" );
836
+ return NGX_HTTP_INTERNAL_SERVER_ERROR ;
837
+ }
838
+ else
839
+ {
840
+ if (status == NGX_HTTP_UNAUTHORIZED )
841
+ {
842
+ ngx_str_set (& code , "unauthorized_request" );
843
+ ngx_str_set (& message , "Access denied due to missing, invalid or expired credentials" );
844
+ }
845
+ else
846
+ {
847
+ ngx_str_set (& code , "server_error" );
848
+ ngx_str_set (& message , "Problem encountered processing the request" );
849
+ }
850
+
851
+ /* The string length calculation replaces the two '%V' markers with their actual values */
852
+ error_format = "{\"code\":\"%V\",\"message\":\"%V\"}" ;
853
+ error_len = ngx_strlen (error_format ) + code .len + message .len - 4 ;
854
+ ngx_snprintf (json_error_data , sizeof (json_error_data ) - 1 , error_format , & code , & message );
855
+ json_error_data [error_len ] = 0 ;
856
+
857
+ request -> headers_out .status = status ;
858
+ request -> headers_out .content_length_n = error_len ;
859
+ ngx_str_set (& request -> headers_out .content_type , "application/json" );
860
+
861
+ rc = ngx_http_send_header (request );
862
+ if (rc == NGX_ERROR || rc > NGX_OK || request -> header_only ) {
863
+ return rc ;
864
+ }
865
+
866
+ body -> pos = json_error_data ;
867
+ body -> last = json_error_data + error_len ;
868
+ body -> memory = 1 ;
869
+ body -> last_buf = 1 ;
870
+ body -> last_in_chain = 1 ;
871
+ output .buf = body ;
872
+ output .next = NULL ;
873
+
874
+ /* Return an error result, which also requires finalize_request to be called, to prevent a 'header already sent' warning in logs
875
+ https://forum.nginx.org/read.php?29,280514,280521#msg-280521 */
876
+ rc = ngx_http_output_filter (request , & output );
877
+ ngx_http_finalize_request (request , rc );
878
+ return NGX_DONE ;
879
+ }
880
+ }
0 commit comments