@@ -342,3 +342,125 @@ def test_options_requests_with_cors_headers(source_api_server):
342
342
assert (
343
343
response .status_code == 200
344
344
), "OPTIONS request with CORS headers should succeed"
345
+
346
+
347
+ @pytest .mark .parametrize (
348
+ "token_audiences,allowed_audiences,expected_status" ,
349
+ [
350
+ # Single audience scenarios
351
+ (["stac-api" ], "stac-api" , 200 ),
352
+ (["stac-api" ], "different-api" , 401 ),
353
+ (["stac-api" ], "stac-api,other-api" , 200 ),
354
+ # Multiple audiences in token
355
+ (["stac-api" , "other-api" ], "stac-api" , 200 ),
356
+ (["stac-api" , "other-api" ], "other-api" , 200 ),
357
+ (["stac-api" , "other-api" ], "different-api" , 401 ),
358
+ (["stac-api" , "other-api" ], "stac-api, other-api,third-api" , 200 ),
359
+ # No audience in token
360
+ (None , "stac-api" , 401 ),
361
+ ("" , "stac-api" , 401 ),
362
+ # Empty allowed audiences will regect tokens with an `aud` claim
363
+ ("any-api" , "" , 401 ),
364
+ ("any-api" , None , 401 ),
365
+ # Backward compatibility - no audience configured
366
+ (None , None , 200 ),
367
+ ("" , None , 200 ),
368
+ ],
369
+ )
370
+ def test_jwt_audience_validation (
371
+ source_api_server ,
372
+ token_builder ,
373
+ token_audiences ,
374
+ allowed_audiences ,
375
+ expected_status ,
376
+ ):
377
+ """Test JWT audience validation with various configurations."""
378
+ # Build app with audience configuration
379
+ app_factory = AppFactory (
380
+ oidc_discovery_url = "https://example-stac-api.com/.well-known/openid-configuration" ,
381
+ default_public = False ,
382
+ allowed_jwt_audiences = allowed_audiences ,
383
+ )
384
+ test_app = app_factory (upstream_url = source_api_server )
385
+
386
+ # Build token with audience claim
387
+ token_payload = {}
388
+ if token_audiences is not None :
389
+ token_payload ["aud" ] = token_audiences
390
+
391
+ valid_auth_token = token_builder (token_payload )
392
+
393
+ client = TestClient (test_app )
394
+ response = client .get (
395
+ "/collections" ,
396
+ headers = {"Authorization" : f"Bearer { valid_auth_token } " },
397
+ )
398
+ assert response .status_code == expected_status
399
+
400
+
401
+ @pytest .mark .parametrize (
402
+ "aud_value,scope,expected_status,description" ,
403
+ [
404
+ (["stac-api" ], "openid" , 401 , "Valid audience but missing scope" ),
405
+ (["stac-api" ], "collection:create" , 200 , "Valid audience and valid scope" ),
406
+ (["wrong-api" ], "collection:create" , 401 , "Invalid audience but valid scope" ),
407
+ ],
408
+ )
409
+ def test_audience_validation_with_scopes (
410
+ source_api_server , token_builder , aud_value , scope , expected_status , description
411
+ ):
412
+ """Test that audience validation works alongside scope validation."""
413
+ app_factory = AppFactory (
414
+ oidc_discovery_url = "https://example-stac-api.com/.well-known/openid-configuration" ,
415
+ default_public = False ,
416
+ allowed_jwt_audiences = "stac-api" ,
417
+ private_endpoints = {r"^/collections$" : [("POST" , "collection:create" )]},
418
+ )
419
+ test_app = app_factory (upstream_url = source_api_server )
420
+
421
+ client = TestClient (test_app )
422
+
423
+ token = token_builder ({"aud" : aud_value , "scope" : scope })
424
+ response = client .post (
425
+ "/collections" ,
426
+ headers = {"Authorization" : f"Bearer { token } " },
427
+ )
428
+ assert response .status_code == expected_status
429
+
430
+
431
+ @pytest .mark .parametrize (
432
+ "allowed_audiences_config,test_audience,expected_status" ,
433
+ [
434
+ # Comma-separated string
435
+ ("stac-api,other-api" , "stac-api" , 200 ),
436
+ ("stac-api,other-api" , "other-api" , 200 ),
437
+ ("stac-api,other-api" , "unknown-api" , 401 ),
438
+ # Comma-separated with spaces
439
+ ("stac-api, other-api" , "stac-api" , 200 ),
440
+ ("stac-api, other-api" , "other-api" , 200 ),
441
+ ("stac-api, other-api" , "unknown-api" , 401 ),
442
+ ],
443
+ )
444
+ def test_allowed_audiences_configuration_formats (
445
+ source_api_server ,
446
+ token_builder ,
447
+ allowed_audiences_config ,
448
+ test_audience ,
449
+ expected_status ,
450
+ ):
451
+ """Test different configuration formats for ALLOWED_JWT_AUDIENCES."""
452
+ app_factory = AppFactory (
453
+ oidc_discovery_url = "https://example-stac-api.com/.well-known/openid-configuration" ,
454
+ default_public = False ,
455
+ allowed_jwt_audiences = allowed_audiences_config ,
456
+ )
457
+ test_app = app_factory (upstream_url = source_api_server )
458
+
459
+ client = TestClient (test_app )
460
+
461
+ token = token_builder ({"aud" : [test_audience ]})
462
+ response = client .get (
463
+ "/collections" ,
464
+ headers = {"Authorization" : f"Bearer { token } " },
465
+ )
466
+ assert response .status_code == expected_status
0 commit comments