Repository: ktorio/ktor
File: ktor-server/ktor-server-plugins/ktor-server-auth-jwt/jvm/src/io/ktor/server/auth/jwt/JWTUtils.kt (Kotlin)

Code before:
import io.ktor.http.auth.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import java.security.interfaces.*
import java.util.*

internal fun Jwk.makeAlgorithm(): Algorithm = when (algorithm) {
    "RS256" -> Algorithm.RSA256(publicKey as RSAPublicKey, null)
    "RS384" -> Algorithm.RSA384(publicKey as RSAPublicKey, null)
    "RS512" -> Algorithm.RSA512(publicKey as RSAPublicKey, null)
    "ES256" -> Algorithm.ECDSA256(publicKey as ECPublicKey, null)
    "ES384" -> Algorithm.ECDSA384(publicKey as ECPublicKey, null)
    "ES512" -> Algorithm.ECDSA512(publicKey as ECPublicKey, null)
    null -> when (type) {
        "EC" -> Algorithm.ECDSA256(publicKey as ECPublicKey, null)
        else -> Algorithm.RSA256(publicKey as RSAPublicKey, null)
    }
    else -> throw IllegalArgumentException("Unsupported algorithm $algorithm")
}

internal fun AuthenticationContext.bearerChallenge(
    cause: AuthenticationFailedCause,
    realm: String,
    schemes: JWTAuthSchemes,
    challengeFunction: JWTAuthChallengeFunction
) {
    challenge(JWTAuthKey, cause) { challenge, call ->
        challengeFunction(JWTChallengeContext(call), schemes.defaultScheme, realm)
        if (!challenge.completed && call.response.status() != null) {
            challenge.complete()
        }
    }
}

internal fun getVerifier(
    jwkProvider: JwkProvider,
    issuer: String?,
    token: HttpAuthHeader,
    schemes: JWTAuthSchemes,
    jwtConfigure: Verification.() -> Unit
): JWTVerifier? {
    val jwk = token.getBlob(schemes)?.let { blob ->
        try {
            jwkProvider.get(JWT.decode(blob).keyId)
        } catch (cause: JwkException) {
            JWTLogger.trace("Failed to get JWK", cause)
            null
        } catch (cause: JWTDecodeException) {
            JWTLogger.trace("Illegal JWT", cause)
            null

Review comment (suggestion):
When algorithm is null and type is "EC", the code defaults to ECDSA256 regardless of the actual EC key curve size. While this is a reasonable default (P-256 is the most common), consider enhancing this in the future to detect the curve from the ECPublicKey parameters:
- P-256 (secp256r1) → ECDSA256
- P-384 (secp384r1) → ECDSA384
- P-521 (secp521r1) → ECDSA512

This would make the implementation more robust when the algorithm field is missing but wouldn't be needed for well-formed JWKs that include the algorithm. This is an optional enhancement, not a blocking issue.

Code after:
import io.ktor.http.auth.*
import io.ktor.server.application.*
import io.ktor.server.auth.*
import io.ktor.server.request.*
import java.security.interfaces.*
import java.util.*

internal fun Jwk.makeAlgorithm(): Algorithm = when (algorithm) {
    "RS256" -> Algorithm.RSA256(publicKey as RSAPublicKey, null)
    "RS384" -> Algorithm.RSA384(publicKey as RSAPublicKey, null)
    "RS512" -> Algorithm.RSA512(publicKey as RSAPublicKey, null)
    "ES256" -> Algorithm.ECDSA256(publicKey as ECPublicKey, null)
    "ES384" -> Algorithm.ECDSA384(publicKey as ECPublicKey, null)
    "ES512" -> Algorithm.ECDSA512(publicKey as ECPublicKey, null)
    null -> when (type) {
        "EC" -> {
            val ecKey = publicKey as ECPublicKey
            when (ecKey.params.order.bitLength()) {
                in 249..258 -> Algorithm.ECDSA256(ecKey, null)
                in 379..388 -> Algorithm.ECDSA384(ecKey, null)
                in 518..527 -> Algorithm.ECDSA512(ecKey, null)
                else -> Algorithm.ECDSA256(ecKey, null)
            }
        }
        else -> Algorithm.RSA256(publicKey as RSAPublicKey, null)
    }
    else -> throw IllegalArgumentException("Unsupported algorithm $algorithm")
}

internal fun AuthenticationContext.bearerChallenge(
    cause: AuthenticationFailedCause,
    realm: String,
    schemes: JWTAuthSchemes,
    challengeFunction: JWTAuthChallengeFunction
) {
    challenge(JWTAuthKey, cause) { challenge, call ->
        challengeFunction(JWTChallengeContext(call), schemes.defaultScheme, realm)
        if (!challenge.completed && call.response.status() != null) {
            challenge.complete()
        }
    }
}

internal fun getVerifier(
    jwkProvider: JwkProvider,
    issuer: String?,
    token: HttpAuthHeader,
    schemes: JWTAuthSchemes,
    jwtConfigure: Verification.() -> Unit
): JWTVerifier? {
    val jwk = token.getBlob(schemes)?.let { blob ->