Skip to content

Commit

Permalink
KTOR-1464 Fix client Digest auth realm handling
Browse files Browse the repository at this point in the history
Fix client digest auth realm handling to use the realm provided by the server if the realm is not configured in the client feature
  • Loading branch information
Sergey Mashkov committed Apr 29, 2021
1 parent 6763a73 commit 82ed9bd
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class Auth(

val request = HttpRequestBuilder()
request.takeFromWithExecutionContext(context)
request.attributes.put(AuthHeaderAttribute, authHeader)
provider.addRequestHeaders(request)
request.attributes.put(circuitBreaker, Unit)

Expand All @@ -67,3 +68,11 @@ public class Auth(
public fun HttpClientConfig<*>.Auth(block: Auth.() -> Unit) {
install(Auth, block)
}

/**
* AuthHeader from the previous unsuccessful request. This actually should be passed as
* parameter to AuthProvider.addRequestHeaders instead in the future and the attribute will
* be removed after that.
*/
@PublicAPICandidate("1.6.0")
internal val AuthHeaderAttribute = AttributeKey<HttpAuthHeader>("AuthHeader")
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,18 @@ public class DigestAuthProvider(

override fun isApplicable(auth: HttpAuthHeader): Boolean {
if (auth !is HttpAuthHeader.Parameterized ||
auth.parameter("realm") != realm ||
auth.authScheme != AuthScheme.Digest
) return false

val newNonce = auth.parameter("nonce") ?: return false
val newQop = auth.parameter("qop")
val newOpaque = auth.parameter("opaque")

val newRealm = auth.parameter("realm") ?: return false
if (newRealm != realm && realm != null) {
return false
}

serverNonce.value = newNonce
qop.value = newQop
opaque.value = newOpaque
Expand Down Expand Up @@ -90,6 +94,9 @@ public class DigestAuthProvider(
}

val token = makeDigest(tokenSequence.joinToString(":"))
val realm = realm ?: request.attributes.getOrNull(AuthHeaderAttribute)?.let { auth ->
(auth as? HttpAuthHeader.Parameterized)?.parameter("realm")
}

val auth = HttpAuthHeader.Parameterized(
AuthScheme.Digest,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,28 @@ class DigestProviderTest {
checkStandardFields(authHeader)
}

@Test
fun addRequestHeadersMissingRealm() = testSuspend {
val providerWithoutRealm = DigestAuthProvider("username", "pass", null)
val authHeader = parseAuthorizationHeader(authAllFields)!!
requestBuilder.attributes.put(AuthHeaderAttribute, authHeader)

assertTrue(providerWithoutRealm.isApplicable(authHeader))
providerWithoutRealm.addRequestHeaders(requestBuilder)

val resultAuthHeader = requestBuilder.headers[HttpHeaders.Authorization]!!
checkStandardFields(resultAuthHeader)
}

@Test
fun addRequestHeadersChangedRealm() = testSuspend {
val providerWithoutRealm = DigestAuthProvider("username", "pass", "wrong!")
val authHeader = parseAuthorizationHeader(authAllFields)!!
requestBuilder.attributes.put(AuthHeaderAttribute, authHeader)

assertFalse(providerWithoutRealm.isApplicable(authHeader))
}

@Test
fun addRequestHeadersOmitsQopAndOpaqueWhenMissing() = testSuspend {
if (!PlatformUtils.IS_JVM) return@testSuspend
Expand Down

0 comments on commit 82ed9bd

Please sign in to comment.