Skip to content

Commit 8839637

Browse files
authored
fear: login credentials provider (#1739)
1 parent 8a9dfcc commit 8839637

File tree

15 files changed

+1298
-4
lines changed

15 files changed

+1298
-4
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"id": "930c7904-1735-466b-9acc-c46e960c26c9",
3+
"type": "feature",
4+
"description": "Adds a new credentials provider for AWS Login token authentication"
5+
}

.github/workflows/minor-version-bump.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ jobs:
88
runs-on: ubuntu-latest
99
steps:
1010
- name: Minor version bump check
11-
uses: awslabs/aws-kotlin-repo-tools/.github/actions/minor-version-bump@main
11+
uses: aws/aws-kotlin-repo-tools/.github/actions/minor-version-bump@main

.github/workflows/service-ci.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
env-vars-for-codebuild: GITHUB_REPOSITORY, UPLOAD, RELEASE_METRICS, IDENTIFIER
2929

3030
- name: Process metrics
31-
uses: awslabs/aws-kotlin-repo-tools/.github/actions/artifact-size-metrics/download-and-process@main
31+
uses: aws/aws-kotlin-repo-tools/.github/actions/artifact-size-metrics/download-and-process@main
3232
with:
3333
download: 'true'
3434

@@ -49,4 +49,3 @@ concurrency:
4949
permissions:
5050
id-token: write
5151
contents: read
52-

aws-runtime/aws-config/api/aws-config.api

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,27 @@ public final class aws/sdk/kotlin/runtime/auth/credentials/InvalidJsonCredential
9292
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
9393
}
9494

95+
public final class aws/sdk/kotlin/runtime/auth/credentials/InvalidLoginTokenException : aws/sdk/kotlin/runtime/ConfigurationException {
96+
public fun <init> (Ljava/lang/String;Ljava/lang/Throwable;)V
97+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
98+
}
99+
95100
public final class aws/sdk/kotlin/runtime/auth/credentials/InvalidSsoTokenException : aws/sdk/kotlin/runtime/ConfigurationException {
96101
public fun <init> (Ljava/lang/String;Ljava/lang/Throwable;)V
97102
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/Throwable;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
98103
}
99104

105+
public final class aws/sdk/kotlin/runtime/auth/credentials/LoginCredentialsProvider : aws/smithy/kotlin/runtime/auth/awscredentials/CloseableCredentialsProvider {
106+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/time/Clock;)V
107+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;Laws/smithy/kotlin/runtime/util/PlatformProvider;Laws/smithy/kotlin/runtime/time/Clock;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
108+
public fun close ()V
109+
public final fun getHttpClient ()Laws/smithy/kotlin/runtime/http/engine/HttpClientEngine;
110+
public final fun getLoginSession ()Ljava/lang/String;
111+
public final fun getPlatformProvider ()Laws/smithy/kotlin/runtime/util/PlatformProvider;
112+
public final fun getRegion ()Ljava/lang/String;
113+
public fun resolve (Laws/smithy/kotlin/runtime/collections/Attributes;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
114+
}
115+
100116
public final class aws/sdk/kotlin/runtime/auth/credentials/ProcessCredentialsProvider : aws/smithy/kotlin/runtime/auth/awscredentials/CredentialsProvider {
101117
public fun <init> (Ljava/lang/String;Laws/smithy/kotlin/runtime/util/PlatformProvider;JJ)V
102118
public synthetic fun <init> (Ljava/lang/String;Laws/smithy/kotlin/runtime/util/PlatformProvider;JJILkotlin/jvm/internal/DefaultConstructorMarker;)V
@@ -237,6 +253,63 @@ public final class aws/sdk/kotlin/runtime/auth/credentials/internal/ManagedCrede
237253
public static final fun manage (Laws/smithy/kotlin/runtime/auth/awscredentials/CloseableCredentialsProvider;)Laws/smithy/kotlin/runtime/auth/awscredentials/CredentialsProvider;
238254
}
239255

256+
public abstract class aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode {
257+
public static final field Companion Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$Companion;
258+
public abstract fun getValue ()Ljava/lang/String;
259+
}
260+
261+
public final class aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$AuthcodeExpired : aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode {
262+
public static final field INSTANCE Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$AuthcodeExpired;
263+
public fun getValue ()Ljava/lang/String;
264+
public fun toString ()Ljava/lang/String;
265+
}
266+
267+
public final class aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$Companion {
268+
public final fun fromValue (Ljava/lang/String;)Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode;
269+
public final fun values ()Ljava/util/List;
270+
}
271+
272+
public final class aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$InsufficientPermissions : aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode {
273+
public static final field INSTANCE Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$InsufficientPermissions;
274+
public fun getValue ()Ljava/lang/String;
275+
public fun toString ()Ljava/lang/String;
276+
}
277+
278+
public final class aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$InvalidRequest : aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode {
279+
public static final field INSTANCE Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$InvalidRequest;
280+
public fun getValue ()Ljava/lang/String;
281+
public fun toString ()Ljava/lang/String;
282+
}
283+
284+
public final class aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$SdkUnknown : aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode {
285+
public fun <init> (Ljava/lang/String;)V
286+
public final fun component1 ()Ljava/lang/String;
287+
public final fun copy (Ljava/lang/String;)Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$SdkUnknown;
288+
public static synthetic fun copy$default (Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$SdkUnknown;Ljava/lang/String;ILjava/lang/Object;)Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$SdkUnknown;
289+
public fun equals (Ljava/lang/Object;)Z
290+
public fun getValue ()Ljava/lang/String;
291+
public fun hashCode ()I
292+
public fun toString ()Ljava/lang/String;
293+
}
294+
295+
public final class aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$ServerError : aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode {
296+
public static final field INSTANCE Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$ServerError;
297+
public fun getValue ()Ljava/lang/String;
298+
public fun toString ()Ljava/lang/String;
299+
}
300+
301+
public final class aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$TokenExpired : aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode {
302+
public static final field INSTANCE Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$TokenExpired;
303+
public fun getValue ()Ljava/lang/String;
304+
public fun toString ()Ljava/lang/String;
305+
}
306+
307+
public final class aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$UserCredentialsChanged : aws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode {
308+
public static final field INSTANCE Laws/sdk/kotlin/runtime/auth/credentials/internal/signin/model/OAuth2ErrorCode$UserCredentialsChanged;
309+
public fun getValue ()Ljava/lang/String;
310+
public fun toString ()Ljava/lang/String;
311+
}
312+
240313
public abstract class aws/sdk/kotlin/runtime/config/AbstractAwsSdkClientFactory : aws/smithy/kotlin/runtime/client/AbstractSdkClientFactory {
241314
public fun <init> ()V
242315
protected fun finalizeEnvironmentalConfig (Laws/smithy/kotlin/runtime/client/SdkClient$Builder;Laws/smithy/kotlin/runtime/util/LazyAsyncValue;Laws/smithy/kotlin/runtime/util/LazyAsyncValue;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;

aws-runtime/aws-config/build.gradle.kts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ dependencies {
7676
codegen(project(":codegen:aws-sdk-codegen"))
7777
codegen(libs.smithy.cli)
7878
codegen(libs.smithy.model)
79+
codegen(libs.smithy.aws.smoke.test.model)
7980
}
8081

8182
smithyBuild {
@@ -188,6 +189,41 @@ smithyBuild {
188189
""",
189190
)
190191
}
192+
193+
create("signin-credentials-provider") {
194+
imports = listOf(
195+
awsModelFile("signin.json"),
196+
)
197+
198+
val serviceShape = "com.amazonaws.signin#Signin"
199+
smithyKotlinPlugin {
200+
serviceShapeId = serviceShape
201+
packageName = "$basePackage.signin"
202+
packageVersion = project.version.toString()
203+
packageDescription = "Internal Signin credentials provider"
204+
sdkId = "Signin"
205+
buildSettings {
206+
generateDefaultBuildFiles = false
207+
generateFullProject = false
208+
}
209+
apiSettings {
210+
visibility = "internal"
211+
}
212+
}
213+
214+
transforms = listOf(
215+
"""
216+
{
217+
"name": "awsSmithyKotlinIncludeOperations",
218+
"args": {
219+
"operations": [
220+
"com.amazonaws.signin#CreateOAuth2Token"
221+
]
222+
}
223+
}
224+
""",
225+
)
226+
}
191227
}
192228
}
193229

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package aws.sdk.kotlin.runtime.auth.credentials
7+
8+
import aws.sdk.kotlin.runtime.auth.credentials.internal.signin.SigninClient
9+
import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.AwsBusinessMetric
10+
import aws.sdk.kotlin.runtime.http.interceptors.businessmetrics.withBusinessMetric
11+
import aws.smithy.kotlin.runtime.auth.awscredentials.CloseableCredentialsProvider
12+
import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials
13+
import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider
14+
import aws.smithy.kotlin.runtime.collections.Attributes
15+
import aws.smithy.kotlin.runtime.http.engine.HttpClientEngine
16+
import aws.smithy.kotlin.runtime.telemetry.logging.logger
17+
import aws.smithy.kotlin.runtime.time.Clock
18+
import aws.smithy.kotlin.runtime.util.PlatformProvider
19+
import kotlinx.coroutines.runBlocking
20+
import kotlin.coroutines.coroutineContext
21+
22+
/**
23+
* [CredentialsProvider] that uses AWS Login to source credentials.
24+
*
25+
* The provider does not initiate or perform the AWS Login flow. It is expected that you have
26+
* already performed the login flow using the AWS CLI (`aws login`). The provider
27+
* expects a valid non-expired access token for the AWS Login session in `~/.aws/login/cache` or
28+
* the directory specified by the `AWS_LOGIN_CACHE_DIRECTORY` environment variable.
29+
* If a cached token is not found, is expired, or the file is malformed an exception will be thrown.
30+
*
31+
* **Instantiating AWS Login provider directly**
32+
*
33+
* You can programmatically construct the AWS Login provider in your application, and provide the necessary
34+
* information to load and retrieve temporary credentials using an access token from `~/.aws/login/cache` or
35+
* the directory specified by the `AWS_LOGIN_CACHE_DIRECTORY` environment variable.
36+
*
37+
* ```
38+
* // Wrap the provider with a caching provider to cache the credentials until their expiration time
39+
* val loginProvider = LoginCredentialsProvider(
40+
* loginSession = "my-login-session"
41+
* ).cached()
42+
* ```
43+
* It is important that you wrap the provider with [CachedCredentialsProvider] if you are programmatically constructing
44+
* the provider directly. This prevents your application from accessing the cached access token and requesting new
45+
* credentials each time the provider is used to source credentials.
46+
*
47+
* @param loginSession The Login Session from the profile
48+
* @param region The AWS region used to call the log in service.
49+
* @param httpClient The [HttpClientEngine] instance to use to make requests. NOTE: This engine's resources and lifetime
50+
* are NOT managed by the provider. Caller is responsible for closing.
51+
* @param platformProvider The platform provider
52+
* @param clock The source of time for the provider
53+
*/
54+
public class LoginCredentialsProvider public constructor(
55+
public val loginSession: String,
56+
public val region: String? = null,
57+
public val httpClient: HttpClientEngine? = null,
58+
public val platformProvider: PlatformProvider = PlatformProvider.System,
59+
private val clock: Clock = Clock.System,
60+
) : CloseableCredentialsProvider {
61+
private val cacheDirectory = resolveCacheDir(platformProvider)
62+
private val client = runBlocking { signinClient(region, httpClient) }
63+
64+
override suspend fun resolve(attributes: Attributes): Credentials {
65+
val logger = coroutineContext.logger<LoginCredentialsProvider>()
66+
67+
val loginTokenProvider =
68+
LoginTokenProvider(
69+
loginSession,
70+
region,
71+
httpClient = httpClient,
72+
platformProvider = platformProvider,
73+
clock = clock,
74+
cacheDirectory = cacheDirectory,
75+
client = client,
76+
)
77+
78+
logger.trace { "Attempting to load token using token provider for login-session: `$loginSession`" }
79+
val creds = loginTokenProvider.resolve(attributes)
80+
81+
return creds.withBusinessMetric(AwsBusinessMetric.Credentials.CREDENTIALS_LOGIN)
82+
}
83+
84+
override fun close() {
85+
client.close()
86+
}
87+
}
88+
89+
internal fun resolveCacheDir(platformProvider: PlatformProvider) =
90+
platformProvider.getenv("AWS_LOGIN_IN_CACHE_DIRECTORY") ?: platformProvider.filepath("~", ".aws", "login", "cache")
91+
92+
internal suspend fun signinClient(providedRegion: String? = null, providedHttpClient: HttpClientEngine? = null) =
93+
SigninClient.fromEnvironment {
94+
region = providedRegion
95+
httpClient = providedHttpClient
96+
}

0 commit comments

Comments
 (0)