'KTOR Client and Spring Switchuser

I'm trying to implement a client for spring-security's SwitchUserFilter (server-side). As client I'm using KTOR (with OKHttp inside).

SwitchUserFilter requires me to log in, then drop the Authorization header and use the Cookie tinstead. If I send the Authorization header together with the Cookie header, spring's SecurityContext coming from SwitchUserFilter will be overwritten with my admin user again.

Is there something I can configure in KTOR, so that the [Authorization] header is removed, once I have switched the user?



Solution 1:[1]

KTOR has to be setup with two things:

  1. SwitchUserFilter will send a redirect (HTTP 302) that we need to ignore. For this a HttpResponseValidator needs to be configured.

  2. Auth needs to be removed similar to the comment from @Delta_George

    HttpClient(OkHttp) {
     HttpResponseValidator {
         // for 302 don't react - so we can switch user successfully. If we follow, this doesn't work anymore.
         validateResponse { response ->
             val statusCode = response.status.value
             val originCall = response.call
             if (statusCode < 300 || originCall.attributes.contains(ValidateMark)) {
                 return@validateResponse
             }
    
             val exceptionCall = originCall.save().apply {
                 attributes.put(ValidateMark, Unit)
             }
             val excResp = exceptionCall.response
             val excRespTxt = excResp.readText()
             when (statusCode) {
                 302 -> {} // do nothing on "Found" statuscode
                 in 300..399 -> throw RedirectResponseException(excResp, excRespTxt)
                 in 400..499 -> throw ClientRequestException(excResp, excRespTxt)
                 in 500..599 -> throw ServerResponseException(excResp, excRespTxt)
                 else -> throw ResponseException(excResp, excRespTxt)
             }
         }
     }
     ... // other configurations
    }
    

and impersonate(...):

    suspend fun impersonate(impersonateWithUser: PersonEntity): Impersonation<PersonEntity> {
        return runCatching {
                val toImpersonate = impersonateWithUser.login.replace(Regex("^\\+"), "%2B")
                client.get<HttpResponse>("$BASE_URL/login/impersonate?username=${toImpersonate}") // with baseauth again
            }.map {
                when (it.status) {
                    HttpStatusCode.Found -> {
                        client.feature(Auth)!!.providers.removeAll { true }
                        Impersonation.ok(impersonateWithUser)
                    }
                    else -> Impersonation.failure(impersonateWithUser, it)
                }
            }.getOrElse {
                Log.e(TAG, "impersonate: ", it)
                Impersonation.communicationError(impersonateWithUser, it)
            }
    }

to end the impersonation you call the respective endpoint given in SwitchUserFilter on the serverside.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Frischling