tba-concrete-dpop-confirmation-binding 0.4.0.dev2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tba_concrete_dpop_confirmation_binding/__init__.py +101 -0
- tba_concrete_dpop_confirmation_binding/py.typed +1 -0
- tba_concrete_dpop_confirmation_binding-0.4.0.dev2.dist-info/METADATA +15 -0
- tba_concrete_dpop_confirmation_binding-0.4.0.dev2.dist-info/RECORD +5 -0
- tba_concrete_dpop_confirmation_binding-0.4.0.dev2.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from hmac import compare_digest
|
|
4
|
+
from typing import Any, Mapping
|
|
5
|
+
|
|
6
|
+
from tba_contracts_identity.resource_server import (
|
|
7
|
+
AccessTokenClaims,
|
|
8
|
+
TokenValidationError,
|
|
9
|
+
)
|
|
10
|
+
from tba_bases_proof_of_possession import (
|
|
11
|
+
ConfirmationBindingValidatorBase,
|
|
12
|
+
ProofOfPossessionDomainBase,
|
|
13
|
+
)
|
|
14
|
+
from tba_contracts_security_trust import (
|
|
15
|
+
CapabilityMap,
|
|
16
|
+
DPoPBinding,
|
|
17
|
+
VerificationResult,
|
|
18
|
+
VerifyRequest,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _cnf_from(value: AccessTokenClaims | Mapping[str, Any]) -> Mapping[str, Any]:
|
|
23
|
+
return value.cnf if isinstance(value, AccessTokenClaims) else value
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class DpopBindingValidator(ProofOfPossessionDomainBase):
|
|
27
|
+
"""Compare a DPoP JWK thumbprint with the corresponding cnf member."""
|
|
28
|
+
|
|
29
|
+
confirmation_member = "jkt"
|
|
30
|
+
|
|
31
|
+
def supports(self) -> CapabilityMap:
|
|
32
|
+
return CapabilityMap(
|
|
33
|
+
ops={"verify": ("dpop",)},
|
|
34
|
+
modes=("dpop",),
|
|
35
|
+
features=("cnf-binding", "proof-of-possession"),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def validate_confirmation(
|
|
39
|
+
self, cnf: Mapping[str, Any], binding: DPoPBinding | None
|
|
40
|
+
) -> bool:
|
|
41
|
+
expected = str(cnf.get(self.confirmation_member) or "").strip()
|
|
42
|
+
presented = str(getattr(binding, "jwk_thumbprint", "") or "").strip()
|
|
43
|
+
return (
|
|
44
|
+
binding is not None
|
|
45
|
+
and bool(expected)
|
|
46
|
+
and compare_digest(presented, expected)
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
async def verify(self, request: VerifyRequest) -> VerificationResult:
|
|
50
|
+
binding = request.context.get("binding")
|
|
51
|
+
cnf = request.context.get("cnf", {})
|
|
52
|
+
if not isinstance(cnf, Mapping):
|
|
53
|
+
return VerificationResult(
|
|
54
|
+
valid=False, reason="DPoP cnf context must be a mapping"
|
|
55
|
+
)
|
|
56
|
+
if not isinstance(binding, DPoPBinding):
|
|
57
|
+
return VerificationResult(valid=False, reason="missing DPoP binding")
|
|
58
|
+
valid = self.validate_confirmation(cnf, binding)
|
|
59
|
+
return VerificationResult(
|
|
60
|
+
valid=valid,
|
|
61
|
+
reason=None if valid else "DPoP binding mismatch",
|
|
62
|
+
claims=binding.confirmation_claim,
|
|
63
|
+
meta={"method": "dpop"},
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class DpopCnfBindingValidator(ConfirmationBindingValidatorBase):
|
|
68
|
+
"""Validate DPoP confirmation material on access-token claims."""
|
|
69
|
+
|
|
70
|
+
def __init__(
|
|
71
|
+
self,
|
|
72
|
+
confirmation_member: str = "jkt",
|
|
73
|
+
*,
|
|
74
|
+
provider: DpopBindingValidator | None = None,
|
|
75
|
+
) -> None:
|
|
76
|
+
self.provider = provider or DpopBindingValidator()
|
|
77
|
+
self.provider.confirmation_member = confirmation_member
|
|
78
|
+
|
|
79
|
+
def supports(self) -> CapabilityMap:
|
|
80
|
+
return CapabilityMap(ops={"confirmation-binding": ("dpop",)}, modes=("dpop",))
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def confirmation_member(self) -> str:
|
|
84
|
+
return self.provider.confirmation_member
|
|
85
|
+
|
|
86
|
+
def validate_confirmation(
|
|
87
|
+
self, cnf: Mapping[str, Any], binding: DPoPBinding | None
|
|
88
|
+
) -> bool:
|
|
89
|
+
return self.provider.validate_confirmation(cnf, binding)
|
|
90
|
+
|
|
91
|
+
def validate(
|
|
92
|
+
self,
|
|
93
|
+
claims: AccessTokenClaims | Mapping[str, Any],
|
|
94
|
+
binding: DPoPBinding | None,
|
|
95
|
+
) -> bool:
|
|
96
|
+
if not self.validate_confirmation(_cnf_from(claims), binding):
|
|
97
|
+
raise TokenValidationError("DPoP binding mismatch")
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
__all__ = ["DpopBindingValidator", "DpopCnfBindingValidator"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tba-concrete-dpop-confirmation-binding
|
|
3
|
+
Version: 0.4.0.dev2
|
|
4
|
+
Summary: Deterministic DPoP confirmation binding validation.
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
Requires-Python: >=3.10,<3.15
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: tba-bases-proof-of-possession (==0.4.0.dev2)
|
|
14
|
+
Requires-Dist: tba-contracts-identity (==0.4.0.dev2)
|
|
15
|
+
Requires-Dist: tba-contracts-security-trust (==0.1.0)
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
tba_concrete_dpop_confirmation_binding/__init__.py,sha256=HPAf-UXLiQkKc70X0b7YqaKlutP_K0Fjt3S9JIVcCys,3313
|
|
2
|
+
tba_concrete_dpop_confirmation_binding/py.typed,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
3
|
+
tba_concrete_dpop_confirmation_binding-0.4.0.dev2.dist-info/METADATA,sha256=18SZWUedujcGlHOUOHGVobd3Oj-142vjIqv15x47AsY,679
|
|
4
|
+
tba_concrete_dpop_confirmation_binding-0.4.0.dev2.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
|
|
5
|
+
tba_concrete_dpop_confirmation_binding-0.4.0.dev2.dist-info/RECORD,,
|