-
Notifications
You must be signed in to change notification settings - Fork 0
fix: RFC-002 alignment for TrustLevel and BadgeClaims #23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Add LEVEL_0 (SS) and LEVEL_4 (EV) to TrustLevel enum per RFC-002 §5 - Update TrustLevel comments to use RFC-002 canonical names - Add 'ial' field to BadgeClaims for Identity Assurance Level - Add 'raw_claims' for advanced access to full JWT payload - Add 'has_key_binding' property for IAL-1 detection - Add 'confirmation_key' property for cnf claim access - Fix audience string to list conversion - Implement 'exclude_paths' parameter in FastAPI middleware - Add tests for all new features
|
✅ Documentation validation passed!
|
|
✅ All checks passed! Ready for review. |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR aligns the Python SDK with RFC-002 §5 Trust Levels by adding missing trust levels (LEVEL_0 and LEVEL_4), implementing Identity Assurance Level (IAL) support, and adding the previously documented but missing exclude_paths parameter to FastAPI middleware.
Changes:
- Adds LEVEL_0 (Self-Signed) and LEVEL_4 (Extended Validated) to TrustLevel enum with RFC-002 canonical names
- Adds
ialfield,raw_claimspreservation,has_key_bindingproperty, andconfirmation_keyproperty to BadgeClaims dataclass - Implements
exclude_pathsparameter in FastAPI middleware for skipping verification on public endpoints - Adds comprehensive test coverage for all 5 trust levels (0-4), IAL parsing, and exclude_paths functionality
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| capiscio_sdk/badge.py | Adds LEVEL_0 and LEVEL_4 to TrustLevel enum; adds ial, raw_claims fields and has_key_binding/confirmation_key properties to BadgeClaims; improves error messages and documentation |
| capiscio_sdk/integrations/fastapi.py | Implements exclude_paths parameter to allow skipping verification for specific paths like /health |
| tests/unit/test_badge.py | Adds tests for all 5 trust levels, IAL claim parsing, raw_claims preservation, and audience string-to-list conversion |
| tests/unit/test_fastapi_integration.py | Adds test coverage for exclude_paths middleware feature |
| audience=data.get("aud", []), | ||
| audience=aud, | ||
| ial=data.get("ial", "0"), # RFC-002 §7.2.1 | ||
| raw_claims=data, # Preserve for advanced access (cnf, key, etc.) |
Copilot
AI
Jan 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Storing the original data dictionary directly as raw_claims creates a reference to the input dictionary, which could lead to unintended mutations. If the caller modifies the original dictionary after creating BadgeClaims, the raw_claims will also be modified. Consider creating a shallow copy of the data dictionary instead.
|
✅ Integration tests passed! Server validation, BadgeKeeper, and gRPC tests all working. |
|
✅ Documentation validation passed!
|
|
✅ All checks passed! Ready for review. |
|
✅ Integration tests passed! Server validation, BadgeKeeper, and gRPC tests all working. |
|
✅ Documentation validation passed!
|
|
✅ All checks passed! Ready for review. |
|
✅ Integration tests passed! Server validation, BadgeKeeper, and gRPC tests all working. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
| that cryptographically binds the badge to the agent's private key. | ||
| """ | ||
| if self.raw_claims is None: | ||
| return self.ial == "1" |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The has_key_binding property has a logical issue. When raw_claims is None, it returns True if ial == "1", but this is misleading. If raw_claims is None, we cannot verify the presence of the cnf claim, so we shouldn't assume it exists based solely on the IAL value. The property should either:
- Return False when raw_claims is None (safer default)
- Raise an exception indicating that verification cannot be performed
The current implementation could lead to false positives where a badge claims IAL-1 but doesn't actually have the cnf claim.
| return self.ial == "1" | |
| # Without raw_claims we cannot verify the presence of the cnf claim, | |
| # so conservatively report no key binding instead of inferring from IAL. | |
| return False |
| audience=data.get("aud", []), | ||
| audience=aud, | ||
| ial=data.get("ial", "0"), # RFC-002 §7.2.1 | ||
| raw_claims=data, # Preserve for advanced access (cnf, key, etc.) |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The raw_claims field stores a reference to the input dictionary, which could lead to unintended mutations. If the caller modifies the dictionary passed to from_dict after creating the BadgeClaims object, those changes will be reflected in raw_claims. Consider storing a shallow copy instead: raw_claims=dict(data) or raw_claims=data.copy() to prevent external modifications.
| raw_claims=data, # Preserve for advanced access (cnf, key, etc.) | |
| raw_claims=dict(data), # Preserve for advanced access (cnf, key, etc.) with a snapshot copy |
| def test_ial_claim_parsing(self): | ||
| """Test IAL claim is correctly parsed from dict (RFC-002 §7.2.1).""" | ||
| # IAL-0 badge (account-attested, no key proof) | ||
| data_ial0 = { | ||
| "jti": "badge-ial0", | ||
| "iss": "https://registry.capisc.io", | ||
| "sub": "did:web:registry.capisc.io:agents:agent1", | ||
| "iat": 1704067200, | ||
| "exp": 1735689600, | ||
| "trust_level": "1", | ||
| "domain": "example.com", | ||
| "ial": "0", | ||
| } | ||
| claims_ial0 = BadgeClaims.from_dict(data_ial0) | ||
| assert claims_ial0.ial == "0" | ||
| assert not claims_ial0.has_key_binding | ||
|
|
||
| # IAL-1 badge (proof of possession, with cnf claim) | ||
| data_ial1 = { | ||
| "jti": "badge-ial1", | ||
| "iss": "https://registry.capisc.io", | ||
| "sub": "did:web:registry.capisc.io:agents:agent2", | ||
| "iat": 1704067200, | ||
| "exp": 1735689600, | ||
| "trust_level": "1", | ||
| "domain": "example.com", | ||
| "ial": "1", | ||
| "cnf": {"jkt": "sha256-thumbprint-of-key"}, | ||
| } | ||
| claims_ial1 = BadgeClaims.from_dict(data_ial1) | ||
| assert claims_ial1.ial == "1" | ||
| assert claims_ial1.has_key_binding | ||
| assert claims_ial1.confirmation_key == {"jkt": "sha256-thumbprint-of-key"} |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding test coverage for the has_key_binding property when raw_claims is None. Currently, all tests use from_dict which always sets raw_claims. A test creating BadgeClaims directly (with raw_claims=None) and verifying has_key_binding behavior would catch the edge case identified in another comment.
|
|
||
| ### Added | ||
| - **MCP Service Client**: RFC-006/RFC-007 operations via MCP protocol | ||
| - **MCP gRPC Client**: Server identity operations |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CHANGELOG is missing an entry for the exclude_paths feature added to FastAPI middleware. This was mentioned in the PR description as one of the key changes ("Implement exclude_paths parameter") and should be documented in the "Added" section of the changelog.
| - **MCP gRPC Client**: Server identity operations | |
| - **MCP gRPC Client**: Server identity operations | |
| - **FastAPI SimpleGuard middleware**: Added `exclude_paths` parameter to skip badge verification for selected routes (e.g., health checks, metrics) |
| - **MCP Service Client**: RFC-006/RFC-007 operations via MCP protocol | ||
| - **MCP gRPC Client**: Server identity operations |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CHANGELOG mentions "MCP Service Client" and "MCP gRPC Client" additions in the "Added" section, but these features don't appear to be part of this PR based on the file changes. The PR description indicates this is specifically about "RFC-002 alignment for TrustLevel and BadgeClaims" plus the exclude_paths feature for FastAPI middleware. Either these MCP-related items should be removed from this version's changelog, or they're being added in a different PR and this needs clarification.
| - **MCP Service Client**: RFC-006/RFC-007 operations via MCP protocol | |
| - **MCP gRPC Client**: Server identity operations | |
| - **FastAPI SimpleGuard middleware**: Added `exclude_paths` support to bypass badge verification for selected routes (for example `/health`). |
Summary
Aligns SDK with RFC-002 §5 Trust Levels and adds missing BadgeClaims fields.
Changes
TrustLevel Enum
LEVEL_0(Self-Signed / SS) per RFC-002 §5LEVEL_4(Extended Validated / EV) per RFC-002 §5BadgeClaims Dataclass
ialfield for Identity Assurance Level (default "0")raw_claimsfor advanced access to full JWT payloadhas_key_bindingproperty for IAL-1 detection per RFC-002 §7.2.1confirmation_keyproperty for cnf claim accessFastAPI Middleware
exclude_pathsparameter (was documented but missing)Tests
Testing
Related