Request a cryptographic challenge for dynamic verification. The verifier sends a random challenge; the agent must sign it with its private key to prove possession.
| 343 | |
| 344 | |
| 345 | class ChallengeRequest(BaseModel): |
| 346 | """ |
| 347 | Request a cryptographic challenge for dynamic verification. |
| 348 | |
| 349 | The verifier sends a random challenge; the agent must sign it with its |
| 350 | private key to prove possession. |
| 351 | """ |
| 352 | agent_id: str = Field(min_length=10, max_length=32) |
| 353 | challenge_id: str = Field( |
| 354 | default_factory=lambda: "chal_" + uuid4().hex[:12], |
| 355 | description="Unique challenge identifier.", |
| 356 | ) |
| 357 | nonce: str = Field( |
| 358 | min_length=16, max_length=64, |
| 359 | description="Random nonce generated by the verifier.", |
| 360 | ) |
| 361 | issued_by: str = Field( |
| 362 | min_length=1, max_length=64, |
| 363 | description="Identifier of the requesting system.", |
| 364 | ) |
| 365 | created_at: datetime = Field(default_factory=_now_utc) |
| 366 | expires_at: datetime = Field( |
| 367 | description="UTC expiry timestamp for this challenge.", |
| 368 | ) |
| 369 | |
| 370 | @field_validator("nonce") |
| 371 | @classmethod |
| 372 | def _validate_nonce(cls, v: str) -> str: |
| 373 | if not _NONCE_RE.match(v): |
| 374 | raise ValueError( |
| 375 | "nonce must be 16-64 chars of Base64url-safe characters." |
| 376 | ) |
| 377 | return v |
| 378 | |
| 379 | @field_validator("expires_at") |
| 380 | @classmethod |
| 381 | def _validate_expiry(cls, v: datetime) -> datetime: |
| 382 | if v <= _now_utc(): |
| 383 | raise ValueError("expires_at must be in the future.") |
| 384 | return v |
| 385 | |
| 386 | |
| 387 | class ChallengeResponse(BaseModel): |
no outgoing calls
no test coverage detected