v0.14 is out: a mobile auth pack for Swift/iOS and Android, catching insecure token storage, cleartext traffic, and OAuth in embedded WebViews. Read more →
HIGH AI PREVALENCE: MEDIUM auth.kotlin.jwt.algorithm-none

A JWT signer or verifier is built with Algorithm.none(), the unsecured algorithm that produces (and accepts) tokens with no signature.

Why AI tools produce this: AI coding tools produce this regularly, typically when prompted for a shortcut or a quick fix.

Why this matters

An alg=none token can be forged by anyone. Changing the subject, roles, or expiry costs nothing because there is no signature to verify (CWE-347). This is a common AI-generated mistake: the "no signature" algorithm is reached for during prototyping or a Ktor demo and never swapped for a real key.

Sign and verify with a real algorithm and a key from configuration: Algorithm.HMAC256(System.getenv("JWT_SECRET")) or an RSA/EC key, e.g. JWT.require(Algorithm.HMAC256(secret)).build().verify(token).

VULNERABLE
vulnerable.kt
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm

fun createToken(): String {
    // ruleid: auth.kotlin.jwt.algorithm-none
    val algorithm = Algorithm.none()
    return JWT.create()
        .withSubject("user-42")
        .sign(algorithm)
}

fun buildVerifier(token: String) {
    // ruleid: auth.kotlin.jwt.algorithm-none
    val verifier = JWT.require(Algorithm.none()).build()
    verifier.verify(token)
}
SAFE
safe.kt
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm

fun createToken(): String {
    val algorithm = Algorithm.HMAC256(System.getenv("JWT_SECRET"))
    return JWT.create()
        .withSubject("user-42")
        .sign(algorithm)
}

fun buildVerifier(token: String) {
    val algorithm = Algorithm.HMAC256(System.getenv("JWT_SECRET"))
    val verifier = JWT.require(algorithm)
        .withIssuer("https://idp.example.com")
        .withAudience("my-api")
        .build()
    verifier.verify(token)
}

Suppressing this rule

If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.

// oauthlint-disable-next-line auth.kotlin.jwt.algorithm-none -- <reason>

References

https://github.com/auth0/java-jwt ↗https://datatracker.ietf.org/doc/html/rfc8725#section-2.1 ↗https://cwe.mitre.org/data/definitions/347.html ↗