@@ -5,8 +5,10 @@ package software.aws.toolkits.jetbrains.core.credentials.sso.bearer
5
5
6
6
import com.intellij.openapi.application.ApplicationManager
7
7
import com.intellij.testFramework.ApplicationRule
8
+ import com.intellij.testFramework.DisposableRule
8
9
import com.intellij.testFramework.RuleChain
9
10
import org.assertj.core.api.Assertions.assertThat
11
+ import org.junit.After
10
12
import org.junit.Before
11
13
import org.junit.Rule
12
14
import org.junit.Test
@@ -15,18 +17,30 @@ import org.mockito.Mockito
15
17
import org.mockito.kotlin.any
16
18
import org.mockito.kotlin.argThat
17
19
import org.mockito.kotlin.mock
20
+ import org.mockito.kotlin.spy
18
21
import org.mockito.kotlin.times
19
22
import org.mockito.kotlin.verify
20
23
import org.mockito.kotlin.verifyNoMoreInteractions
21
24
import org.mockito.kotlin.whenever
25
+ import software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider
26
+ import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration
22
27
import software.amazon.awssdk.core.exception.SdkException
28
+ import software.amazon.awssdk.core.interceptor.Context
29
+ import software.amazon.awssdk.core.interceptor.ExecutionAttributes
30
+ import software.amazon.awssdk.core.interceptor.ExecutionInterceptor
31
+ import software.amazon.awssdk.core.internal.InternalCoreExecutionAttribute
32
+ import software.amazon.awssdk.regions.Region
23
33
import software.amazon.awssdk.services.ssooidc.SsoOidcClient
24
34
import software.amazon.awssdk.services.ssooidc.model.AccessDeniedException
25
35
import software.amazon.awssdk.services.ssooidc.model.CreateTokenRequest
26
36
import software.amazon.awssdk.services.ssooidc.model.CreateTokenResponse
37
+ import software.amazon.awssdk.services.ssooidc.model.InvalidGrantException
27
38
import software.aws.toolkits.core.region.aRegionId
28
39
import software.aws.toolkits.core.utils.test.aString
40
+ import software.aws.toolkits.jetbrains.core.AwsClientManager
41
+ import software.aws.toolkits.jetbrains.core.MockClientManager
29
42
import software.aws.toolkits.jetbrains.core.MockClientManagerRule
43
+ import software.aws.toolkits.jetbrains.core.credentials.sono.SONO_URL
30
44
import software.aws.toolkits.jetbrains.core.credentials.sso.AccessToken
31
45
import software.aws.toolkits.jetbrains.core.credentials.sso.AccessTokenCacheKey
32
46
import software.aws.toolkits.jetbrains.core.credentials.sso.ClientRegistration
@@ -46,6 +60,10 @@ class InteractiveBearerTokenProviderTest {
46
60
mockClientManager
47
61
)
48
62
63
+ @Rule
64
+ @JvmField
65
+ val disposableRule = DisposableRule ()
66
+
49
67
private lateinit var oidcClient: SsoOidcClient
50
68
private val diskCache = mock<DiskCache >()
51
69
private val startUrl = aString()
@@ -57,29 +75,56 @@ class InteractiveBearerTokenProviderTest {
57
75
oidcClient = mockClientManager.create<SsoOidcClient >()
58
76
}
59
77
60
- @Test
61
- fun `reads last token from disk on initialziation` () {
62
- buildSut()
63
- verify(diskCache).loadAccessToken(
64
- argThat<AccessTokenCacheKey > {
65
- val (_, url, scopes) = this
66
- url == startUrl && scopes == this .scopes
67
- }
68
- )
78
+ @After
79
+ fun tearDown () {
80
+ oidcClient.close()
69
81
}
70
82
71
83
@Test
72
- fun `resolveToken refreshes from service if local token expired` () {
73
- stubClientRegistration()
74
- stubAccessToken()
75
- val sut = buildSut()
76
- sut.resolveToken()
84
+ fun `oidcClient retries twice on InvalidGrantException failure` () {
85
+ fun verifyRetryAttempts (configuration : ClientOverrideConfiguration .Builder ) {
86
+ configuration.addExecutionInterceptor(
87
+ object : ExecutionInterceptor {
88
+ override fun onExecutionFailure (context : Context .FailedExecution ? , executionAttributes : ExecutionAttributes ? ) {
89
+ super .onExecutionFailure(context, executionAttributes)
90
+
91
+ // 3 total network calls, showing 4 since the sdk increments the attempt count at the beginning
92
+ // of the loop before it checks whether it's allowed to retry.
93
+ assertThat(executionAttributes?.getAttribute(InternalCoreExecutionAttribute .EXECUTION_ATTEMPT )).isEqualTo(4 )
94
+ }
95
+ }
96
+ )
97
+ }
98
+ fun buildUnmanagedSsoOidcClientForTests (region : String ): SsoOidcClient =
99
+ AwsClientManager .getInstance()
100
+ .createUnmanagedClient(
101
+ AnonymousCredentialsProvider .create(),
102
+ Region .of(region),
103
+ clientCustomizer = { _, _, _, _, configuration ->
104
+ verifyRetryAttempts(ssoOidcClientConfigurationBuilder(configuration))
105
+ }
106
+ )
77
107
78
- verify(oidcClient).createToken(
79
- argThat<CreateTokenRequest > {
80
- grantType() == " refresh_token"
108
+ MockClientManager .useRealImplementations(disposableRule.disposable)
109
+ oidcClient = spy(buildUnmanagedSsoOidcClientForTests(" us-east-1" ))
110
+ val registerClientResponse = oidcClient.registerClient {
111
+ it.clientType(" public" )
112
+ it.scopes(scopes)
113
+ it.clientName(" test" )
114
+ }
115
+ val deviceAuthorizationResponse = oidcClient.startDeviceAuthorization {
116
+ it.clientId(registerClientResponse.clientId())
117
+ it.clientSecret(registerClientResponse.clientSecret())
118
+ it.startUrl(SONO_URL )
119
+ }
120
+ assertThrows<InvalidGrantException > {
121
+ oidcClient.createToken {
122
+ it.clientId(registerClientResponse.clientId())
123
+ it.clientSecret(registerClientResponse.clientSecret())
124
+ it.deviceCode(deviceAuthorizationResponse.deviceCode() + " invalid" )
125
+ it.grantType(" urn:ietf:params:oauth:grant-type:device_code" )
81
126
}
82
- )
127
+ }
83
128
}
84
129
85
130
@Test
0 commit comments