27

I am scouring the documentation, and it only provides pseudo-code of the credentials for v3 (e.g. const client = new S3Client(clientParams)

How do I initialize an S3Client with the bucket and credentials to perform a getSignedUrl request? Any resources pointing me in the right direction would be most helpful. I've even searched YouTube, SO, etc and I can't find any specific info on v3. Even the documentation and examples doesn't provide the actual code to use credentials. Thanks!

As an aside, do I have to include the fake folder structure in the filename, or can I just use the actual filename? For example: bucket/folder1/folder2/uniqueFilename.zip or uniqueFilename.zip

Here's the code I have so far: (Keep in mind I was returning the wasabiObjKey to ensure I was getting the correct file name. I am. It's the client, GetObjectCommand, and getSignedUrl that I'm having issues with.

exports.getPresignedUrl = functions.https.onCall(async (data, ctx) => {
  const wasabiObjKey = `${data.bucket_prefix ? `${data.bucket_prefix}/` : ''}${data.uid.replace(/-/g, '_').toLowerCase()}${data.variation ? `_${data.variation.replace(/\./g, '').toLowerCase()}` : ''}.zip`
  const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3')
  const s3 = new S3Client({
    bucketEndpoint: functions.config().s3_bucket.name,
    region: functions.config().s3_bucket.region,
    credentials: {
      secretAccessKey: functions.config().s3.secret,
      accessKeyId: functions.config().s3.access_key
    }
  })
  const command = new GetObjectCommand({
    Bucket: functions.config().s3_bucket.name,
    Key: wasabiObjKey,
  })
  const { getSignedUrl } = require("@aws-sdk/s3-request-presigner")
  const url = getSignedUrl(s3, command, { expiresIn: 60 })
  return wasabiObjKey
})

3 Answers 3

53

There is a credential chain that provides credentials to your API calls from the SDK https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/setting-credentials-node.html

Loaded from AWS Identity and Access Management (IAM) roles for Amazon EC2

Loaded from the shared credentials file (~/.aws/credentials)

Loaded from environment variables

Loaded from a JSON file on disk

Other credential-provider classes provided by the JavaScript SDK

You can embed the credential inside your source code but it's not the prefered way

new S3Client(configuration: S3ClientConfig): S3Client

Where S3ClientConfig contain a credentials property

https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/modules/credentials.html

    const { S3Client,GetObjectCommand } = require("@aws-sdk/client-s3");
    
    let client = new S3Client({
        region:'ap-southeast-1',
        credentials:{
            accessKeyId:'',
            secretAccessKey:''
        }
    });
    
    (async () => {
      const response = await client.send(new GetObjectCommand({Bucket:"BucketNameHere",Key:"ObjectNameHere"}));
      console.log(response);
    })();

Sample answer

  '$metadata': {
    httpStatusCode: 200,
    requestId: undefined,
    extendedRequestId: '7kwrFkEp3lEnLU+OtxjrgdmS6gQmvPdbnqqR7I8P/rdFrUPBkdKYPYykWivuHPXCF1IHgjCIbe8=',
    cfId: undefined,
    attempts: 1,
    totalRetryDelay: 0
  },
9
  • I was trying to dig up my code that I have. I'll post it to see where I'm going wrong. I have it in a firebase function (and I'm getting the credentials from the functions.config().fileName env vars.
    – Joel Hager
    Commented Jul 6, 2021 at 3:56
  • 1
    Also, the link to your documentation is v2, not v3.
    – Joel Hager
    Commented Jul 6, 2021 at 3:58
  • The credential chain does not change... Only the SDK syntax change from V2 to V3. That would affect only the last level of credential provider (Direct credential input from source code)
    – qkhanhpro
    Commented Jul 6, 2021 at 3:59
  • Why would they split the documentation like that?
    – Joel Hager
    Commented Jul 6, 2021 at 5:00
  • 1
    I am pretty sure that Bucket is required for GetObjectCommand
    – qkhanhpro
    Commented Jul 6, 2021 at 5:44
10

Here's a simple approach I use (in Deno) for testing (in case you don't want to go the signedUrl approach and just let the SDK do the heavy lifting for you):

import { config as env } from 'https://deno.land/x/dotenv/mod.ts' // https://github.com/pietvanzoen/deno-dotenv
import { S3Client, ListObjectsV2Command } from 'https://cdn.skypack.dev/@aws-sdk/client-s3' // https://github.com/aws/aws-sdk-js-v3

const {AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY} = env()

// https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/modules/credentials.html
const credentials = {
    accessKeyId: AWS_ACCESS_KEY_ID,
    secretAccessKey: AWS_SECRET_ACCESS_KEY,
}

// https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/s3clientconfig.html
const config = {
    region: 'ap-southeast-1',
    credentials,
}

// https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/s3client.html
const client = new S3Client(config)

export async function list() {
    // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/interfaces/listobjectsv2commandinput.html
    const input = {
        Bucket: 'BucketNameHere'
    }

    // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/command.html
    const cmd = new ListObjectsV2Command(input)
    
    // https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-s3/classes/listobjectsv2command.html
    return await client.send(cmd)
}
1
  • This is great. I was able to use this pattern to work with the Bedrock SDK with the aws js-sdk v3. Commented Dec 11, 2023 at 16:13
4

This answer is basically the same as what's been said above, but for anyone who's migrating from v2 to v3 and not moving to the new modular model, you will find that your existing clients don't immediately work, because the expected credentials format is different. If you previously had…

new AWS.CloudWatch({
    apiVersion: '2010-08-01',
    region: event.region,
    credentials: new AWS.Credentials(
        role.Credentials.AccessKeyId,
        role.Credentials.SecretAccessKey,
        role.Credentials.SessionToken,
    )
})

…you'll have to replace new AWS.Credentials with an object:

new CloudWatch({
    apiVersion: '2010-08-01',
    region: event.region,
    credentials: {
        accessKeyId: role.Credentials.AccessKeyId,
        secretAccessKey: role.Credentials.SecretAccessKey,
        sessionToken: role.Credentials.SessionToken,
    },
  });

Not the answer you're looking for? Browse other questions tagged or ask your own question.