Custom credential chain functionality allows building flexible credential provider sequences that try multiple credential sources in order, with automatic expiration and refresh capabilities.
Creates a custom credential provider chain from multiple credential providers.
/**
* Creates a credential chain from multiple credential providers that are tried in sequence
* @param credentialProviders - One or more credential providers to chain together
* @returns Chainable credential provider with expiration control
*/
function createCredentialChain(
...credentialProviders: RuntimeConfigAwsCredentialIdentityProvider[]
): RuntimeConfigAwsCredentialIdentityProvider & CustomCredentialChainOptions;
interface CustomCredentialChainOptions {
/** Sets credential expiration time to force refresh after specified milliseconds */
expireAfter(milliseconds: number): AwsCredentialIdentityProvider & CustomCredentialChainOptions;
}Usage Examples:
import { S3Client } from "@aws-sdk/client-s3";
import {
createCredentialChain,
fromEnv,
fromIni,
fromInstanceMetadata
} from "@aws-sdk/credential-providers";
// Basic credential chain
const client = new S3Client({
region: "us-east-1",
credentials: createCredentialChain(
fromEnv(),
fromIni(),
fromInstanceMetadata()
)
});
// Chain with custom expiration
const expiringClient = new S3Client({
region: "us-east-1",
credentials: createCredentialChain(
fromEnv(),
fromIni({ profile: "default" })
).expireAfter(15 * 60 * 1000) // 15 minutes
});
// Complex chain with multiple profiles and sources
const complexClient = new S3Client({
region: "us-east-1",
credentials: createCredentialChain(
fromEnv(),
fromIni({ profile: "development" }),
fromIni({ profile: "default" }),
fromInstanceMetadata()
)
});Credential chains follow this execution pattern:
import { createCredentialChain, fromEnv, fromIni } from "@aws-sdk/credential-providers";
const chainWithLogging = createCredentialChain(
fromEnv({ logger: console }), // Try environment variables first
fromIni({
profile: "backup-profile",
logger: console
}) // Fallback to INI file
);
// This will:
// 1. Try environment variables
// 2. If that fails, try the backup-profile from INI
// 3. If both fail, throw the last errorMix existing providers with custom credential functions:
import { createCredentialChain, fromEnv, fromIni } from "@aws-sdk/credential-providers";
// Custom credential function
async function fromCustomSource(): Promise<AwsCredentialIdentity> {
// Fetch credentials from your custom source
// e.g., corporate credential management system, vault, etc.
const response = await fetch("https://internal-auth.company.com/credentials");
const data = await response.json();
return {
accessKeyId: data.accessKey,
secretAccessKey: data.secretKey,
sessionToken: data.sessionToken,
expiration: new Date(data.expiresAt)
};
}
// Create chain with custom provider
const client = new S3Client({
region: "us-east-1",
credentials: createCredentialChain(
fromEnv(),
fromCustomSource,
fromIni()
)
});Set automatic credential expiration to force periodic refresh:
import { createCredentialChain, fromEnv, fromIni } from "@aws-sdk/credential-providers";
// Credentials expire after 30 minutes
const thirtyMinuteCredentials = createCredentialChain(
fromEnv(),
fromIni()
).expireAfter(30 * 60 * 1000);
// Minimum expiration is 5 minutes
try {
const tooShort = createCredentialChain(fromEnv()).expireAfter(60 * 1000); // 1 minute
} catch (error) {
console.error(error.message);
// "@aws-sdk/credential-providers - createCredentialChain(...).expireAfter(ms) may not be called with a duration lower than five minutes."
}Apply shared initialization properties to all providers in the chain:
import { createCredentialChain, fromEnv, fromIni } from "@aws-sdk/credential-providers";
// Shared configuration
const sharedInit = {
logger: console,
clientConfig: {
maxAttempts: 3,
requestTimeout: 10000
}
};
const client = new S3Client({
region: "us-east-1",
credentials: createCredentialChain(
fromEnv(sharedInit),
fromIni({
...sharedInit,
profile: "development"
})
)
});Create different chains for different environments:
import {
createCredentialChain,
fromEnv,
fromIni,
fromInstanceMetadata,
fromContainerMetadata
} from "@aws-sdk/credential-providers";
function createCredentialsForEnvironment(environment: string) {
switch (environment) {
case "development":
return createCredentialChain(
fromEnv(),
fromIni({ profile: "dev" })
);
case "testing":
return createCredentialChain(
fromEnv(),
fromIni({ profile: "test" })
);
case "production":
return createCredentialChain(
fromInstanceMetadata(), // EC2 instances
fromContainerMetadata(), // ECS containers
fromEnv() // Fallback to environment
);
default:
return createCredentialChain(
fromEnv(),
fromIni()
);
}
}
const client = new S3Client({
region: "us-east-1",
credentials: createCredentialsForEnvironment(process.env.NODE_ENV || "development")
});Handle chain failures and debug credential resolution:
import { createCredentialChain, fromEnv, fromIni } from "@aws-sdk/credential-providers";
const debugChain = createCredentialChain(
fromEnv({ logger: console }),
fromIni({
profile: "nonexistent",
logger: console
})
);
try {
const credentials = await debugChain();
console.log("Credentials resolved successfully");
} catch (error) {
console.error("All credential providers in chain failed:");
console.error("Final error:", error.message);
// Check specific error types
if (error.name === "ProviderError") {
console.error("No providers in chain succeeded");
}
}import { createCredentialChain, fromEnv, fromIni, fromInstanceMetadata } from "@aws-sdk/credential-providers";
function createAdaptiveChain() {
const providers = [fromEnv()];
// Add profile-based provider if profile is available
if (process.env.AWS_PROFILE) {
providers.push(fromIni({ profile: process.env.AWS_PROFILE }));
}
// Add metadata provider if running on AWS
if (process.env.AWS_EXECUTION_ENV || process.env.AWS_LAMBDA_FUNCTION_NAME) {
providers.push(fromInstanceMetadata());
}
return createCredentialChain(...providers);
}import { createCredentialChain, fromEnv, fromIni } from "@aws-sdk/credential-providers";
function withRetry<T extends (...args: any[]) => any>(fn: T, maxRetries = 3): T {
return (async (...args: any[]) => {
let lastError;
for (let i = 0; i <= maxRetries; i++) {
try {
return await fn(...args);
} catch (error) {
lastError = error;
if (i < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
}
throw lastError;
}) as T;
}
const resilientChain = createCredentialChain(
withRetry(fromEnv()),
withRetry(fromIni())
);expireAfter() judiciously to balance security and performance