Custom Credential Providers
Sometimes your credentials live somewhere the SDK does not know about.
Busca en todas las páginas de la documentación
Sometimes your credentials live somewhere the SDK does not know about.
A secrets broker like HashiCorp Vault, a bespoke identity service, or a Secrets Manager entry holding rotated keys - none of these are in the default chain. A custom credential provider teaches the SDK to fetch from that source and, crucially, to refresh before the credentials expire.
Reach for this only when a built-in provider genuinely does not fit. The built-ins are safer, and a hand-rolled provider is code you now own.
Wrap a fetch-from-somewhere function so the SDK calls it for credentials and re-calls it to refresh. boto3 uses
RefreshableCredentials; SDK v3 uses a provider function that returns anexpiration.
# --- Python (boto3) ---
import boto3
from botocore.credentials import RefreshableCredentials
from botocore.session import get_session
def fetch():
# Call Vault/your broker here; return the botocore metadata shape.
creds = load_from_vault() # your function
return {
"access_key": creds["accessKeyId"],
"secret_key": creds["secretAccessKey"],
"token": creds["sessionToken"],
"expiry_time": creds["expiresAt"], # ISO-8601 string
}
refreshable = RefreshableCredentials.create_from_metadata(
metadata=fetch(), refresh_using=fetch, method="custom-vault",
)
botocore_session = get_session()
botocore_session._credentials = refreshable
session = boto3.Session(botocore_session=botocore_session)
s3 = session.client("s3", region_name="us-east-1")// --- TypeScript (AWS SDK v3) ---
import { S3Client } from "@aws-sdk/client-s3";
import type { AwsCredentialIdentityProvider } from "@aws-sdk/types";
// Returning `expiration` lets the SDK re-invoke this to refresh.
const fromVault: AwsCredentialIdentityProvider = async () => {
const creds = await loadFromVault(); // your function
return {
accessKeyId: creds.accessKeyId,
secretAccessKey: creds.secretAccessKey,
sessionToken: creds.sessionToken,
expiration: new Date(creds.expiresAt),
};
};
const s3 = new S3Client({ region: "us-east-1", credentials: fromVault });When to reach for this:
A common concrete case: keys stored in AWS Secrets Manager that rotate on a schedule.
The provider reads the secret, and by returning an expiration it lets the SDK re-fetch after rotation without a restart.
# --- Python (boto3) ---
import json
from datetime import datetime, timezone, timedelta
import boto3
from botocore.credentials import RefreshableCredentials
from botocore.session import get_session
sm = boto3.client("secretsmanager", region_name="us-east-1")
def fetch():
raw = sm.get_secret_value(SecretId="prod/app/aws-keys")["SecretString"]
data = json.loads(raw)
return {
"access_key": data["accessKeyId"],
"secret_key": data["secretAccessKey"],
"token": data.get("sessionToken"),
# Re-fetch in 15 minutes to pick up rotation.
"expiry_time": (datetime.now(timezone.utc) + timedelta(minutes=15)).isoformat(),
}
refreshable = RefreshableCredentials.create_from_metadata(
metadata=fetch(), refresh_using=fetch, method="secrets-manager",
)
bs = get_session()
bs._credentials = refreshable
s3 = boto3.Session(botocore_session=bs).client("s3", region_name="us-east-1")// --- TypeScript (AWS SDK v3) ---
import { S3Client } from "@aws-sdk/client-s3";
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
import type { AwsCredentialIdentityProvider } from "@aws-sdk/types";
const sm = new SecretsManagerClient({ region: "us-east-1" });
const fromSecret: AwsCredentialIdentityProvider = async () => {
const out = await sm.send(new GetSecretValueCommand({ SecretId: "prod/app/aws-keys" }));
const data = JSON.parse(out.SecretString ?? "{}");
return {
accessKeyId: data.accessKeyId,
secretAccessKey: data.secretAccessKey,
sessionToken: data.sessionToken,
// Re-invoke in 15 minutes to pick up rotation.
expiration: new Date(Date.now() + 15 * 60_000),
};
};
const s3 = new S3Client({ region: "us-east-1", credentials: fromSecret });What this demonstrates:
RefreshableCredentials.create_from_metadata binds an initial fetch and a refresh_using callback.expiry_time / expiration is what enables automatic refresh.boto3 and SDK v3 both refresh, but wire it differently.
RefreshableCredentials holds an expiry_time and calls refresh_using when credentials are within an advisory window of expiry. It refreshes lazily, on the next request that needs credentials.expiration nears, the SDK discards the cache and calls the function again. No expiration means "cache forever" - fine for static keys, wrong for rotating ones.Your provider often needs credentials to fetch credentials.
Reading Secrets Manager requires AWS auth, so the client that fetches the secret must itself authenticate through the normal chain - typically an instance or task role. Keep that bootstrap identity minimal: permission to read exactly one secret, nothing more.
The refresh callback can be invoked at any time, possibly concurrently.
Make it idempotent and fast. If the source is briefly unavailable, prefer raising a clear error over returning stale or partial credentials, and let the SDK's retry surface it. Cache within the provider only if your source is rate-limited.
Most "custom" needs are already covered.
Assuming a role? Use fromTemporaryCredentials / a profile. OIDC token? Use fromWebToken. An external CLI that prints credentials? boto3's credential_process and SDK v3's fromProcess already shell out for you. A custom provider is the last resort, not the first.
expiration for rotating credentials._credentials on the default session can surprise other code. Fix: build a dedicated botocore_session for the custom provider.fetch fast and consider a short internal cache.fromTemporaryCredentials, fromWebToken, or credential_process first.| Alternative | Use When | Don't Use When |
|---|---|---|
| Custom provider | Your source has no built-in provider | A built-in already covers it |
credential_process / fromProcess | An external CLI prints credentials JSON | You can call the source in-process cleanly |
fromTemporaryCredentials / profile | You need STS AssumeRole | The source is not an AWS role |
fromWebToken / web identity | An OIDC token maps to a role | The source is not OIDC-based |
Code that fetches credentials from a source the SDK does not natively support and hands them to a client, ideally with refresh support.
RefreshableCredentials calls your refresh_using callback when the current credentials near their expiry_time, lazily on the next request.
It memoizes the provider function and re-invokes it when the returned expiration approaches. Omitting expiration disables refresh.
A dict with access_key, secret_key, token, and expiry_time (ISO-8601). Pass it as metadata to create_from_metadata.
An object with accessKeyId, secretAccessKey, optional sessionToken, and optional expiration (a Date).
Reading Secrets Manager or another AWS source requires AWS auth. That bootstrap client uses the normal chain, typically an instance or task role.
The SDK treats the credentials as static and never refreshes, so rotated keys silently go stale until a restart. Always return an expiration for rotating sources.
Yes. Make it idempotent and fast, and add an internal cache only if your source is rate-limited.
No. Use fromTemporaryCredentials (SDK v3) or a role_arn profile (boto3). They handle AssumeRole and refresh for you.
Use credential_process in a boto3 profile or fromProcess in SDK v3. They already run an external command and parse its JSON output.
Raise a clear error rather than returning partial or stale credentials, and let the SDK's retry and your logs surface the failure.
No. It is a last resort when no built-in fits, because it is extra security-sensitive code you must maintain.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Revisado por Chris St. John·Última actualización: 23 jul 2026