Category: spec-conformance Severity: blocker
Location: src/Internal/Runtime/CredentialLifecycle.php:143-154
Spec: ARCP v1.1 §9.5
What
§9.5: 'expires_at is ISO 8601 with timezone, MUST be UTC (Z suffix), and MUST be in the future at submission time. Past or invalid values are rejected with INVALID_REQUEST.' The implementation accepts any DateTimeImmutable-parseable string, never compares to clock.now().
Evidence
private function expiresAt(mixed $leaseArg): ?\DateTimeImmutable
{
if (!\is_array($leaseArg)) {
return null;
}
$constraints = $leaseArg['lease_constraints'] ?? null;
$expiresAt = $leaseArg['expires_at'] ?? null;
if ($expiresAt === null && \is_array($constraints)) {
$expiresAt = $constraints['expires_at'] ?? null;
}
return \is_string($expiresAt) ? new \DateTimeImmutable($expiresAt) : null;
}
Proposed fix
Validate the raw string ends with Z; parse and compare against runtime->clock->now(); reject past or non-UTC values with the INVALID_REQUEST error code.
Acceptance criteria
Category: spec-conformance Severity: blocker
Location:
src/Internal/Runtime/CredentialLifecycle.php:143-154Spec: ARCP v1.1 §9.5
What
§9.5: 'expires_at is ISO 8601 with timezone, MUST be UTC (Z suffix), and MUST be in the future at submission time. Past or invalid values are rejected with INVALID_REQUEST.' The implementation accepts any DateTimeImmutable-parseable string, never compares to clock.now().
Evidence
Proposed fix
Validate the raw string ends with
Z; parse and compare against runtime->clock->now(); reject past or non-UTC values with the INVALID_REQUEST error code.Acceptance criteria