Skip to content

Commit e7a2450

Browse files
committed
Replace homeserver authorization approach with an Authorization header instead of access_token when talking to the application service, as per [MSC2832](matrix-org/matrix-spec-proposals#2832).
1 parent d02c263 commit e7a2450

File tree

4 files changed

+108
-56
lines changed

4 files changed

+108
-56
lines changed

trixnity-applicationserviceapi/trixnity-applicationserviceapi-server/src/commonMain/kotlin/net/folivo/trixnity/applicationserviceapi/server/MatrixQueryParameterAuth.kt

-50
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package net.folivo.trixnity.applicationserviceapi.server
2+
3+
import io.ktor.http.*
4+
import io.ktor.http.auth.*
5+
import io.ktor.server.auth.*
6+
import io.ktor.server.request.*
7+
import io.ktor.server.response.*
8+
import net.folivo.trixnity.core.ErrorResponse
9+
10+
class MatrixQueryParameterOrBearerAuthenticationProvider internal constructor(
11+
configuration: Configuration,
12+
private val field: String,
13+
private val token: String
14+
) : AuthenticationProvider(configuration) {
15+
class Configuration internal constructor(name: String? = null) : Config(name)
16+
17+
override suspend fun onAuthenticate(context: AuthenticationContext) {
18+
val queryCredentials = context.call.request.queryParameters[field]
19+
val accessTokenCredentials = context.call.request.getAccessTokenFromHeader()
20+
val cause = when {
21+
queryCredentials == null && accessTokenCredentials == null -> AuthenticationFailedCause.NoCredentials
22+
accessTokenCredentials != null && queryCredentials != null && accessTokenCredentials != queryCredentials
23+
|| (accessTokenCredentials == null && queryCredentials != token)
24+
|| (queryCredentials == null && accessTokenCredentials != token)
25+
|| (accessTokenCredentials == queryCredentials && queryCredentials != token) -> AuthenticationFailedCause.InvalidCredentials
26+
27+
else -> null
28+
}
29+
30+
if (cause != null) {
31+
context.challenge("MatrixQueryParameterAuth", cause) { challenge, call ->
32+
when (cause) {
33+
AuthenticationFailedCause.NoCredentials ->
34+
call.respond<ErrorResponse>(HttpStatusCode.Unauthorized, ErrorResponse.Unauthorized())
35+
36+
else -> call.respond<ErrorResponse>(HttpStatusCode.Forbidden, ErrorResponse.Forbidden())
37+
}
38+
challenge.complete()
39+
}
40+
} else {
41+
context.principal(UserIdPrincipal("homeserver"))
42+
}
43+
}
44+
}
45+
46+
private fun ApplicationRequest.getAccessTokenFromHeader(): String? {
47+
return when (val authHeader = parseAuthorizationHeader()) {
48+
is HttpAuthHeader.Single -> {
49+
if (!authHeader.authScheme.equals("Bearer", ignoreCase = true)) null
50+
else authHeader.blob
51+
}
52+
53+
else -> null
54+
}
55+
}
56+
57+
fun AuthenticationConfig.matrixQueryParameterOrBearer(
58+
name: String? = null,
59+
field: String,
60+
token: String,
61+
) {
62+
val provider =
63+
MatrixQueryParameterOrBearerAuthenticationProvider(
64+
MatrixQueryParameterOrBearerAuthenticationProvider.Configuration(name),
65+
field,
66+
token
67+
)
68+
register(provider)
69+
}

trixnity-applicationserviceapi/trixnity-applicationserviceapi-server/src/commonMain/kotlin/net/folivo/trixnity/applicationserviceapi/server/matrixApplicationServiceApiServer.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ fun Application.matrixApplicationServiceApiServer(
1616
routes: Route.() -> Unit,
1717
) {
1818
install(Authentication) {
19-
matrixQueryParameter("matrix-query-parameter-auth", "access_token", hsToken)
19+
matrixQueryParameterOrBearer("matrix-query-parameter-auth", "access_token", hsToken)
2020
}
2121
matrixApiServer(json) {
2222
authenticate("matrix-query-parameter-auth") {
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ private fun Application.testAppMatrixQueryParameterAuth() {
2323
json()
2424
}
2525
install(Authentication) {
26-
matrixQueryParameter(null, "access_token", "validToken")
26+
matrixQueryParameterOrBearer(null, "access_token", "validToken")
2727
}
2828
routing {
2929
authenticate {
@@ -34,10 +34,10 @@ private fun Application.testAppMatrixQueryParameterAuth() {
3434
}
3535
}
3636

37-
class MatrixQueryParameterAuthTest {
37+
class MatrixQueryParameterOrBearerAuthTest {
3838

3939
@Test
40-
fun `should forbid missing token`() = testApplication {
40+
fun `should forbid missing query or auth token`() = testApplication {
4141
application { testAppMatrixQueryParameterAuth() }
4242
val response = client.get("/_matrix/something")
4343
assertEquals(HttpStatusCode.Unauthorized, response.status)
@@ -47,7 +47,7 @@ class MatrixQueryParameterAuthTest {
4747
}
4848

4949
@Test
50-
fun `should forbid wrong token`() = testApplication {
50+
fun `should forbid wrong query token`() = testApplication {
5151
application { testAppMatrixQueryParameterAuth() }
5252
val response = client.get("/_matrix/something?access_token=invalidToken")
5353
assertEquals(HttpStatusCode.Forbidden, response.status)
@@ -57,9 +57,42 @@ class MatrixQueryParameterAuthTest {
5757
}
5858

5959
@Test
60-
fun `should permit right token`() = testApplication {
60+
fun `should forbid wrong header token`() = testApplication {
61+
application { testAppMatrixQueryParameterAuth() }
62+
val response = client.get("/_matrix/something") {
63+
bearerAuth("invalidToken")
64+
}
65+
assertEquals(HttpStatusCode.Forbidden, response.status)
66+
assertEquals(ContentType.Application.Json.withCharset(UTF_8), response.contentType())
67+
Json.decodeFromString(ErrorResponseSerializer, response.body())
68+
.shouldBeInstanceOf<ErrorResponse.Forbidden>()
69+
}
70+
71+
@Test
72+
fun `should forbid non matching token`() = testApplication {
73+
application { testAppMatrixQueryParameterAuth() }
74+
val response = client.get("/_matrix/something?access_token=validToken") {
75+
bearerAuth("invalidToken")
76+
}
77+
assertEquals(HttpStatusCode.Forbidden, response.status)
78+
assertEquals(ContentType.Application.Json.withCharset(UTF_8), response.contentType())
79+
Json.decodeFromString(ErrorResponseSerializer, response.body())
80+
.shouldBeInstanceOf<ErrorResponse.Forbidden>()
81+
}
82+
83+
@Test
84+
fun `should permit right query token`() = testApplication {
6185
application { testAppMatrixQueryParameterAuth() }
6286
val response = client.get("/_matrix/something?access_token=validToken")
6387
assertEquals(HttpStatusCode.OK, response.status)
6488
}
89+
90+
@Test
91+
fun `should permit right header token`() = testApplication {
92+
application { testAppMatrixQueryParameterAuth() }
93+
val response = client.get("/_matrix/something") {
94+
bearerAuth("validToken")
95+
}
96+
assertEquals(HttpStatusCode.OK, response.status)
97+
}
6598
}

0 commit comments

Comments
 (0)