How to customize jwt parsing in Quarkus?

2.1k Views Asked by At

I'm using smallrye.jwt as authorization tool. My quarkus app does not generate jwt tokens, but verifies them having secret key.

Problem is that incoming tokens have sub claim of non-string type, but parser expects java.lang.String (I receive 45 instead of "45"). I don't have access to token generation, so I need it to work with what I have. Apparently there's no way to make it work with microprofile. How can I achieve it?

The error I get (I replaced a few values with ...):

Caused by: org.jose4j.jwt.consumer.InvalidJwtException: JWT (claims->{"iss":"...","iat":...,"exp":...,"nbf":...,"jti":"...","sub":45,"prv":"...","pid": ...}) rejected due to invalid claims or other invalid content. Additional details: [[18] The value of the 'sub' claim is not the expected type (1517 - Cannot cast java.lang.Long to java.lang.String)]

My application.properties:

smallrye.jwt.verify.key-format=JWK
smallrye.jwt.verify.key.location=JWTSecret.jwk
smallrye.jwt.verify.algorithm=HS256
2

There are 2 best solutions below

1
Sergey Beryozkin On

Looks like it is coming directly from Jose4j, sub is a standard JWT claim, https://www.rfc-editor.org/rfc/rfc7519#section-4.1.2, and looks like Jose4J is expecting it to be a string as per the RFC7519 text

6
E. Dn On

In the end I had to provide custom factory implementation of JWTCallerPrincipalFactory, where I manually parsed json and replaced "sub" claim with the same claim of String type.

import javax.annotation.Priority
import javax.enterprise.context.ApplicationScoped
import javax.enterprise.inject.Alternative
import io.smallrye.jwt.auth.principal.*
import org.jose4j.jwt.JwtClaims
import org.jose4j.jwt.consumer.InvalidJwtException
import java.nio.charset.StandardCharsets
import java.util.*
import com.your.company.SignatureValidator // this is your custom validator for your algorithm

@ApplicationScoped
@Alternative
@Priority(1)
class MyJWTCallerPrincipalFactory : JWTCallerPrincipalFactory() {
    @Inject
    @field:Default
    lateinit var signatureValidator: SignatureValidator

    override fun parse(token: String, authContextInfo: JWTAuthContextInfo): JWTCallerPrincipal {
        return try {
            val isSignatureValid = signatureValidator.validate(token)
            if (!isSignatureValid) {
                throw ForbiddenException("Invalid Token Signature")
            }

            val json = String(Base64.getUrlDecoder().decode(token.split("\\.".toRegex()).dropLastWhile { it.isEmpty() }
                .toTypedArray()[1]), StandardCharsets.UTF_8)
            val jwtClaims = JwtClaims.parse(json)
            val subClaimValue = jwtClaims.getClaimValue("sub")
            jwtClaims.setClaim("sub", subClaimValue.toString())
            DefaultJWTCallerPrincipal(jwtClaims)
        } catch (ex: Exception) {
            throw ParseException(ex.message)
        }
    }
}