The Solution to the Issue of Signed URL for an S3 Object Expiring Before the Desired Time
Tags: aws, s3, signedurl, typescript
Published: 2023-10-27 09:29:56

In my current project, my team was instructed to create a signed URL for an s3 object created programmatically so that they were able to download the s3 object without any AWS account.

## Writing The Code

We created a lambda function. This lambda function used typescript and javascript aws sdk v3.

```typescript
import { Context } from "aws-lambda";
import {
  GetObjectCommand,
  PutObjectCommand,
  S3Client,
  S3ClientConfig,
} from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
export const lambdaHandler = async (event: object, context: Context) => {
  const maxExpireTime = 604800; //7 days maximum expire time
  const region = "us-west-2";
  const s3client = new S3Client({
    region,
  } as S3ClientConfig);
  const bucketName = "my-bucket-name";
  const key = "my-key";
  const body = "my-body";

  const putObjectCommand = new PutObjectCommand({
    Bucket: bucketName,
    Key: key,
    Body: body,
  });
  await s3client.send(putObjectCommand);

  const getObjectCommand = new GetObjectCommand({
    Bucket: bucketName,
    Key: key,
  });
  const signedURL = await getSignedUrl(s3client, getObjectCommand, {
    expiresIn: maxExpireTime,
  });
  return {
    statusCode: 200,
    body: {
      signedURL,
    },
  };
};
```

According to [this link](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html), the max expiration time is 604800 seconds which means 7 days.

## The Problem

So far so good. Lambda worked properly and gave us the signed URL but after 12 hours the signed URL for the s3 object expired and threw an error like below.
![S3 signed url expired error](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w7qj808wv96w6i4iq5nk.png)

After investigating, we found out that if the token expires the signed URL expires.

## The Solution

[**if you want a token that expires in 7 days which means more than 12 hours you need to use IAM user credentials with signature version 4.**](https://repost.aws/knowledge-center/presigned-url-s3-bucket-expiration)

We created IAM user access keys using the AWS console and implemented these keys with signature version 4 in our lambda function.

```typescript
import { Context } from "aws-lambda";
import {
  GetObjectCommand,
  PutObjectCommand,
  S3Client,
  S3ClientConfig,
} from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
export const lambdaHandler = async (event: object, context: Context) => {
  const maxExpireTime = 604800; //7 days maximum expire time
  const region = "us-west-2";

  const accessKeyId = "MY_ACCESS_KEY_ID";
  const secretAccessKey = "MY_SECRET_ACCESS_KEY";

  const s3client = new S3Client({
    region,
    credentials: {
      accessKeyId,
      secretAccessKey,
    },
    signatureVersion: "v4",
  } as S3ClientConfig);
  const bucketName = "my-bucket-name";
  const key = "my-key";
  const body = "my-body";

  const putObjectCommand = new PutObjectCommand({
    Bucket: bucketName,
    Key: key,
    Body: body,
  });
  await s3client.send(putObjectCommand);

  const getObjectCommand = new GetObjectCommand({
    Bucket: bucketName,
    Key: key,
  });
  const signedURL = await getSignedUrl(s3client, getObjectCommand, {
    expiresIn: maxExpireTime,
  });
  return {
    statusCode: 200,
    body: {
      signedURL,
    },
  };
};
```

## The Result

After using the IAM user access keys with signature version 4, the generated signed URL didn't expire after 12 hours as expected.

## The Sources

- https://docs.aws.amazon.com/AmazonS3/latest/userguide/ShareObjectPreSignedURL.html
- https://repost.aws/knowledge-center/presigned-url-s3-bucket-expiration
- https://elasticscale.cloud/why-do-your-s3-presigned-urls-expire-after-12-hours-even-if-youve-set-a-longer-time/#:~:text=This%20means%20that%20if%20the,will%20stop%20working%20as%20well

Contact me:

- [Linkedin](https://www.linkedin.com/in/cengiz-berat-din%C3%A7kan-ab4208128/)

- [twitter](https://twitter.com/dinckan_berat)

- [github](https://github.com/pandashavenobugs)