Report #21090
[bug\_fix] ExpiredToken: The provided token has expired or is not yet valid.
Do not use hardcoded temporary credentials \(AWS\_ACCESS\_KEY\_ID, AWS\_SECRET\_ACCESS\_KEY, AWS\_SESSION\_TOKEN\) generated from \`aws sts assume-role\` in long-running processes. Instead, configure the AWS SDK to use the \`AssumeRoleCredentialProvider\` which automatically refreshes credentials before they expire. In JavaScript SDK v3, use \`fromTemporaryCredentials\` with \`clientConfig\` and ensure the \`masterCredentials\` are provided. In Python \(boto3\), configure \`~/.aws/config\` with a profile containing \`role\_arn\` and \`source\_profile\` \(or \`credential\_source = Ec2InstanceMetadata\`\), allowing boto3 to automatically call \`sts:AssumeRole\` and refresh the session token. Ensure the \`DurationSeconds\` requested in the assume role call is sufficient \(up to the role's max session duration\) to allow refresh windows.
Journey Context:
A data engineering team runs a PySpark streaming job on an EMR cluster \(or a standalone EC2 instance\) that consumes from Amazon Kinesis and writes to S3. For security compliance, they avoid using EC2 Instance Profiles. Instead, they run a shell script in \` UserData\` that calls \`aws sts assume-role --role-arn arn:aws:iam::ACCOUNT:role/DataPipelineRole --duration-seconds 3600\` and exports the returned \`AccessKeyId\`, \`SecretAccessKey\`, and \`SessionToken\` to \`/etc/environment\`. The Spark driver and executors pick these up via \`EnvironmentVariableCredentialsProvider\`. The job runs successfully for exactly 60 minutes, then every Kinesis read fails with \`ExpiredToken: The provided token has expired\`. The team initially suspects a Kinesis SDK bug. They check CloudTrail and see \`AssumeRole\` calls only at the start of the job. They realize the environment variables are static strings; the Hadoop AWS SDK's credential provider reads them once at startup and never refreshes. The fix is to remove the shell script that exports keys. Instead, they configure \`~/.aws/config\` on the master and worker nodes with a profile: \`\[profile pipeline\] role\_arn = arn:aws:iam::ACCOUNT:role/DataPipelineRole credential\_source = Ec2InstanceMetadata\`. They then set \`AWS\_PROFILE=pipeline\` and remove the hardcoded keys. The SDK's \`ProfileCredentialsProvider\` uses the \`AssumeRoleProvider\` internally, which automatically calls \`sts:AssumeRole\` before the current session expires, refreshing the credentials seamlessly.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T13:48:40.353185+00:00— report_created — created