Skip to content

Commit 2dfa9ce

Browse files
authored
Added authType to loginWithBrowser metric (#4843)
* Added authType to loginWithBrowser metric * Add open and close sign in metrics * detekt * addressed feedback * feedback * moved the util function
1 parent 279b9b0 commit 2dfa9ce

File tree

8 files changed

+107
-33
lines changed

8 files changed

+107
-33
lines changed

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/controller/chat/telemetry/TelemetryHelper.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import software.aws.toolkits.core.utils.debug
1212
import software.aws.toolkits.core.utils.getLogger
1313
import software.aws.toolkits.core.utils.info
1414
import software.aws.toolkits.core.utils.warn
15+
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnection
16+
import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
17+
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
1518
import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
1619
import software.aws.toolkits.jetbrains.services.codewhisperer.credentials.CodeWhispererClientAdaptor
1720
import software.aws.toolkits.jetbrains.services.codewhisperer.customization.CodeWhispererCustomization
@@ -29,6 +32,7 @@ import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService
2932
import software.aws.toolkits.jetbrains.utils.notifyError
3033
import software.aws.toolkits.resources.message
3134
import software.aws.toolkits.telemetry.AmazonqTelemetry
35+
import software.aws.toolkits.telemetry.AuthTelemetry
3236
import software.aws.toolkits.telemetry.CwsprChatCommandType
3337
import software.aws.toolkits.telemetry.CwsprChatConversationType
3438
import software.aws.toolkits.telemetry.CwsprChatInteractionType
@@ -386,12 +390,20 @@ class TelemetryHelper(private val context: AmazonQAppInitContext, private val se
386390
companion object {
387391
private val logger = getLogger<TelemetryHelper>()
388392

393+
fun getQConnection(): ToolkitConnection? = ToolkitConnectionManager.getInstance(null).activeConnectionForFeature(QConnection.getInstance())
394+
389395
fun recordOpenChat() {
390396
AmazonqTelemetry.openChat(passive = true)
397+
if (getQConnection() == null) {
398+
AuthTelemetry.signInPageOpened()
399+
}
391400
}
392401

393402
fun recordCloseChat() {
394403
AmazonqTelemetry.closeChat(passive = true)
404+
if (getQConnection() == null) {
405+
AuthTelemetry.signInPageClosed()
406+
}
395407
}
396408

397409
fun recordTelemetryChatRunCommand(type: CwsprChatCommandType, name: String? = null, startUrl: String? = null) {

plugins/core/jetbrains-community/resources/telemetryOverride.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,15 @@
308308
"reloaded"
309309
],
310310
"description": "Toolkit run state."
311+
},
312+
{
313+
"name": "authType",
314+
"allowedValues": [
315+
"PKCE",
316+
"DeviceCode",
317+
"IAM"
318+
],
319+
"description": "Login is with Legacy device code or newer PKCE flow"
311320
}
312321
],
313322
"metrics": [
@@ -791,6 +800,9 @@
791800
},
792801
{
793802
"type": "result"
803+
},
804+
{
805+
"type": "authType"
794806
}
795807
]
796808
},
@@ -839,4 +851,4 @@
839851
]
840852
}
841853
]
842-
}
854+
}

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

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ import software.aws.toolkits.core.utils.warn
2222
import software.aws.toolkits.jetbrains.core.credentials.sono.SONO_URL
2323
import software.aws.toolkits.jetbrains.core.credentials.sso.pkce.PKCE_CLIENT_NAME
2424
import software.aws.toolkits.jetbrains.core.credentials.sso.pkce.ToolkitOAuthService
25+
import software.aws.toolkits.jetbrains.core.webview.getAuthType
2526
import software.aws.toolkits.jetbrains.utils.assertIsNonDispatchThread
2627
import software.aws.toolkits.jetbrains.utils.sleepWithCancellation
2728
import software.aws.toolkits.resources.AwsCoreBundle
29+
import software.aws.toolkits.telemetry.AuthType
2830
import software.aws.toolkits.telemetry.AwsTelemetry
2931
import software.aws.toolkits.telemetry.CredentialSourceId
3032
import software.aws.toolkits.telemetry.Result
@@ -159,8 +161,7 @@ class SsoAccessTokenProvider(
159161
return it
160162
}
161163

162-
val isCommercialRegion = !ssoRegion.startsWith("us-gov") && !ssoRegion.startsWith("us-iso") && !ssoRegion.startsWith("cn")
163-
val token = if (isCommercialRegion && isNewAuthPkce) {
164+
val token = if (getAuthType(ssoRegion) == AuthType.PKCE) {
164165
pollForPkceToken()
165166
} else {
166167
pollForDAGToken()
@@ -336,11 +337,12 @@ class SsoAccessTokenProvider(
336337
}
337338
}
338339

339-
private fun sendFailedRefreshCredentialsMetricIfNeeded(
340+
private fun sendRefreshCredentialsMetric(
340341
currentToken: AccessToken,
341-
reason: String,
342-
reasonDesc: String,
343-
requestId: String? = null
342+
reason: String?,
343+
reasonDesc: String?,
344+
requestId: String? = null,
345+
result: Result
344346
) {
345347
val tokenCreationTime = currentToken.createdAt
346348
val sessionDuration = Duration.between(Instant.now(clock), tokenCreationTime)
@@ -349,7 +351,7 @@ class SsoAccessTokenProvider(
349351
if (tokenCreationTime != Instant.EPOCH) {
350352
AwsTelemetry.refreshCredentials(
351353
project = null,
352-
result = Result.Failed,
354+
result = result,
353355
sessionDuration = sessionDuration.toHours().toInt(),
354356
credentialSourceId = credentialSourceId,
355357
reason = reason,
@@ -362,10 +364,11 @@ class SsoAccessTokenProvider(
362364
fun refreshToken(currentToken: AccessToken): AccessToken {
363365
if (currentToken.refreshToken == null) {
364366
val message = "Requested token refresh, but refresh token was null"
365-
sendFailedRefreshCredentialsMetricIfNeeded(
367+
sendRefreshCredentialsMetric(
366368
currentToken,
367369
reason = "Null refresh token",
368-
reasonDesc = message
370+
reasonDesc = message,
371+
result = Result.Failed
369372
)
370373
throw InvalidRequestException.builder().message(message).build()
371374
}
@@ -376,10 +379,11 @@ class SsoAccessTokenProvider(
376379
}
377380
if (registration == null) {
378381
val message = "Unable to load client registration"
379-
sendFailedRefreshCredentialsMetricIfNeeded(
382+
sendRefreshCredentialsMetric(
380383
currentToken,
381384
reason = "Null client registration",
382-
reasonDesc = message
385+
reasonDesc = message,
386+
result = Result.Failed
383387
)
384388
throw InvalidClientException.builder().message(message).build()
385389
}
@@ -399,6 +403,13 @@ class SsoAccessTokenProvider(
399403

400404
saveAccessToken(token)
401405

406+
sendRefreshCredentialsMetric(
407+
currentToken,
408+
result = Result.Succeeded,
409+
reason = null,
410+
reasonDesc = null
411+
)
412+
402413
return token
403414
} catch (e: Exception) {
404415
val requestId = when (e) {
@@ -409,11 +420,12 @@ class SsoAccessTokenProvider(
409420
is AwsServiceException -> e.awsErrorDetails()?.errorMessage() ?: "Unknown error"
410421
else -> e.message ?: "Unknown error"
411422
}
412-
sendFailedRefreshCredentialsMetricIfNeeded(
423+
sendRefreshCredentialsMetric(
413424
currentToken,
414425
reason = "Refresh access token request failed",
415426
reasonDesc = message,
416-
requestId = requestId
427+
requestId = requestId,
428+
result = Result.Failed
417429
)
418430
throw e
419431
}

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/credentials/sso/SsoLoginCallbackProvider.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.ConfirmUserCo
1111
import software.aws.toolkits.jetbrains.utils.computeOnEdt
1212
import software.aws.toolkits.jetbrains.utils.notifyError
1313
import software.aws.toolkits.resources.AwsCoreBundle
14+
import software.aws.toolkits.telemetry.AuthType
1415
import software.aws.toolkits.telemetry.AwsTelemetry
1516
import software.aws.toolkits.telemetry.CredentialType
1617
import software.aws.toolkits.telemetry.Result
@@ -38,12 +39,12 @@ class DefaultSsoLoginCallbackProvider : SsoLoginCallbackProvider {
3839

3940
interface SsoPrompt : SsoLoginCallback {
4041
override fun tokenRetrieved() {
41-
AwsTelemetry.loginWithBrowser(project = null, result = Result.Succeeded, credentialType = CredentialType.SsoProfile)
42+
AwsTelemetry.loginWithBrowser(project = null, result = Result.Succeeded, credentialType = CredentialType.SsoProfile, authType = AuthType.DeviceCode)
4243
}
4344

4445
override fun tokenRetrievalFailure(e: Exception) {
4546
e.notifyError(AwsCoreBundle.message("credentials.sso.login.failed"))
46-
AwsTelemetry.loginWithBrowser(project = null, result = Result.Failed, credentialType = CredentialType.SsoProfile)
47+
AwsTelemetry.loginWithBrowser(project = null, result = Result.Failed, credentialType = CredentialType.SsoProfile, authType = AuthType.DeviceCode)
4748
}
4849
}
4950

@@ -59,7 +60,12 @@ object DefaultSsoPrompt : SsoPrompt {
5960
if (result) {
6061
BrowserUtil.browse(authorization.verificationUriComplete)
6162
} else {
62-
AwsTelemetry.loginWithBrowser(project = null, result = Result.Cancelled, credentialType = CredentialType.SsoProfile)
63+
AwsTelemetry.loginWithBrowser(
64+
project = null,
65+
result = Result.Cancelled,
66+
credentialType = CredentialType.SsoProfile,
67+
authType = AuthType.DeviceCode
68+
)
6369
throw ProcessCanceledException(IllegalStateException(AwsCoreBundle.message("credentials.sso.login.cancelled")))
6470
}
6571
}
@@ -76,11 +82,11 @@ object SsoPromptWithBrowserSupport : SsoPrompt {
7682

7783
interface BearerTokenPrompt : SsoLoginCallback {
7884
override fun tokenRetrieved() {
79-
AwsTelemetry.loginWithBrowser(project = null, result = Result.Succeeded, credentialType = CredentialType.BearerToken)
85+
AwsTelemetry.loginWithBrowser(project = null, result = Result.Succeeded, credentialType = CredentialType.BearerToken, authType = AuthType.DeviceCode)
8086
}
8187

8288
override fun tokenRetrievalFailure(e: Exception) {
83-
AwsTelemetry.loginWithBrowser(project = null, result = Result.Failed, credentialType = CredentialType.BearerToken)
89+
AwsTelemetry.loginWithBrowser(project = null, result = Result.Failed, credentialType = CredentialType.BearerToken, authType = AuthType.DeviceCode)
8490
}
8591
}
8692

@@ -96,7 +102,12 @@ object DefaultBearerTokenPrompt : BearerTokenPrompt {
96102
if (codeCopied) {
97103
BrowserUtil.browse(authorization.verificationUriComplete)
98104
} else {
99-
AwsTelemetry.loginWithBrowser(project = null, result = Result.Cancelled, credentialType = CredentialType.BearerToken)
105+
AwsTelemetry.loginWithBrowser(
106+
project = null,
107+
result = Result.Cancelled,
108+
credentialType = CredentialType.BearerToken,
109+
authType = AuthType.DeviceCode
110+
)
100111
}
101112
}
102113
}

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/credentials/sso/bearer/BearerTokenProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ interface BearerTokenProvider : SdkTokenProvider, SdkAutoCloseable, ToolkitBeare
8888

8989
class InteractiveBearerTokenProvider(
9090
val startUrl: String,
91-
region: String,
91+
val region: String,
9292
val scopes: List<String>,
9393
id: String,
9494
cache: DiskCache = diskCache

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/credentials/sso/bearer/ConfirmUserCodeLoginDialog.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.intellij.util.ui.JBFont
1919
import com.intellij.util.ui.components.BorderLayoutPanel
2020
import software.aws.toolkits.core.utils.tryOrNull
2121
import software.aws.toolkits.resources.AwsCoreBundle
22+
import software.aws.toolkits.telemetry.AuthType
2223
import software.aws.toolkits.telemetry.AwsTelemetry
2324
import software.aws.toolkits.telemetry.CredentialType
2425
import software.aws.toolkits.telemetry.Result
@@ -66,7 +67,7 @@ class ConfirmUserCodeLoginDialog(
6667

6768
override fun doCancelAction() {
6869
super.doCancelAction()
69-
AwsTelemetry.loginWithBrowser(project = null, result = Result.Cancelled, credentialType = credentialType)
70+
AwsTelemetry.loginWithBrowser(project = null, result = Result.Cancelled, credentialType = credentialType, authType = AuthType.DeviceCode)
7071
}
7172
}
7273

plugins/core/jetbrains-community/src/software/aws/toolkits/jetbrains/core/webview/LoginBrowser.kt

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import software.aws.toolkits.jetbrains.utils.pluginAwareExecuteOnPooledThread
4343
import software.aws.toolkits.jetbrains.utils.pollFor
4444
import software.aws.toolkits.resources.AwsCoreBundle
4545
import software.aws.toolkits.telemetry.AuthTelemetry
46+
import software.aws.toolkits.telemetry.AuthType
4647
import software.aws.toolkits.telemetry.AwsTelemetry
4748
import software.aws.toolkits.telemetry.CredentialSourceId
4849
import software.aws.toolkits.telemetry.CredentialType
@@ -85,22 +86,23 @@ abstract class LoginBrowser(
8586

8687
private var browserOpenTimer: Timer? = null
8788

88-
private fun startBrowserOpenTimer(credentialSourceId: CredentialSourceId) {
89+
private fun startBrowserOpenTimer(startUrl: String, ssoRegion: String) {
8990
browserOpenTimer = Timer()
9091
browserOpenTimer?.schedule(
9192
object : TimerTask() {
9293
override fun run() {
9394
AwsTelemetry.loginWithBrowser(
9495
project = null,
95-
credentialStartUrl = SONO_URL,
96+
credentialStartUrl = startUrl,
9697
result = Result.Failed,
9798
reason = "Browser authentication idle for more than 15min",
98-
credentialSourceId = credentialSourceId
99+
credentialSourceId = if (startUrl == SONO_URL) CredentialSourceId.AwsId else CredentialSourceId.IamIdentityCenter,
100+
authType = getAuthType(ssoRegion)
99101
)
100102
AuthTelemetry.addConnection(
101103
result = Result.Failed,
102104
reason = "Browser authentication idle for more than 15min",
103-
credentialSourceId = credentialSourceId
105+
credentialSourceId = if (startUrl == SONO_URL) CredentialSourceId.AwsId else CredentialSourceId.IamIdentityCenter
104106
)
105107
stopAndClearBrowserOpenTimer()
106108
}
@@ -118,7 +120,7 @@ abstract class LoginBrowser(
118120
}
119121

120122
protected val onPendingToken: (InteractiveBearerTokenProvider) -> Unit = { provider ->
121-
startBrowserOpenTimer(if (provider.startUrl == SONO_URL) CredentialSourceId.AwsId else CredentialSourceId.IamIdentityCenter)
123+
startBrowserOpenTimer(provider.startUrl, provider.region)
122124
projectCoroutineScope(project).launch {
123125
val authorization = pollForAuthorization(provider)
124126
if (authorization != null) {
@@ -186,7 +188,8 @@ abstract class LoginBrowser(
186188
result = Result.Failed,
187189
reason = e.message,
188190
credentialSourceId = CredentialSourceId.AwsId,
189-
isReAuth = isReauth
191+
isReAuth = isReauth,
192+
authType = getAuthType()
190193
)
191194
AuthTelemetry.addConnection(
192195
result = Result.Failed,
@@ -202,7 +205,8 @@ abstract class LoginBrowser(
202205
credentialStartUrl = SONO_URL,
203206
result = Result.Succeeded,
204207
credentialSourceId = CredentialSourceId.AwsId,
205-
isReAuth = isReauth
208+
isReAuth = isReauth,
209+
authType = getAuthType()
206210
)
207211
AuthTelemetry.addConnection(
208212
result = Result.Succeeded,
@@ -256,7 +260,8 @@ abstract class LoginBrowser(
256260
isReAuth = isReAuth,
257261
result = result,
258262
reason = message,
259-
credentialSourceId = CredentialSourceId.IamIdentityCenter
263+
credentialSourceId = CredentialSourceId.IamIdentityCenter,
264+
authType = getAuthType(region.name)
260265
)
261266
AuthTelemetry.addConnection(
262267
result = result,
@@ -273,7 +278,8 @@ abstract class LoginBrowser(
273278
isReAuth = isReAuth,
274279
credentialType = CredentialType.BearerToken,
275280
credentialStartUrl = url,
276-
credentialSourceId = CredentialSourceId.IamIdentityCenter
281+
credentialSourceId = CredentialSourceId.IamIdentityCenter,
282+
authType = getAuthType(region.name)
277283
)
278284
AuthTelemetry.addConnection(
279285
project = null,
@@ -296,7 +302,8 @@ abstract class LoginBrowser(
296302
project = null,
297303
result = Result.Failed,
298304
reason = error.message,
299-
credentialType = CredentialType.StaticProfile
305+
credentialType = CredentialType.StaticProfile,
306+
authType = AuthType.IAM
300307
)
301308
LOG.error(error) { "Profile file error" }
302309
Messages.showErrorDialog(jcefBrowser.component, error.message, AwsCoreBundle.message("gettingstarted.auth.failed"))
@@ -306,6 +313,7 @@ abstract class LoginBrowser(
306313
project = null,
307314
result = Result.Failed,
308315
reason = "Profile already exists",
316+
authType = AuthType.IAM
309317
)
310318
},
311319
{ error ->
@@ -314,6 +322,7 @@ abstract class LoginBrowser(
314322
project = null,
315323
result = Result.Failed,
316324
reason = reason,
325+
authType = AuthType.IAM
317326
)
318327
LOG.error(error) { reason }
319328
Messages.showErrorDialog(jcefBrowser.component, error.message, AwsCoreBundle.message("gettingstarted.auth.failed"))
@@ -325,7 +334,8 @@ abstract class LoginBrowser(
325334
AwsTelemetry.loginWithBrowser(
326335
project = null,
327336
result = Result.Succeeded,
328-
credentialType = CredentialType.StaticProfile
337+
credentialType = CredentialType.StaticProfile,
338+
authType = AuthType.IAM
329339
)
330340
}
331341
}

0 commit comments

Comments
 (0)