Joel Hager
Joel Hager

Reputation: 3440

How to set credentials in AWS SDK v3 JavaScript?

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
})

Upvotes: 34

Views: 67572

Answers (4)

Augusto Moser
Augusto Moser

Reputation: 29

Although the original question wasn't related directly to the use of credentials with authenticated Cognito user pool, I think it will be the case for others, like me.

I was looking for an answer to use the credentials in the Java AWS SDK v3 and set in the web browser. I found a VERY EASY solution in the AWS SDK for JavaScript v3 Guide.

This solution applies to modular app and you need to install two packages: the @aws-sdk/credential-providers and the @aws-sdk/client-cognito-identity. It is straightforward and you don't even need to worry about storing the credentials.

import { CognitoIdentityProviderClient } from "@aws-sdk/client-cognito-identity-provider";
import { fromCognitoIdentityPool } from "@aws-sdk/credential-providers";

// Get the Amazon Cognito ID token for the user. 'getToken()' below.
let idToken = getToken();
let COGNITO_ID = "COGNITO_ID"; // 'COGNITO_ID' has the format 'cognito-idp.REGION.amazonaws.com/COGNITO_USER_POOL_ID'
let loginData = {
  [COGNITO_ID]: idToken,
};
const s3Client = new S3Client({
    region: REGION,
    credentials: fromCognitoIdentityPool({
    clientConfig: { region: REGION }, // Configure the underlying CognitoIdentityClient.
    identityPoolId: 'IDENTITY_POOL_ID',
    logins: loginData
  })
});

// Strips the token ID from the URL after authentication.
window.getToken = function () {
  var idtoken = window.location.href;
  var idtoken1 = idtoken.split("=")[1];
  var idtoken2 = idtoken1.split("&")[0];
  var idtoken3 = idtoken2.split("&")[0];
  return idtoken3;
};

Likewise, if you would want to generate the credentials from a JWT from another provider, you can use the credentials-providers package with fromWebToken().

Upvotes: 0

qkhanhpro
qkhanhpro

Reputation: 5240

There is a credential chain that provides credentials to your API calls from the SDK https://docs.aws.amazon.com/sdk-for-javascript/v3/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
  },

Upvotes: 62

Farski
Farski

Reputation: 1764

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,
    },
  });

Upvotes: 4

Billy Clark
Billy Clark

Reputation: 301

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)
}

Upvotes: 12

Related Questions