Skip to content

Commit bb65a3a

Browse files
authored
allow type header to be omitted when parsing JWT strings (#29)
1 parent 4c3db4b commit bb65a3a

File tree

2 files changed

+33
-2
lines changed
  • core/src
    • main/kotlin/io/github/nefilim/kjwt
    • test/kotlin/io/github/nefilim/kjwt

2 files changed

+33
-2
lines changed

core/src/main/kotlin/io/github/nefilim/kjwt/JWT.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ fun String?.toJWTKeyID(): JWTKeyID? = this?.let { JWTKeyID(it)}
3535
@Serializable
3636
data class JOSEHeader<T: JWSAlgorithm>(
3737
@SerialName("alg") @Serializable(JWSAlgorithmSerializer::class) val algorithm: T,
38-
@SerialName("typ") @Serializable(JOSETypeSerializer::class) val type: JOSEType,
38+
@SerialName("typ") @Serializable(JOSETypeSerializer::class) val type: JOSEType? = null,
3939
@SerialName("kid") val keyID: JWTKeyID? = null,
4040
) {
4141
fun toJSON(): String {
@@ -104,6 +104,7 @@ class JWT<T: JWSAlgorithm> private constructor(
104104
prettyPrint = true
105105
}
106106

107+
internal fun es256WithoutTypeHeader(claims: JWTClaimSetBuilder.() -> Unit): JWT<JWSES256Algorithm> = buildJWT(JOSEHeader(JWSES256Algorithm), claims)
107108
fun es256(keyID: JWTKeyID? = null, claims: JWTClaimSetBuilder.() -> Unit): JWT<JWSES256Algorithm> = buildJWT(JOSEHeader(JWSES256Algorithm, JOSEType.JWT, keyID), claims)
108109
fun es256k(keyID: JWTKeyID? = null, claims: JWTClaimSetBuilder.() -> Unit): JWT<JWSES256KAlgorithm> = buildJWT(JOSEHeader(JWSES256KAlgorithm, JOSEType.JWT, keyID), claims)
109110
fun es384(keyID: JWTKeyID? = null, claims: JWTClaimSetBuilder.() -> Unit): JWT<JWSES384Algorithm> = buildJWT(JOSEHeader(JWSES384Algorithm, JOSEType.JWT, keyID), claims)
@@ -131,7 +132,9 @@ class JWT<T: JWSAlgorithm> private constructor(
131132

132133
val h = Either.catch {
133134
format.decodeFromString(JOSEHeader.serializer(PolymorphicSerializer(JWSAlgorithm::class)), jwtDecodeString(parts[0]))
134-
}.mapLeft { KJWTVerificationError.AlgorithmMismatch }.bind()
135+
}.mapLeft {
136+
println(it)
137+
KJWTVerificationError.AlgorithmMismatch }.bind()
135138
val claims = Either.catch { format.parseToJsonElement(jwtDecodeString(parts[1])) }.mapLeft { KJWTVerificationError.InvalidJWT }.bind()
136139
val claimsMap = Either.catch { (claims as JsonObject) }.mapLeft { KJWTVerificationError.EmptyClaims }.bind()
137140

core/src/test/kotlin/io/github/nefilim/kjwt/JWTSpec.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import io.github.nefilim.kjwt.ClaimsVerification.requiredOptionClaim
1515
import io.github.nefilim.kjwt.ClaimsVerification.subject
1616
import io.github.nefilim.kjwt.ClaimsVerification.validateClaims
1717
import io.github.nefilim.kjwt.JWT.Companion.es256
18+
import io.github.nefilim.kjwt.JWT.Companion.es256WithoutTypeHeader
1819
import io.github.nefilim.kjwt.JWT.Companion.es256k
1920
import io.github.nefilim.kjwt.JWT.Companion.es384
2021
import io.github.nefilim.kjwt.JWT.Companion.es512
@@ -136,6 +137,33 @@ class JWTSpec: WordSpec() {
136137
}
137138
}
138139

140+
"decode JWT with missing type header" {
141+
val rawJWT = es256WithoutTypeHeader {
142+
subject("1234567890")
143+
issuedAt(LocalDateTime.ofInstant(Instant.ofEpochSecond(1516239022), ZoneId.of("UTC")))
144+
}
145+
// create a token with a spec violating lowercase type of "jwt"
146+
val jwtString = listOf(
147+
"""
148+
{
149+
"alg": "${rawJWT.header.algorithm.headerID}"
150+
}
151+
""".trimIndent(),
152+
"""
153+
{
154+
"sub": "${rawJWT.subject().getOrElse { "" }}",
155+
"iat": ${rawJWT.issuedAt().map { it.toEpochSecond(ZoneOffset.UTC) }.getOrElse { 0 }}
156+
}
157+
""".trimIndent()
158+
).joinToString(".") {
159+
jwtEncodeBytes(it.toByteArray(Charsets.UTF_8))
160+
}
161+
JWT.decode(jwtString).shouldBeRight().also {
162+
it.parts.size shouldBe 2
163+
it.jwt shouldBe rawJWT
164+
}
165+
}
166+
139167
"support arbitrary JSON claim values" {
140168
val thelist = listOf("tagA", "tagB", "tagC")
141169
val rawJWT = es256 {

0 commit comments

Comments
 (0)