@@ -270,6 +270,166 @@ START_TEST(test_proto_validate_jwt) {
270270}
271271END_TEST
272272
273+ START_TEST (test_proto_nonce_and_jti ) {
274+ request_rec * r = oidc_test_request_get ();
275+ char * nonce = NULL ;
276+ ck_assert_int_eq (oidc_proto_nonce_gen (r , & nonce ), TRUE);
277+ ck_assert_ptr_nonnull (nonce );
278+
279+ char * jti = oidc_proto_jti_gen (r );
280+ ck_assert_ptr_nonnull (jti );
281+ /* jti should be a non-empty string */
282+ ck_assert_int_ne (_oidc_strlen (jti ), 0 );
283+ }
284+ END_TEST
285+
286+ START_TEST (test_proto_supported_flows_and_check ) {
287+ apr_pool_t * pool = oidc_test_pool_get ();
288+ apr_array_header_t * flows = oidc_proto_supported_flows (pool );
289+ ck_assert_ptr_nonnull (flows );
290+ ck_assert_int_eq (flows -> nelts , 6 );
291+
292+ /* known supported flows */
293+ ck_assert_int_eq (oidc_proto_flow_is_supported (pool , "code" ), TRUE);
294+ ck_assert_int_eq (oidc_proto_flow_is_supported (pool , "id_token token" ), TRUE);
295+ ck_assert_int_eq (oidc_proto_flow_is_supported (pool , "unrecognized flow" ), FALSE);
296+ }
297+ END_TEST
298+
299+ START_TEST (test_proto_state_getters_setters_and_string ) {
300+ request_rec * r = oidc_test_request_get ();
301+ oidc_proto_state_t * ps = oidc_proto_state_new ();
302+ ck_assert_ptr_nonnull (ps );
303+
304+ oidc_proto_state_set_issuer (ps , "https://example.org" );
305+ oidc_proto_state_set_nonce (ps , "mynonce" );
306+ oidc_proto_state_set_original_url (ps , "https://example.org/orig" );
307+ oidc_proto_state_set_original_method (ps , "POST" );
308+ oidc_proto_state_set_response_mode (ps , "fragment" );
309+ oidc_proto_state_set_response_type (ps , "id_token token" );
310+ oidc_proto_state_set_state (ps , "12345" );
311+ oidc_proto_state_set_prompt (ps , "none" );
312+ oidc_proto_state_set_pkce_state (ps , "pkce123" );
313+ oidc_proto_state_set_timestamp_now (ps );
314+
315+ ck_assert_str_eq (oidc_proto_state_get_issuer (ps ), "https://example.org" );
316+ ck_assert_str_eq (oidc_proto_state_get_nonce (ps ), "mynonce" );
317+ ck_assert_str_eq (oidc_proto_state_get_original_url (ps ), "https://example.org/orig" );
318+ ck_assert_str_eq (oidc_proto_state_get_original_method (ps ), "POST" );
319+ ck_assert_str_eq (oidc_proto_state_get_response_mode (ps ), "fragment" );
320+ ck_assert_str_eq (oidc_proto_state_get_response_type (ps ), "id_token token" );
321+ ck_assert_str_eq (oidc_proto_state_get_state (ps ), "12345" );
322+ ck_assert_str_eq (oidc_proto_state_get_prompt (ps ), "none" );
323+ ck_assert_str_eq (oidc_proto_state_get_pkce_state (ps ), "pkce123" );
324+ ck_assert (oidc_proto_state_get_timestamp (ps ) > 0 );
325+
326+ char * s = oidc_proto_state_to_string (r , ps );
327+ ck_assert_ptr_nonnull (s );
328+ /* basic sanity: string contains issuer and nonce */
329+ ck_assert_ptr_nonnull (_oidc_strstr (s , "https://example.org" ));
330+ ck_assert_ptr_nonnull (_oidc_strstr (s , "mynonce" ));
331+
332+ oidc_proto_state_destroy (ps );
333+ }
334+ END_TEST
335+
336+ START_TEST (test_proto_state_cookie_roundtrip ) {
337+ request_rec * r = oidc_test_request_get ();
338+ oidc_cfg_t * c = oidc_test_cfg_get ();
339+
340+ oidc_proto_state_t * ps = oidc_proto_state_new ();
341+ oidc_proto_state_set_nonce (ps , "rndnonce" );
342+ oidc_proto_state_set_state (ps , "s1" );
343+ oidc_proto_state_set_issuer (ps , "https://idp.example.com" );
344+ oidc_proto_state_set_timestamp_now (ps );
345+
346+ char * cookie = oidc_proto_state_to_cookie (r , c , ps );
347+ ck_assert_ptr_nonnull (cookie );
348+
349+ oidc_proto_state_t * parsed = oidc_proto_state_from_cookie (r , c , cookie );
350+ ck_assert_ptr_nonnull (parsed );
351+ ck_assert_str_eq (oidc_proto_state_get_nonce (parsed ), "rndnonce" );
352+ ck_assert_str_eq (oidc_proto_state_get_state (parsed ), "s1" );
353+ ck_assert_str_eq (oidc_proto_state_get_issuer (parsed ), "https://idp.example.com" );
354+
355+ oidc_proto_state_destroy (ps );
356+ oidc_proto_state_destroy (parsed );
357+ }
358+ END_TEST
359+
360+ START_TEST (test_proto_pkce_plain_and_s256 ) {
361+ request_rec * r = oidc_test_request_get ();
362+ char * state_plain = NULL ;
363+ char * challenge_plain = NULL ;
364+ char * verifier_plain = NULL ;
365+
366+ /* plain */
367+ ck_assert_int_eq (oidc_pkce_plain .state (r , & state_plain ), TRUE);
368+ ck_assert_ptr_nonnull (state_plain );
369+ ck_assert_int_eq (oidc_pkce_plain .challenge (r , state_plain , & challenge_plain ), TRUE);
370+ ck_assert_ptr_nonnull (challenge_plain );
371+ ck_assert_str_eq (challenge_plain , state_plain );
372+ ck_assert_int_eq (oidc_pkce_plain .verifier (r , state_plain , & verifier_plain ), TRUE);
373+ ck_assert_ptr_nonnull (verifier_plain );
374+ ck_assert_str_eq (verifier_plain , state_plain );
375+
376+ /* s256 */
377+ char * state_s256 = NULL ;
378+ char * challenge_s256 = NULL ;
379+ char * verifier_s256 = NULL ;
380+ ck_assert_int_eq (oidc_pkce_s256 .state (r , & state_s256 ), TRUE);
381+ ck_assert_ptr_nonnull (state_s256 );
382+ ck_assert_int_eq (oidc_pkce_s256 .challenge (r , state_s256 , & challenge_s256 ), TRUE);
383+ ck_assert_ptr_nonnull (challenge_s256 );
384+ ck_assert_int_ne (_oidc_strlen (challenge_s256 ), 0 );
385+ /* s256 challenge should not equal raw state */
386+ ck_assert_int_ne (_oidc_strcmp (challenge_s256 , state_s256 ), 0 );
387+ ck_assert_int_eq (oidc_pkce_s256 .verifier (r , state_s256 , & verifier_s256 ), TRUE);
388+ ck_assert_ptr_nonnull (verifier_s256 );
389+ ck_assert_str_eq (verifier_s256 , state_s256 );
390+ }
391+ END_TEST
392+
393+ START_TEST (test_proto_profile_helpers ) {
394+ apr_pool_t * pool = oidc_test_pool_get ();
395+ oidc_provider_t * provider = oidc_cfg_provider_create (pool );
396+
397+ /* default profile: token_endpoint_auth_aud returns token endpoint */
398+ oidc_cfg_provider_token_endpoint_url_set (pool , provider , "https://idp.example.com/token" );
399+ ck_assert_str_eq (oidc_proto_profile_token_endpoint_auth_aud (provider ), "https://idp.example.com/token" );
400+
401+ /* revocation: when val=="token" should return token endpoint */
402+ oidc_cfg_provider_revocation_endpoint_url_set (pool , provider , "https://idp.example.com/rev" );
403+ ck_assert_str_eq (oidc_proto_profile_revocation_endpoint_auth_aud (provider , "token" ),
404+ "https://idp.example.com/token" );
405+
406+ /* if profile is FAPI20 behavior changes */
407+ /* set profile to FAPI20 */
408+ oidc_cfg_provider_profile_int_set (provider , OIDC_PROFILE_FAPI20 );
409+ /* token endpoint aud should now be issuer */
410+ oidc_cfg_provider_issuer_set (pool , provider , "https://idp.example.com" );
411+ ck_assert_str_eq (oidc_proto_profile_token_endpoint_auth_aud (provider ), "https://idp.example.com" );
412+ /* pkce should be forced to S256 */
413+ ck_assert_ptr_eq (oidc_proto_profile_pkce_get (provider ), & oidc_pkce_s256 );
414+ /* DPoP should be required */
415+ ck_assert_int_eq (oidc_proto_profile_dpop_mode_get (provider ), OIDC_DPOP_MODE_REQUIRED );
416+ /* response require iss should be true */
417+ ck_assert_int_eq (oidc_proto_profile_response_require_iss_get (provider ), 1 );
418+ }
419+ END_TEST
420+
421+ START_TEST (test_proto_return_www_authenticate_header ) {
422+ request_rec * r = oidc_test_request_get ();
423+ /* ensure no auth_name in stub (stub returns NULL) */
424+ int rc = oidc_proto_return_www_authenticate (r , "invalid_token" , "bad token" );
425+ ck_assert_int_eq (rc , HTTP_UNAUTHORIZED );
426+ const char * hdr = apr_table_get (r -> err_headers_out , "WWW-Authenticate" );
427+ ck_assert_ptr_nonnull (hdr );
428+ ck_assert_ptr_nonnull (_oidc_strstr (hdr , "invalid_token" ));
429+ ck_assert_ptr_nonnull (_oidc_strstr (hdr , "bad token" ));
430+ }
431+ END_TEST
432+
273433int main (void ) {
274434 TCase * core = tcase_create ("core" );
275435 tcase_add_checked_fixture (core , oidc_test_setup , oidc_test_teardown );
@@ -280,6 +440,13 @@ int main(void) {
280440 tcase_add_test (core , test_logout_request );
281441 tcase_add_test (core , test_proto_validate_nonce );
282442 tcase_add_test (core , test_proto_validate_jwt );
443+ tcase_add_test (core , test_proto_nonce_and_jti );
444+ tcase_add_test (core , test_proto_supported_flows_and_check );
445+ tcase_add_test (core , test_proto_state_getters_setters_and_string );
446+ tcase_add_test (core , test_proto_state_cookie_roundtrip );
447+ tcase_add_test (core , test_proto_pkce_plain_and_s256 );
448+ tcase_add_test (core , test_proto_profile_helpers );
449+ tcase_add_test (core , test_proto_return_www_authenticate_header );
283450
284451 Suite * s = suite_create ("proto" );
285452 suite_add_tcase (s , core );
0 commit comments