What is the ExpiredToken error with S3 signed URLs and how to avoid it

S3 signed URLs allow implementing file downloads in a serverless environment. In a nutshell, it works by:
- the user wants to download a file
- the backend signs a URL
- the user then uses the URL to download the file directly from S3
A common misconception is that the URL is a simple link to a file. But it's a bit more complicated than that.
A signed URL is "X is doing Y", where:
- X is an identity, usually a Lambda execution role, but can be an IAM user or other roles
- Y is an action, in this case "get this object from this S3 bucket"
The signature has an expiration time and it gives a "Signature expired" error when used after that.
https://terraform-....s3.eu-central-1.amazonaws.com/file1?
x-id=GetObject&
X-Amz-Date=20230808T074548Z&
X-Amz-Expires=900&
...
But the identity itself can also expire. IAM roles give temporary credentials that can only be used up until they expire.
{
"Credentials": {
"SecretAccessKey": "...",
"SessionToken": "...",
"Expiration": "2016-03-15T00:05:07Z",
"AccessKeyId": "..."
}
}
So, what happens when the signature is still valid but the credentials that signed it are expired? That's the ExpiredToken
error.
How to avoid it?
Make sure that when you sign a URL then the signature expires before the credentials. In the common case of a Lambda signer that means using 15 minutes or less for the expiration.