Repository: appwrite/appwrite
File: src/Appwrite/Utopia/Response/Model/ResourceToken.php (PHP)

Code before:
                'type' => self::TYPE_DATETIME,
                'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_RESOURCE_TOKEN_ACCESS / 60 / 60 . ' hours.',
                'default' => '',
                'example' => self::TYPE_DATETIME_EXAMPLE
            ])
        ;
    }

    public function filter(Document $document): Document
    {
        $expire = $document->getAttribute('expire');
        $now = new \DateTime();
        
        // Calculate expiration timestamp for JWT
        $expTimestamp = null;
        if ($expire !== null) {
            $expiryDate = new \DateTime($expire);
            $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();
            }
        }

        // 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);

        return $document;
    }

    /**

Review comment (suggestion):
This seems excessive given we already have `$expire`.

Also, let's get rid of that 1 minute expiration adjustment. I'm pretty sure it was a hack because maxAge had to be greater than 0.

Code after:
                'type' => self::TYPE_DATETIME,
                'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_RESOURCE_TOKEN_ACCESS / 60 / 60 . ' hours.',
                'default' => '',
                'example' => self::TYPE_DATETIME_EXAMPLE
            ])
        ;
    }

    public function filter(Document $document): Document
    {
        $expire = $document->getAttribute('expire');

        // Use a large but reasonable maxAge to avoid auto-exp when we set explicit exp
        $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400 * 365 * 10, 10); // 10 years

        $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 ($expire !== null) {
            $expiryDate = new \DateTime($expire);
            $payload['exp'] = $expiryDate->getTimestamp();
        } else {
            // For infinite expiry, set 'iat' to prevent JWT library from auto-adding 'exp'
            $payload['iat'] = time();
        }

        $secret = $jwt->encode($payload);

        $document->setAttribute('secret', $secret);

        return $document;
    }

    /**
     * Get Name
     *
     * @return string
     */
    public function getName(): string
    {
        return 'ResourceToken';
    }

    /**
     * Get Type
     *