
commonMain.aws.sdk.kotlin.runtime.auth.credentials.StsWebIdentityCredentialsProvider.kt Maven / Gradle / Ivy
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package aws.sdk.kotlin.runtime.auth.credentials
import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.StsClient
import aws.sdk.kotlin.runtime.auth.credentials.internal.sts.assumeRoleWithWebIdentity
import aws.sdk.kotlin.runtime.config.AwsSdkSetting
import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials
import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider
import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException
import aws.smithy.kotlin.runtime.auth.awscredentials.DEFAULT_CREDENTIALS_REFRESH_SECONDS
import aws.smithy.kotlin.runtime.config.EnvironmentSetting
import aws.smithy.kotlin.runtime.config.resolve
import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine
import aws.smithy.kotlin.runtime.telemetry.logging.logger
import aws.smithy.kotlin.runtime.telemetry.telemetryProvider
import aws.smithy.kotlin.runtime.time.TimestampFormat
import aws.smithy.kotlin.runtime.util.Attributes
import aws.smithy.kotlin.runtime.util.PlatformProvider
import kotlin.coroutines.coroutineContext
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds
private const val PROVIDER_NAME = "WebIdentityToken"
/**
* A [CredentialsProvider] that exchanges a Web Identity Token for credentials from the AWS Security Token Service (STS).
*
* @param roleArn The ARN of the target role to assume, e.g. `arn:aws:iam:123456789:role/example`
* @param webIdentityTokenFilePath The path to the file containing a JWT token
* @param region The AWS region to assume the role in
* @param roleSessionName The name to associate with the session. Use the role session name to uniquely identify a session
* when the same role is assumed by different principals or for different reasons. In cross-account scenarios, the
* role session name is visible to, and can be logged by the account that owns the role. The role session name is also
* in the ARN of the assumed role principal.
* @param duration The expiry duration of the credentials. Defaults to 15 minutes if not set.
* @param platformProvider The platform API provider
* @param httpClient the [HttpClientEngine] instance to use to make requests. NOTE: This engine's resources and lifetime
* are NOT managed by the provider. Caller is responsible for closing.
*/
public class StsWebIdentityCredentialsProvider(
private val roleArn: String,
private val webIdentityTokenFilePath: String,
private val region: String?,
private val roleSessionName: String? = null,
private val duration: Duration = DEFAULT_CREDENTIALS_REFRESH_SECONDS.seconds,
private val platformProvider: PlatformProvider = PlatformProvider.System,
private val httpClient: HttpClientEngine? = null,
) : CredentialsProvider {
public companion object {
/**
* Create an [StsWebIdentityCredentialsProvider] from the current execution environment. This will attempt
* to automatically resolve any setting not explicitly provided from the current set of environment variables
* or system properties.
*/
public fun fromEnvironment(
roleArn: String? = null,
webIdentityTokenFilePath: String? = null,
region: String? = null,
roleSessionName: String? = null,
duration: Duration = DEFAULT_CREDENTIALS_REFRESH_SECONDS.seconds,
platformProvider: PlatformProvider = PlatformProvider.System,
httpClient: HttpClientEngine? = null,
): StsWebIdentityCredentialsProvider {
val resolvedRoleArn = platformProvider.resolve(roleArn, AwsSdkSetting.AwsRoleArn, "roleArn")
val resolvedTokenFilePath = platformProvider.resolve(webIdentityTokenFilePath, AwsSdkSetting.AwsWebIdentityTokenFile, "webIdentityTokenFilePath")
val resolvedRegion = region ?: AwsSdkSetting.AwsRegion.resolve(platformProvider)
return StsWebIdentityCredentialsProvider(resolvedRoleArn, resolvedTokenFilePath, resolvedRegion, roleSessionName, duration, platformProvider, httpClient)
}
}
override suspend fun resolve(attributes: Attributes): Credentials {
val logger = coroutineContext.logger()
logger.debug { "retrieving assumed credentials via web identity" }
val provider = this
val token = platformProvider
.readFileOrNull(webIdentityTokenFilePath)
?.decodeToString() ?: throw CredentialsProviderException("failed to read webIdentityToken from $webIdentityTokenFilePath")
val telemetry = coroutineContext.telemetryProvider
val client = StsClient {
region = provider.region
httpClient = provider.httpClient
// NOTE: credentials provider not needed for this operation
telemetryProvider = telemetry
}
val resp = try {
client.assumeRoleWithWebIdentity {
roleArn = provider.roleArn
webIdentityToken = token
durationSeconds = provider.duration.inWholeSeconds.toInt()
roleSessionName = provider.roleSessionName ?: defaultSessionName(platformProvider)
}
} catch (ex: Exception) {
logger.debug { "sts refused to grant assumed role credentials from web identity" }
throw CredentialsProviderException("STS failed to assume role from web identity", ex)
} finally {
client.close()
}
val roleCredentials = resp.credentials ?: throw CredentialsProviderException("STS credentials must not be null")
logger.debug { "obtained assumed credentials via web identity; expiration=${roleCredentials.expiration?.format(TimestampFormat.ISO_8601)}" }
return Credentials(
accessKeyId = checkNotNull(roleCredentials.accessKeyId) { "Expected accessKeyId in STS assumeRoleWithWebIdentity response" },
secretAccessKey = checkNotNull(roleCredentials.secretAccessKey) { "Expected secretAccessKey in STS assumeRoleWithWebIdentity response" },
sessionToken = roleCredentials.sessionToken,
expiration = roleCredentials.expiration,
providerName = PROVIDER_NAME,
)
}
}
// convenience function to resolve parameters for fromEnvironment()
private inline fun PlatformProvider.resolve(explicit: T?, setting: EnvironmentSetting, name: String): T =
explicit
?: setting.resolve(this)
?: throw ProviderConfigurationException(
"Required field `$name` could not be automatically inferred for StsWebIdentityCredentialsProvider. Either explicitly pass a value, set the environment variable `${setting.envVar}`, or set the JVM system property `${setting.sysProp}`",
)
© 2015 - 2025 Weber Informatics LLC | Privacy Policy