Skip to content

Commit 0e46da0

Browse files
committed
impl: simple way of triggering the OAuth flow.
A bunch of code thrown around to launch the OAuth flow. Still needs a couple of things: - persist the client id and registration uri and token - re-use client id instead of re-register every time - properly handle scenarios where OAuth is not available - the OAuth right now can be enabled if we log out and then hit next in the deployment screen
1 parent 6462f14 commit 0e46da0

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

src/main/kotlin/com/coder/toolbox/views/DeploymentUrlStep.kt

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
package com.coder.toolbox.views
22

33
import com.coder.toolbox.CoderToolboxContext
4+
import com.coder.toolbox.browser.browse
5+
import com.coder.toolbox.oauth.ClientRegistrationRequest
6+
import com.coder.toolbox.oauth.CoderAuthorizationApi
7+
import com.coder.toolbox.oauth.CoderOAuthCfg
8+
import com.coder.toolbox.plugin.PluginManager
9+
import com.coder.toolbox.sdk.CoderHttpClientBuilder
10+
import com.coder.toolbox.sdk.convertors.LoggingConverterFactory
11+
import com.coder.toolbox.sdk.interceptors.Interceptors
412
import com.coder.toolbox.util.WebUrlValidationResult.Invalid
513
import com.coder.toolbox.util.toURL
614
import com.coder.toolbox.util.validateStrictWebUrl
@@ -14,8 +22,12 @@ import com.jetbrains.toolbox.api.ui.components.RowGroup
1422
import com.jetbrains.toolbox.api.ui.components.TextField
1523
import com.jetbrains.toolbox.api.ui.components.TextType
1624
import com.jetbrains.toolbox.api.ui.components.ValidationErrorField
25+
import com.squareup.moshi.Moshi
1726
import kotlinx.coroutines.flow.StateFlow
1827
import kotlinx.coroutines.flow.update
28+
import kotlinx.coroutines.launch
29+
import retrofit2.Retrofit
30+
import retrofit2.converter.moshi.MoshiConverterFactory
1931
import java.net.MalformedURLException
2032
import java.net.URL
2133

@@ -42,6 +54,16 @@ class DeploymentUrlStep(
4254

4355
private val errorField = ValidationErrorField(context.i18n.pnotr(""))
4456

57+
val interceptors = buildList {
58+
add((Interceptors.userAgent(PluginManager.pluginInfo.version)))
59+
add(Interceptors.logging(context))
60+
}
61+
val okHttpClient = CoderHttpClientBuilder.build(
62+
context,
63+
interceptors
64+
)
65+
66+
4567
override val panel: RowGroup
4668
get() {
4769
if (!context.settingsStore.disableSignatureVerification) {
@@ -86,6 +108,61 @@ class DeploymentUrlStep(
86108
errorReporter.report("URL is invalid", e)
87109
return false
88110
}
111+
val service = Retrofit.Builder()
112+
.baseUrl(CoderCliSetupContext.url!!)
113+
.client(okHttpClient)
114+
.addConverterFactory(
115+
LoggingConverterFactory.wrap(
116+
context,
117+
MoshiConverterFactory.create(Moshi.Builder().build())
118+
)
119+
)
120+
.build()
121+
.create(CoderAuthorizationApi::class.java)
122+
context.cs.launch {
123+
context.logger.info(">> checking if Coder supports OAuth2")
124+
val response = service.discoveryMetadata()
125+
if (response.isSuccessful) {
126+
val authServer = requireNotNull(response.body()) {
127+
"Successful response returned null body or oauth server discovery metadata"
128+
}
129+
context.logger.info(">> registering coder-jetbrains-toolbox as client app $response")
130+
val clientResponse = service.registerClient(
131+
ClientRegistrationRequest(
132+
clientName = "coder-jetbrains-toolbox",
133+
redirectUris = listOf("jetbrains://gateway/com.coder.toolbox/auth"),//URLEncoder.encode("jetbrains://gateway/com.coder.toolbox/oauth", StandardCharsets.UTF_8.toString())),
134+
grantTypes = listOf("authorization_code", "refresh_token"),
135+
responseTypes = authServer.supportedResponseTypes,
136+
scope = "coder:workspaces.operate coder:workspaces.delete coder:workspaces.access user:read",
137+
tokenEndpointAuthMethod = "client_secret_post"
138+
)
139+
)
140+
if (clientResponse.isSuccessful) {
141+
val clientResponse =
142+
requireNotNull(clientResponse.body()) { "Successful response returned null body or client registration metadata" }
143+
context.logger.info(">> initiating oauth login with $clientResponse")
144+
145+
val oauthCfg = CoderOAuthCfg(
146+
baseUrl = CoderCliSetupContext.url!!.toString(),
147+
authUrl = authServer.authorizationEndpoint,
148+
tokenUrl = authServer.tokenEndpoint,
149+
clientId = clientResponse.clientId,
150+
clientSecret = clientResponse.clientSecret,
151+
)
152+
153+
val loginUrl = context.oauthManager.initiateLogin(oauthCfg)
154+
context.logger.info(">> retrieving token")
155+
context.desktop.browse(loginUrl) {
156+
context.ui.showErrorInfoPopup(it)
157+
}
158+
val token = context.oauthManager.getToken("coder", forceRefresh = false)
159+
context.logger.info(">> token is $token")
160+
} else {
161+
context.logger.error(">> ${clientResponse.code()} ${clientResponse.message()} || ${clientResponse.errorBody()}")
162+
}
163+
}
164+
}
165+
89166
if (context.settingsStore.requireTokenAuth) {
90167
CoderCliSetupWizardState.goToNextStep()
91168
} else {

0 commit comments

Comments
 (0)