|
28 | 28 | parse_client_id, |
29 | 29 | parse_client_secret, |
30 | 30 | parse_resource_metadata_url, |
| 31 | + parse_use_id_token_as_bearer, |
31 | 32 | ) |
32 | 33 |
|
33 | 34 | # --------------------------------------------------------------------------- |
@@ -127,6 +128,7 @@ def authenticate(req: falcon.Request) -> AuthContext: |
127 | 128 | _METADATA_WITH_CLIENT_SECRET = dataclasses.replace( |
128 | 129 | _METADATA, client_id="my-client-id", client_secret="my-client-secret" |
129 | 130 | ) |
| 131 | +_METADATA_WITH_ID_TOKEN = dataclasses.replace(_METADATA, use_id_token_as_bearer=True) |
130 | 132 |
|
131 | 133 |
|
132 | 134 | # --------------------------------------------------------------------------- |
@@ -467,6 +469,81 @@ def test_client_discovery_round_trip_with_client_secret(self) -> None: |
467 | 469 | assert meta is not None |
468 | 470 | assert meta.client_secret == "my-client-secret" |
469 | 471 |
|
| 472 | + def test_use_id_token_as_bearer_in_well_known_json_when_true(self) -> None: |
| 473 | + """use_id_token_as_bearer appears in well-known JSON when True.""" |
| 474 | + server = RpcServer(_EchoService, _EchoImpl()) |
| 475 | + client = make_sync_client(server, signing_key=b"k", oauth_resource_metadata=_METADATA_WITH_ID_TOKEN) |
| 476 | + resp = client.get("/.well-known/oauth-protected-resource") |
| 477 | + body = json.loads(resp.content) |
| 478 | + assert body["use_id_token_as_bearer"] is True |
| 479 | + |
| 480 | + def test_use_id_token_as_bearer_absent_from_well_known_json_when_false(self) -> None: |
| 481 | + """use_id_token_as_bearer absent from well-known JSON when False.""" |
| 482 | + d = _METADATA.to_json_dict() |
| 483 | + assert "use_id_token_as_bearer" not in d |
| 484 | + |
| 485 | + def test_use_id_token_as_bearer_in_www_authenticate(self) -> None: |
| 486 | + """use_id_token_as_bearer appears in WWW-Authenticate header when True.""" |
| 487 | + _priv, pub = _make_rsa_key() |
| 488 | + auth_fn = _make_local_auth(pub) |
| 489 | + server = RpcServer(_EchoService, _EchoImpl()) |
| 490 | + client = make_sync_client( |
| 491 | + server, |
| 492 | + signing_key=b"k", |
| 493 | + authenticate=auth_fn, |
| 494 | + oauth_resource_metadata=_METADATA_WITH_ID_TOKEN, |
| 495 | + ) |
| 496 | + resp = client.post( |
| 497 | + "/vgi/echo", |
| 498 | + content=b"garbage", |
| 499 | + headers={"Content-Type": "application/octet-stream"}, |
| 500 | + ) |
| 501 | + assert resp.status_code == 401 |
| 502 | + www_auth = resp.headers.get("www-authenticate", "") |
| 503 | + assert 'use_id_token_as_bearer="true"' in www_auth |
| 504 | + |
| 505 | + def test_use_id_token_as_bearer_absent_from_www_authenticate(self) -> None: |
| 506 | + """use_id_token_as_bearer absent from WWW-Authenticate when False.""" |
| 507 | + _priv, pub = _make_rsa_key() |
| 508 | + auth_fn = _make_local_auth(pub) |
| 509 | + server = RpcServer(_EchoService, _EchoImpl()) |
| 510 | + client = make_sync_client( |
| 511 | + server, |
| 512 | + signing_key=b"k", |
| 513 | + authenticate=auth_fn, |
| 514 | + oauth_resource_metadata=_METADATA, |
| 515 | + ) |
| 516 | + resp = client.post( |
| 517 | + "/vgi/echo", |
| 518 | + content=b"garbage", |
| 519 | + headers={"Content-Type": "application/octet-stream"}, |
| 520 | + ) |
| 521 | + assert resp.status_code == 401 |
| 522 | + www_auth = resp.headers.get("www-authenticate", "") |
| 523 | + assert "use_id_token_as_bearer" not in www_auth |
| 524 | + |
| 525 | + def test_parse_use_id_token_as_bearer_extracts_value(self) -> None: |
| 526 | + """parse_use_id_token_as_bearer() extracts value from header.""" |
| 527 | + header = ( |
| 528 | + 'Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource/vgi"' |
| 529 | + ', use_id_token_as_bearer="true"' |
| 530 | + ) |
| 531 | + assert parse_use_id_token_as_bearer(header) is True |
| 532 | + |
| 533 | + def test_parse_use_id_token_as_bearer_returns_false_when_absent(self) -> None: |
| 534 | + """parse_use_id_token_as_bearer() returns False when not present.""" |
| 535 | + assert parse_use_id_token_as_bearer("Bearer") is False |
| 536 | + assert parse_use_id_token_as_bearer('Bearer resource_metadata="https://example.com"') is False |
| 537 | + assert parse_use_id_token_as_bearer("") is False |
| 538 | + |
| 539 | + def test_client_discovery_round_trip_with_use_id_token_as_bearer(self) -> None: |
| 540 | + """Client discovers use_id_token_as_bearer set on server.""" |
| 541 | + server = RpcServer(_EchoService, _EchoImpl()) |
| 542 | + client = make_sync_client(server, signing_key=b"k", oauth_resource_metadata=_METADATA_WITH_ID_TOKEN) |
| 543 | + meta = http_oauth_metadata(client=client) |
| 544 | + assert meta is not None |
| 545 | + assert meta.use_id_token_as_bearer is True |
| 546 | + |
470 | 547 | def test_401_discovery_flow(self) -> None: |
471 | 548 | """Full 401-based discovery: get 401, parse header, fetch metadata.""" |
472 | 549 | _priv, pub = _make_rsa_key() |
|
0 commit comments