Skip to content

Commit

Permalink
refactor: Made JWT payload and header classes to be used as input
Browse files Browse the repository at this point in the history
  • Loading branch information
Zoe Maas committed Aug 2, 2024
1 parent fe0df4c commit db8e116
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
package com.sphereon.oid.fed.common.jwt

expect fun sign(payload: String, header: String, opts: Map<String, Any>): String
expect class JwtHeader
expect class JwtPayload

expect fun sign(payload: JwtPayload, header: JwtHeader, opts: Map<String, Any>): String
expect fun verify(jwt: String, key: Any, opts: Map<String, Any>): Boolean
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
package com.sphereon.oid.fed.common.jwt

import com.sphereon.oid.fed.openapi.models.EntityStatement
import com.sphereon.oid.fed.openapi.models.JWTHeader
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@JsModule("jose")
@JsNonModule
external object Jose {
Expand All @@ -18,22 +23,20 @@ external object Jose {
fun jwtVerify(jwt: String, key: Any, options: dynamic = definedExternally): dynamic
}

@JsModule("uuid")
@JsNonModule
external object Uuid {
fun v4(): String
}
actual typealias JwtPayload = EntityStatement
actual typealias JwtHeader = JWTHeader

@ExperimentalJsExport
@JsExport
actual fun sign(
payload: String,
header: String,
payload: JwtPayload,
header: JwtHeader,
opts: Map<String, Any>
): String {
val privateKey = opts["privateKey"] ?: throw IllegalArgumentException("JWK private key is required")
return Jose.SignJWT(JSON.parse<Any>(payload).asDynamic())
.setProtectedHeader(JSON.parse<Any>(header).asDynamic())

return Jose.SignJWT(JSON.parse<Any>(Json.encodeToString(payload)))
.setProtectedHeader(JSON.parse<Any>(Json.encodeToString(header)))
.sign(key = privateKey, signOptions = opts)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ class JoseJwtTest {
@Test
fun signTest() = runTest {
val keyPair = (generateKeyPair("RS256") as Promise<dynamic>).await()
val result = async { sign("{\"iss\":\"test\"}",
"{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"test\"}",
val result = async {
sign(
JwtPayload(iss="test"),
JwtHeader(typ="JWT",alg="RS256",kid="test"),
mutableMapOf("privateKey" to keyPair.privateKey)) }
assertTrue((result.await() as Promise<String>).await().startsWith("ey"))
}
Expand All @@ -24,10 +26,10 @@ class JoseJwtTest {
fun verifyTest() = runTest {
val keyPair = (generateKeyPair("RS256") as Promise<dynamic>).await()
val signed = (sign(
"{\"iss\":\"test\"}",
"{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"test\" }",
JwtPayload(iss="test"),
JwtHeader(typ="JWT",alg="RS256",kid="test"),
mutableMapOf("privateKey" to keyPair.privateKey)) as Promise<dynamic>).await()
val result = async { verify(signed, keyPair.publicKey, emptyMap()) }
assertTrue((result.await() as Promise<Boolean>).await())
assertTrue((result.await()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,21 @@ import com.nimbusds.jose.jwk.RSAKey
import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.SignedJWT

actual typealias JwtPayload = JWTClaimsSet
actual typealias JwtHeader = JWSHeader

actual fun sign(
payload: String,
header: String,
payload: JwtPayload,
header: JwtHeader,
opts: Map<String, Any>
): String {
val rsaJWK = opts["key"] as RSAKey? ?: throw IllegalArgumentException("The RSA key pair is required")

val protectedHeader = JWSHeader.parse(header)

val signer: JWSSigner = RSASSASigner(rsaJWK)

val claimsSet = JWTClaimsSet.parse(payload)

val signedJWT = SignedJWT(
protectedHeader,
claimsSet
header,
payload
)

signedJWT.sign(signer)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,15 @@ class JoseJwtTest {
fun signTest() {
val key = RSAKeyGenerator(2048).keyID("key1").generate()
val signature = sign(
"{ \"iss\": \"test\" }",
"{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"${key.keyID}\"}",
JwtPayload.parse(
mutableMapOf<String, Any>(
"iss" to "test"
)
),
JwtHeader.parse(mutableMapOf<String, Any>(
"typ" to "JWT",
"alg" to "RS256",
"kid" to key.keyID)),
mutableMapOf("key" to key)
)
assertTrue { signature.startsWith("ey") }
Expand All @@ -22,10 +29,16 @@ class JoseJwtTest {
fun verifyTest() {
val kid = "key1"
val key: RSAKey = RSAKeyGenerator(2048).keyID(kid).generate()
val signature = sign("{ \"iss\": \"test\" }","{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"test\"}", mutableMapOf(
"key" to key,
"jwtHeader" to "{\"typ\":\"JWT\",\"alg\":\"RS256\",\"kid\":\"${key.keyID}\"}"
))
val signature = sign(
JwtPayload.parse(
mutableMapOf<String, Any>("iss" to "test")
),
JwtHeader.parse(mutableMapOf<String, Any>(
"typ" to "JWT",
"alg" to "RS256",
"kid" to key.keyID)),
mutableMapOf("key" to key)
)
assertTrue { verify(signature, key, emptyMap()) }
}
}

0 comments on commit db8e116

Please sign in to comment.