fix - incorrect file token expiry

This commit is contained in:
Evan
2025-08-15 16:07:37 -07:00
parent 2ffdfd10d4
commit eec62752ed
2 changed files with 63 additions and 9 deletions
@@ -61,24 +61,40 @@ class ResourceToken extends Model
public function filter(Document $document): Document
{
$maxAge = PHP_INT_MAX;
$expire = $document->getAttribute('expire');
$now = new \DateTime();
// Calculate expiration timestamp for JWT
$expTimestamp = null;
if ($expire !== null) {
$now = new \DateTime();
$expiryDate = new \DateTime($expire);
// set 1 min if expired, we check for expiry later on route hooks for validation!
$maxAge = min(60, $expiryDate->getTimestamp() - $now->getTimestamp());
$secondsUntilExpiry = $expiryDate->getTimestamp() - $now->getTimestamp();
// If token is expired, set expiration to 1 minute from now
// We check for actual expiry later on route hooks for validation
if ($secondsUntilExpiry <= 0) {
$expTimestamp = $now->getTimestamp() + 60;
} else {
$expTimestamp = $expiryDate->getTimestamp();
}
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $maxAge, 10);
$secret = $jwt->encode([
// Use maxAge as fallback, but rely on exp in payload for actual expiration
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', PHP_INT_MAX, 10);
$payload = [
'tokenId' => $document->getId(),
'resourceId' => $document->getAttribute('resourceId'),
'resourceType' => $document->getAttribute('resourceType'),
'resourceInternalId' => $document->getAttribute('resourceInternalId'),
]);
];
// Set explicit expiration in JWT payload if we have an expiry date
if ($expTimestamp !== null) {
$payload['exp'] = $expTimestamp;
}
$secret = $jwt->encode($payload);
$document->setAttribute('secret', $secret);
@@ -107,6 +107,44 @@ class TokensConsoleClientTest extends Scope
return $data;
}
/**
* @depends testCreateToken
*/
public function testExpiredTokenJWT(array $data): array
{
$fileId = $data['fileId'];
$bucketId = $data['bucketId'];
// Create a token with an expiry date in the past (expired)
$pastExpiry = DateTime::addSeconds(new \DateTime(), -3600); // 1 hour ago
$expiredToken = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()), [
'expire' => $pastExpiry,
]);
$this->assertEquals(201, $expiredToken['headers']['status-code']);
$this->assertEquals('files', $expiredToken['body']['resourceType']);
// Verify that the JWT is generated without causing a 500 error
$this->assertNotEmpty($expiredToken['body']['secret']);
// Parse the JWT to verify expiration is set correctly for expired tokens
$jwtParts = explode('.', $expiredToken['body']['secret']);
$this->assertCount(3, $jwtParts, 'JWT should have 3 parts');
$payload = json_decode(base64_decode($jwtParts[1]), true);
$this->assertArrayHasKey('exp', $payload, 'JWT payload should contain exp field');
// For expired tokens, exp should be set to a short time in the future (around 1 minute)
$now = time();
$this->assertGreaterThan($now, $payload['exp'], 'JWT exp should be in the future even for expired tokens');
$this->assertLessThanOrEqual($now + 120, $payload['exp'], 'JWT exp should not be more than 2 minutes in the future for expired tokens');
return $data;
}
/**
* @depends testCreateToken
*/