Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1a3415b

Browse files
committed
impl: setup auth manager with auth and token endpoints
Toolbox API comes with a basic oauth2 client. This commitsets-up details about two important oauth flows:- authorization flow, in which the user is sent to web page where an authorization code is generated which is exchanged for an access token.- details about token refresh endpoint where users can obtain a new access token and a new refresh token.A couple of important aspects:- the client app id is resolved in upstream- as well as the actual endpoints for authorization and token refresh- S256 is the only code challenge supported
1 parent8bfee5e commit1a3415b

File tree

5 files changed

+130
-0
lines changed

5 files changed

+130
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
packagecom.coder.toolbox.oauth
2+
3+
importcom.squareup.moshi.Json
4+
importcom.squareup.moshi.JsonClass
5+
6+
@JsonClass(generateAdapter=true)
7+
data classAuthorizationServer(
8+
@field:Json(name = "authorization_endpoint")valauthorizationEndpoint:String,
9+
@field:Json(name = "token_endpoint")valtokenEndpoint:String,
10+
@property:Json(name = "token_endpoint_auth_methods_supported")valauthMethodForTokenEndpoint:List<TokenEndpointAuthMethod>,
11+
)
12+
13+
enumclassTokenEndpointAuthMethod {
14+
@Json(name="none")
15+
NONE,
16+
17+
@Json(name="client_secret_post")
18+
CLIENT_SECRET_POST,
19+
20+
@Json(name="client_secret_basic")
21+
CLIENT_SECRET_BASIC,
22+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
packagecom.coder.toolbox.oauth
2+
3+
importcom.jetbrains.toolbox.api.core.auth.Account
4+
5+
data classCoderAccount(overridevalid:String,overridevalfullName:String) : Account
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
packagecom.coder.toolbox.oauth
2+
3+
importcom.coder.toolbox.util.toBaseURL
4+
importcom.jetbrains.toolbox.api.core.auth.AuthConfiguration
5+
importcom.jetbrains.toolbox.api.core.auth.ContentType
6+
importcom.jetbrains.toolbox.api.core.auth.ContentType.FORM_URL_ENCODED
7+
importcom.jetbrains.toolbox.api.core.auth.OAuthToken
8+
importcom.jetbrains.toolbox.api.core.auth.PluginAuthInterface
9+
importcom.jetbrains.toolbox.api.core.auth.RefreshConfiguration
10+
11+
classCoderOAuthManager(
12+
privatevalclientId:String,
13+
privatevalauthServer:AuthorizationServer
14+
) : PluginAuthInterface<CoderAccount, CoderLoginCfg> {
15+
overridefunserialize(account:CoderAccount):String="${account.id}|${account.fullName}"
16+
17+
overridefundeserialize(string:String):CoderAccount=CoderAccount(
18+
string.split('|')[0],
19+
string.split('|')[1]
20+
)
21+
22+
overridesuspendfuncreateAccount(
23+
token:OAuthToken,
24+
config:AuthConfiguration
25+
):CoderAccount {
26+
TODO("Not yet implemented")
27+
}
28+
29+
overridesuspendfunupdateAccount(
30+
token:OAuthToken,
31+
account:CoderAccount
32+
):CoderAccount {
33+
TODO("Not yet implemented")
34+
}
35+
36+
overridefuncreateAuthConfig(loginConfiguration:CoderLoginCfg):AuthConfiguration=AuthConfiguration(
37+
authParams=mapOf("response_type" to"code","client_id" to clientId),
38+
tokenParams=mapOf("grant_type" to"authorization_code","client_id" to clientId),
39+
baseUrl= authServer.authorizationEndpoint.toBaseURL().toString(),
40+
authUrl= authServer.authorizationEndpoint,
41+
tokenUrl= authServer.tokenEndpoint,
42+
codeChallengeParamName="code_challenge",
43+
codeChallengeMethod="S256",
44+
verifierParamName="code_verifier",
45+
authorization=null
46+
)
47+
48+
49+
overridefuncreateRefreshConfig(account:CoderAccount):RefreshConfiguration {
50+
returnobject:RefreshConfiguration {
51+
overrideval refreshUrl:String= authServer.tokenEndpoint
52+
overrideval parameters:Map<String,String>=
53+
mapOf("grant_type" to"refresh_token","client_id" to clientId)
54+
overrideval authorization:String?=null
55+
overrideval contentType:ContentType=FORM_URL_ENCODED
56+
}
57+
}
58+
}
59+
60+
object CoderLoginCfg

‎src/main/kotlin/com/coder/toolbox/util/URLExtensions.kt‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ import java.net.URL
88

99
fun String.toURL():URL=URI.create(this).toURL()
1010

11+
fun String.toBaseURL():URL {
12+
val url=this.toURL()
13+
val port=if (url.port!=-1)":${url.port}"else""
14+
returnURI.create("${url.protocol}://${url.host}$port").toURL()
15+
}
16+
1117
fun String.validateStrictWebUrl():WebUrlValidationResult=try {
1218
val uri=URI(this)
1319

@@ -21,15 +27,18 @@ fun String.validateStrictWebUrl(): WebUrlValidationResult = try {
2127
"The URL\"$this\" is missing a scheme (like https://)."+
2228
"Please enter a full web address like\"https://example.com\""
2329
)
30+
2431
uri.scheme?.lowercase()!insetOf("http","https")->
2532
Invalid(
2633
"The URL\"$this\" must start with http:// or https://, not\"${uri.scheme}\""
2734
)
35+
2836
uri.authority.isNullOrBlank()->
2937
Invalid(
3038
"The URL\"$this\" does not include a valid website name."+
3139
"Please enter a full web address like\"https://example.com\""
3240
)
41+
3342
else->Valid
3443
}
3544
}catch (_:Exception) {

‎src/test/kotlin/com/coder/toolbox/util/URLExtensionsTest.kt‎

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import java.net.URI
44
importjava.net.URL
55
importkotlin.test.Test
66
importkotlin.test.assertEquals
7+
importkotlin.test.assertFailsWith
78

89
internalclassURLExtensionsTest {
910
@Test
@@ -152,4 +153,37 @@ internal class URLExtensionsTest {
152153
result
153154
)
154155
}
156+
157+
@Test
158+
fun`returns base URL without path or query`() {
159+
val fullUrl="https://example.com/path/to/page?param=1"
160+
val result= fullUrl.toBaseURL()
161+
assertEquals(URL("https://example.com"), result)
162+
}
163+
164+
@Test
165+
fun`includes port if specified`() {
166+
val fullUrl="https://example.com:8080/api/v1/resource"
167+
val result= fullUrl.toBaseURL()
168+
assertEquals(URL("https://example.com:8080"), result)
169+
}
170+
171+
@Test
172+
fun`handles subdomains correctly`() {
173+
val fullUrl="http://api.subdomain.example.org/v2/users"
174+
val result= fullUrl.toBaseURL()
175+
assertEquals(URL("http://api.subdomain.example.org"), result)
176+
}
177+
178+
@Test
179+
fun`handles simple domain without path`() {
180+
val fullUrl="https://test.com"
181+
val result= fullUrl.toBaseURL()
182+
assertEquals(URL("https://test.com"), result)
183+
}
184+
185+
@Test
186+
fun`throws exception for invalid URL`() {
187+
assertFailsWith<IllegalArgumentException> {"ht!tp://bad_url".toBaseURL() }
188+
}
155189
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp