Skip to content

Commit eef50ee

Browse files
authored
Centralize bearer token auth logic and add more logs (#3538)
1 parent e691afc commit eef50ee

File tree

6 files changed

+42
-66
lines changed

6 files changed

+42
-66
lines changed

jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/ConnectionSettingsMenuBuilder.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ class ConnectionSettingsMenuBuilder private constructor() {
241241
addAll(
242242
object : DumbAwareAction(message("credentials.individual_identity.reconnect")) {
243243
override fun actionPerformed(e: AnActionEvent) {
244-
reauthProviderIfNeeded(value)
244+
reauthProviderIfNeeded(e.project, value)
245245
}
246246
},
247247

jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/ToolkitAuthManager.kt

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import software.aws.toolkits.core.TokenConnectionSettings
1515
import software.aws.toolkits.core.credentials.ToolkitBearerTokenProvider
1616
import software.aws.toolkits.core.utils.getLogger
1717
import software.aws.toolkits.core.utils.info
18+
import software.aws.toolkits.core.utils.warn
1819
import software.aws.toolkits.jetbrains.core.credentials.pinning.FeatureWithPinnedConnection
1920
import software.aws.toolkits.jetbrains.core.credentials.sono.ALL_SONO_SCOPES
2021
import software.aws.toolkits.jetbrains.core.credentials.sono.isSono
@@ -165,7 +166,7 @@ fun loginSso(project: Project?, startUrl: String, scopes: List<String> = ALL_SON
165166
}
166167

167168
private fun reauthConnection(project: Project?, connection: ToolkitConnection): BearerTokenProvider {
168-
val provider = reauthProviderIfNeeded(connection)
169+
val provider = reauthProviderIfNeeded(project, connection)
169170

170171
ToolkitConnectionManager.getInstance(project).switchConnection(connection)
171172

@@ -204,23 +205,42 @@ fun AwsBearerTokenConnection.lazyIsUnauthedBearerConnection(): Boolean {
204205
return false
205206
}
206207

207-
fun reauthProviderIfNeeded(connection: ToolkitConnection): BearerTokenProvider {
208+
fun reauthProviderIfNeeded(project: Project?, connection: ToolkitConnection): BearerTokenProvider {
208209
val tokenProvider = (connection.getConnectionSettings() as TokenConnectionSettings).tokenProvider.delegate as BearerTokenProvider
210+
211+
return reauthProviderIfNeeded(project, tokenProvider)
212+
}
213+
214+
fun reauthProviderIfNeeded(project: Project?, tokenProvider: BearerTokenProvider): BearerTokenProvider {
215+
maybeReauthProviderIfNeeded(project, tokenProvider) {
216+
runUnderProgressIfNeeded(project, message("credentials.sono.login.pending"), true) {
217+
tokenProvider.reauthenticate()
218+
}
219+
}
220+
221+
return tokenProvider
222+
}
223+
224+
fun maybeReauthProviderIfNeeded(project: Project?, tokenProvider: BearerTokenProvider, onReauthRequired: (SsoOidcException?) -> Any) {
209225
val state = tokenProvider.state()
210-
runUnderProgressIfNeeded(null, message("settings.states.validating.short"), true) {
211-
if (state == BearerTokenAuthState.NEEDS_REFRESH) {
226+
when (state) {
227+
BearerTokenAuthState.NOT_AUTHENTICATED -> {
228+
getLogger<ToolkitAuthManager>().info { "Token provider NOT_AUTHENTICATED, requesting login" }
229+
onReauthRequired(null)
230+
}
231+
232+
BearerTokenAuthState.NEEDS_REFRESH -> {
212233
try {
213-
tokenProvider.resolveToken()
214-
BearerTokenProviderListener.notifyCredUpdate(tokenProvider.id)
234+
runUnderProgressIfNeeded(project, message("credentials.sono.login.refreshing"), true) {
235+
tokenProvider.resolveToken()
236+
BearerTokenProviderListener.notifyCredUpdate(tokenProvider.id)
237+
}
215238
} catch (e: SsoOidcException) {
216-
tokenProvider.reauthenticate()
239+
getLogger<ToolkitAuthManager>().warn(e) { "Redriving AWS Builder ID login flow since token could not be refreshed" }
240+
onReauthRequired(e)
217241
}
218-
} else if (state == BearerTokenAuthState.NOT_AUTHENTICATED) {
219-
tokenProvider.reauthenticate()
220242
}
221243

222-
Unit
244+
BearerTokenAuthState.AUTHORIZED -> {}
223245
}
224-
225-
return tokenProvider
226246
}

jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/sono/SonoCredentialManager.kt

Lines changed: 4 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,18 @@ package software.aws.toolkits.jetbrains.core.credentials.sono
55

66
import com.intellij.openapi.components.service
77
import com.intellij.openapi.project.Project
8-
import software.amazon.awssdk.services.ssooidc.model.SsoOidcException
98
import software.aws.toolkits.core.TokenConnectionSettings
109
import software.aws.toolkits.core.credentials.ToolkitBearerTokenProvider
1110
import software.aws.toolkits.core.telemetry.DefaultMetricEvent
12-
import software.aws.toolkits.core.utils.getLogger
1311
import software.aws.toolkits.core.utils.tryOrNull
14-
import software.aws.toolkits.core.utils.warn
1512
import software.aws.toolkits.jetbrains.core.AwsResourceCache
1613
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
17-
import software.aws.toolkits.jetbrains.core.credentials.ToolkitAuthManager
1814
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
1915
import software.aws.toolkits.jetbrains.core.credentials.loginSso
2016
import software.aws.toolkits.jetbrains.core.credentials.pinning.CodeCatalystConnection
17+
import software.aws.toolkits.jetbrains.core.credentials.reauthProviderIfNeeded
2118
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenAuthState
2219
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenProvider
23-
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenProviderListener
2420
import software.aws.toolkits.jetbrains.core.region.AwsRegionProvider
2521
import software.aws.toolkits.jetbrains.services.caws.CawsResources
2622
import software.aws.toolkits.jetbrains.utils.runUnderProgressIfNeeded
@@ -72,35 +68,11 @@ class SonoCredentialManager {
7268
fun getProviderAndPromptAuth(): BearerTokenProvider {
7369
val provider = provider()
7470
return when (provider?.state()) {
75-
null -> runUnderProgressIfNeeded(null, message("credentials.sono.login.pending"), true) {
71+
null -> runUnderProgressIfNeeded(project, message("credentials.sono.login.pending"), true) {
7672
loginSso(project, SONO_URL, ALL_SONO_SCOPES)
7773
}
7874

79-
BearerTokenAuthState.NOT_AUTHENTICATED -> {
80-
runUnderProgressIfNeeded(null, message("credentials.sono.login.pending"), true) {
81-
provider.reauthenticate()
82-
}
83-
84-
return provider
85-
}
86-
87-
BearerTokenAuthState.NEEDS_REFRESH -> {
88-
try {
89-
runUnderProgressIfNeeded(null, message("credentials.sono.login.refreshing"), true) {
90-
provider.resolveToken()
91-
BearerTokenProviderListener.notifyCredUpdate(provider.id)
92-
}
93-
} catch (e: SsoOidcException) {
94-
LOG.warn(e) { "Redriving AWS Builder ID login flow since token could not be refreshed" }
95-
runUnderProgressIfNeeded(null, message("credentials.sono.login.pending"), true) {
96-
provider.reauthenticate()
97-
}
98-
}
99-
100-
return provider
101-
}
102-
103-
BearerTokenAuthState.AUTHORIZED -> provider
75+
else -> reauthProviderIfNeeded(project, provider)
10476
}
10577
}
10678

@@ -112,13 +84,9 @@ class SonoCredentialManager {
11284
fun getInstance(project: Project? = null) = project?.let { it.service<SonoCredentialManager>() } ?: service()
11385

11486
fun loginSono(project: Project?) {
115-
val provider = SonoCredentialManager.getInstance(project).getProviderAndPromptAuth()
116-
val connection = ToolkitAuthManager.getInstance().getConnection(provider.id) ?: error("Mismatch between provider id and connection id")
117-
ToolkitConnectionManager.getInstance(project).switchConnection(connection)
87+
SonoCredentialManager.getInstance(project).getProviderAndPromptAuth()
11888
}
11989

120-
private val LOG = getLogger<SonoCredentialManager>()
121-
12290
private val SSO_REGION by lazy {
12391
with(AwsRegionProvider.getInstance()) {
12492
get(SONO_REGION) ?: throw RuntimeException("AwsRegionProvider was unable to provide SSO_REGION for AWS Builder ID")

jetbrains-core/src/software/aws/toolkits/jetbrains/core/credentials/sso/SsoAccessTokenProvider.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,10 +141,10 @@ class SsoAccessTokenProvider(
141141

142142
fun refreshToken(currentToken: AccessToken): AccessToken {
143143
if (currentToken.refreshToken == null) {
144-
throw InvalidRequestException.builder().build()
144+
throw InvalidRequestException.builder().message("Requested token refresh, but refresh token was null").build()
145145
}
146146

147-
val registration = loadClientRegistration() ?: throw InvalidClientException.builder().build()
147+
val registration = loadClientRegistration() ?: throw InvalidClientException.builder().message("Unable to load client registration").build()
148148

149149
val newToken = client.createToken {
150150
it.clientId(registration.clientId)

jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/util/CodeWhispererUtil.kt

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import com.intellij.openapi.application.ApplicationManager
99
import com.intellij.openapi.application.runInEdt
1010
import com.intellij.openapi.project.Project
1111
import software.amazon.awssdk.services.codewhisperer.model.Recommendation
12-
import software.amazon.awssdk.services.ssooidc.model.SsoOidcException
1312
import software.aws.toolkits.jetbrains.core.credentials.AwsBearerTokenConnection
1413
import software.aws.toolkits.jetbrains.core.credentials.BearerSsoConnection
1514
import software.aws.toolkits.jetbrains.core.credentials.ManagedBearerSsoConnection
1615
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnection
1716
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
1817
import software.aws.toolkits.jetbrains.core.credentials.loginSso
1918
import software.aws.toolkits.jetbrains.core.credentials.logoutFromSsoConnection
19+
import software.aws.toolkits.jetbrains.core.credentials.maybeReauthProviderIfNeeded
2020
import software.aws.toolkits.jetbrains.core.credentials.pinning.CodeWhispererConnection
2121
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenAuthState
2222
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenProvider
@@ -29,7 +29,6 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.actions.DoNotShowA
2929
import software.aws.toolkits.jetbrains.utils.notifyError
3030
import software.aws.toolkits.jetbrains.utils.notifyInfo
3131
import software.aws.toolkits.jetbrains.utils.notifyWarn
32-
import software.aws.toolkits.jetbrains.utils.runUnderProgressIfNeeded
3332
import software.aws.toolkits.resources.message
3433
import software.aws.toolkits.telemetry.CodewhispererCompletionType
3534
import java.net.URI
@@ -104,19 +103,7 @@ object CodeWhispererUtil {
104103
val connection = ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(CodeWhispererConnection.getInstance())
105104
if (connection !is BearerSsoConnection) return
106105
val tokenProvider = tokenProvider(project) ?: return
107-
val state = tokenProvider.state()
108-
if (state == BearerTokenAuthState.NEEDS_REFRESH) {
109-
try {
110-
runUnderProgressIfNeeded(null, message("settings.states.validating.short"), false) {
111-
tokenProvider.resolveToken()
112-
}
113-
} catch (e: SsoOidcException) {
114-
runInEdt {
115-
notifyConnectionExpired(project, connection)
116-
callback()
117-
}
118-
}
119-
} else if (state == BearerTokenAuthState.NOT_AUTHENTICATED) {
106+
maybeReauthProviderIfNeeded(project, tokenProvider) {
120107
runInEdt {
121108
notifyConnectionExpired(project, connection)
122109
callback()

jetbrains-core/tst/software/aws/toolkits/jetbrains/core/credentials/DefaultToolkitAuthManagerTest.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ class DefaultToolkitAuthManagerTest {
291291

292292
mockConstruction(InteractiveBearerTokenProvider::class.java) { context, _ ->
293293
doNothing().whenever(context).reauthenticate()
294+
whenever(context.state()).thenReturn(BearerTokenAuthState.NOT_AUTHENTICATED)
294295
}.use {
295296
// before
296297
assertThat(sut.listConnections()).hasSize(0)

0 commit comments

Comments
 (0)