Repository: ktorio/ktor
File: ktor-client/ktor-client-plugins/ktor-client-auth/common/src/io/ktor/client/plugins/auth/Auth.kt (Kotlin)

Code before:

internal val LOGGER = KtorSimpleLogger("io.ktor.client.plugins.auth.Auth")

private class AtomicCounter {
    val atomic = atomic(0)
}

@KtorDsl
public class AuthConfig {
    public val providers: MutableList<AuthProvider> = mutableListOf()
    public var isUnauthorized: (HttpResponse) -> Boolean = { it.status == HttpStatusCode.Unauthorized }
}

/**
 * Shows that request should skip auth and refresh token procedure.
 */
public val AuthCircuitBreaker: AttributeKey<Unit> = AttributeKey("auth-request")

/**
 * A client's plugin that handles authentication and authorization.
 * Typical usage scenarios include logging in users and gaining access to specific resources.
 *
 * You can learn more from [Authentication and authorization](https://ktor.io/docs/auth.html).
 *
 * [providers] - list of auth providers to use.
 * [isUnauthorized] - lambda function to control whether a response is unauthorized and should trigger a re-auth.
 */
public val Auth: ClientPlugin<AuthConfig> = createClientPlugin("Auth", ::AuthConfig) {
    val providers = pluginConfig.providers.toList()

    client.attributes.put(AuthProvidersKey, providers)

    val tokenVersions = ConcurrentMap<AuthProvider, AtomicCounter>()
    val tokenVersionsAttributeKey =
        AttributeKey<MutableMap<AuthProvider, Int>>("ProviderVersionAttributeKey")

    @OptIn(InternalAPI::class)
    fun findProvider(
        call: HttpClientCall,
        candidateProviders: Set<AuthProvider>
    ): Pair<AuthProvider, HttpAuthHeader?>? {
        val headerValues = call.response.headers.getAll(HttpHeaders.WWWAuthenticate)
        val authHeaders = headerValues?.map { parseAuthorizationHeaders(it) }?.flatten() ?: emptyList()

        return when {
            authHeaders.isEmpty() && candidateProviders.size == 1 -> {
                candidateProviders.first() to null
            }

            authHeaders.isEmpty() -> {
                LOGGER.trace {

Review comment (security):
Something else: this refers to variables that are actually defined on AuthConfig. Shouldn't the docstring be defined there instead or at least refer to the full path `[AuthConfig.providers]`? I just copied the existing code, but maybe this should be fixed, too.

Code after:
internal val LOGGER = KtorSimpleLogger("io.ktor.client.plugins.auth.Auth")

private class AtomicCounter {
    val atomic = atomic(0)
}

/**
 * Configuration used by [Auth] plugin.
 */
@KtorDsl
public class AuthConfig {
    /**
     * [AuthProvider] list to use.
     */
    public val providers: MutableList<AuthProvider> = mutableListOf()

    /**
     * The currently set function to control whether a response is unauthorized and should trigger a refresh / re-auth.
     *
     * By default checks against HTTP status 401.
     *
     * You can set this value via [reAuthorizeOnResponse].
     */
    @InternalAPI
    public var isUnauthorizedResponse: suspend (HttpResponse) -> Boolean = { it.status == HttpStatusCode.Unauthorized }
        private set

    /**
     * Sets a custom function to control whether a response is unauthorized and should trigger a refresh / re-auth.
     *
     * Use this to change the value of [isUnauthorizedResponse].
     */
    public fun reAuthorizeOnResponse(block: suspend (HttpResponse) -> Boolean) {
        @OptIn(InternalAPI::class)
        isUnauthorizedResponse = block
    }
}

/**
 * Shows that request should skip auth and refresh token procedure.
 */
public val AuthCircuitBreaker: AttributeKey<Unit> = AttributeKey("auth-request")

/**
 * A client's plugin that handles authentication and authorization.
 * Typical usage scenarios include logging in users and gaining access to specific resources.
 *
 * You can learn more from [Authentication and authorization](https://ktor.io/docs/auth.html).
 *
 * @see [AuthConfig] for configuration options.
 */