aws-sdk-js-v3 icon indicating copy to clipboard operation
aws-sdk-js-v3 copied to clipboard

@aws-sdk/credential-provider-ini - Enable uncached request for credentials

Open mdpratt opened this issue 2 years ago • 4 comments

Is your feature request related to a problem? Please describe.

As a user, I want want to automatically refresh credentials in my service if it detects a change in credentials. Invoking fromIni() will always return the same results, even if the ~/.aws/credentials file has changed between invocations.

Describe the solution you'd like

I want to be able to retrieve current credentials, regardless of how many times I've invoked fromIni(). This could be an extra { ignoreCache: true } property that I can provide.

Describe alternatives you've considered

I've tried using @aws-sdk/shared-ini-file-loader directly, and deleting the require / import but that apparently won't clear the const fileStatusHash: { [key: string]: FileStatus } = {}; object.

// Example, assume wrapped in a function
delete require.cache[require.resolve('@aws-sdk/shared-ini-file-loader')]
let loader = require('@aws-sdk/shared-ini-file-loader');
loader.loadSharedConfigFiles().then((p: any) => console.log(p.credentialsFile['default']))

mdpratt avatar Mar 04 '22 05:03 mdpratt

After thinking some more, since the fileStatusHash just uses the filename as the key, I can get around this problem by creating and then deleting temporary symlinks. Full example below, when run, will watch the ~/.aws/credentials file and then console.log the default profile. This does run the risk of an eventual memory leak, but it's okay for my shorter lived process.

import chokidar from 'chokidar';
import fs from 'fs';
import { homedir } from 'os';
import { resolve } from 'path';
import { nanoid } from 'nanoid';
import { fromIni } from '@aws-sdk/credential-providers';

const { symlink, unlink } = fs.promises;
const AWS_CREDENTIALS = resolve(`${homedir}/.aws/credentials`);

const getCredentials = () => {
  const filepath = `./${nanoid(10)}`
  symlink(AWS_CREDENTIALS, filepath, 'file')
    .then(() => fromIni({ filepath })())
    .then(console.log) // Or do whatever
    .then(() => unlink(filepath));
}

chokidar.watch(AWS_CREDENTIALS).on('change', getCredentials)

mdpratt avatar Mar 04 '22 06:03 mdpratt

:+1: I also think I need this!

Westermann avatar Apr 11 '22 21:04 Westermann

I am also using @aws-sdk/shared-ini-file-loader and this kind of property will be definitely helpful in my case when my creds from credentials file are added/modified using another application from backend and my frontend is receiving only cached credentials.

jaypan13 avatar May 21 '22 07:05 jaypan13

We are running into this issue here at Postman, forcing us to use the old v2 SDK.

JoshuaWise avatar Jun 28 '22 11:06 JoshuaWise

@ajredniwja Is there anything that needs to be done so that this PR can be merged?

Aeolun avatar Dec 09 '22 01:12 Aeolun

I am facing the same problem.

If that helps, I found another workaround using v2.

import { SharedIniFileCredentials } from 'aws-sdk';

export const fromIni = (params) => {
  return async () => {
    const credentials = new SharedIniFileCredentials(params);

    return {
      accessKeyId: credentials.accessKeyId,
      secretAccessKey: credentials.secretAccessKey,
      sessionToken: credentials.sessionToken,
    };
  };
};

bboure avatar Dec 27 '22 17:12 bboure

Thanks @bboure I can confirm using SharedIniFileCredentials from sdk v2 works!

stoyan-scava avatar Apr 21 '23 13:04 stoyan-scava

Feature parity with v2 would be nice:

Occasionally credentials can expire in the middle of a long-running application. In this case, the SDK will automatically attempt to refresh the credentials from the storage location if the Credentials class implements the {refresh} method.

https://github.com/aws/aws-sdk-js/blob/5c14d6b1447a44454189bacd1ab5351433746a4e/lib/credentials.js#L16-L19

vecerek avatar Jul 11 '23 10:07 vecerek

@bboure I've tried your workaround and after 12 hours, the credentials expired and I'm getting ExpiredToken: The provided token has expired. errors. Is this expected, or am I supposed to implement something extra to ensure that the tokens get refreshed? I have a process set up that makes sure the credentials file is updated just before the token expires.

vecerek avatar Jul 12 '23 08:07 vecerek

I am running into the same issue on my local development machine where I use oktacli to automatically refresh the credentials file each hour. The current implementation of @aws-sdk/shared-ini-file-loader treats the files as static and there's no way to clear the SDK's internal cache without a full NodeJS restart. It'd be great if there's a way to prevent the file-loader from caching the files, or even better, if it would watch the file and refresh automagically when the contents change.

MartinDevillers avatar Aug 18 '23 18:08 MartinDevillers

@vecerek I added this in the return clause to solve the same problem:

expiration:
        credentials.expireTime ||
        DateTime.now().plus({ minutes: 5, seconds: 30 }).toJSDate(),

explanation: If expiration is not present, clients assume the tokens are always valid and they won't call fromIni again. When expiration is present, fromIni is called again 5 minutes before expiry. In my case, I wanted to force a refresh every 30 seconds, so I set a fake expiry date of 5 min and 30 seconds.

bboure avatar Sep 16 '23 08:09 bboure