@@ -230,6 +230,69 @@ val parser = Jwt.parser()
230230
231231---
232232
233+ ## Using cryptography-kotlin Keys Directly (Parser)
234+
235+ The ` co.touchlab.kjwt.ext ` package also provides ` verifyWith ` and ` decryptWith ` overloads on
236+ ` JwtParserBuilder ` that accept a raw ` String ` key plus the corresponding cryptography-kotlin format
237+ type. These are the parser-side twins of the builder extensions described above.
238+
239+ ### HMAC (HS256 / HS384 / HS512)
240+
241+ ``` kotlin
242+ import dev.whyoleg.cryptography.algorithms.HMAC
243+
244+ val parser = Jwt .parser()
245+ .verifyWith(SigningAlgorithm .HS256 , hmacKeyString, HMAC .Key .Format .RAW )
246+ .build()
247+ ```
248+
249+ ### RSA PKCS #1 v1.5 (RS256 / RS384 / RS512)
250+
251+ ``` kotlin
252+ import dev.whyoleg.cryptography.algorithms.RSA
253+
254+ val parser = Jwt .parser()
255+ .verifyWith(SigningAlgorithm .RS256 , pemString, RSA .PublicKey .Format .PEM )
256+ .build()
257+ ```
258+
259+ ### RSA PSS (PS256 / PS384 / PS512)
260+
261+ ``` kotlin
262+ val parser = Jwt .parser()
263+ .verifyWith(SigningAlgorithm .PS256 , pemString, RSA .PublicKey .Format .PEM )
264+ .build()
265+ ```
266+
267+ ### ECDSA (ES256 / ES384 / ES512)
268+
269+ ``` kotlin
270+ import dev.whyoleg.cryptography.algorithms.EC
271+
272+ val parser = Jwt .parser()
273+ .verifyWith(SigningAlgorithm .ES256 , rawKeyString, EC .PublicKey .Format .RAW )
274+ .build()
275+ ```
276+
277+ All overloads accept an optional ` keyId ` parameter so the key participates in the standard ` kid `
278+ matching strategy described under [ Multiple Keys] ( #multiple-keys-key-rotation ) .
279+
280+ ### Direct key (` dir ` ) decryption from ` ByteArray ` or ` String `
281+
282+ ``` kotlin
283+ // from raw bytes
284+ val parser = Jwt .parser()
285+ .decryptWith(cekBytes, EncryptionAlgorithm .Dir )
286+ .build()
287+
288+ // from a UTF-8 string (converted to bytes automatically)
289+ val parser = Jwt .parser()
290+ .decryptWith(cekString, EncryptionAlgorithm .Dir )
291+ .build()
292+ ```
293+
294+ ---
295+
233296## Standard Claims
234297
235298All seven RFC 7519 registered claims are supported via the builder:
@@ -273,11 +336,44 @@ val jws: JwtInstance.Jws = Jwt.builder()
273336val token: String = jws.compact()
274337```
275338
339+ ### Merging a serializable object into the payload
340+
341+ Use ` payload(value) ` to merge all fields from a ` @Serializable ` object into the token payload at once,
342+ instead of setting each claim individually:
343+
344+ ``` kotlin
345+ @Serializable
346+ data class UserClaims (
347+ @SerialName(" role" ) val role : String? = null ,
348+ @SerialName(" level" ) val level : Int? = null ,
349+ )
350+
351+ val jws: JwtInstance .Jws = Jwt .builder()
352+ .subject(" user-123" )
353+ .payload(UserClaims (role = " admin" , level = 5 )) // merges all fields
354+ .signWith(signingKey)
355+ ```
356+
357+ Each field in the object is written as a claim, overwriting any existing value with the same name.
358+ Standard claims set before or after the call (e.g. ` .subject() ` ) are not affected unless the
359+ serializable type defines a field that maps to the same claim name.
360+
276361## Header Parameters
277362
363+ Header fields can be set either with flat setter methods or with the ` header { } ` DSL block:
364+
278365``` kotlin
279366val rsaSigningKey = SigningAlgorithm .RS256 .parsePrivateKey(pemBytes, keyId = " key-2024-01" )
280367
368+ // Flat setters (new)
369+ val jws: JwtInstance .Jws = Jwt .builder()
370+ .subject(" user-123" )
371+ .type(" JWT" ) // typ
372+ .contentType(" application/json" ) // cty
373+ .header(" x-custom" , " value" ) // extra parameter (reified)
374+ .signWith(rsaSigningKey)
375+
376+ // DSL block (original)
281377val jws: JwtInstance .Jws = Jwt .builder()
282378 .subject(" user-123" )
283379 .header {
@@ -289,6 +385,25 @@ val jws: JwtInstance.Jws = Jwt.builder()
289385val token: String = jws.compact()
290386```
291387
388+ ### Merging a serializable object into the header
389+
390+ Use ` header(value) ` to merge all fields from a ` @Serializable ` object into the JOSE header at once:
391+
392+ ``` kotlin
393+ @Serializable
394+ data class MyHeader (
395+ @SerialName(" x-tenant" ) val tenant : String? = null ,
396+ @SerialName(" x-version" ) val version : Int? = null ,
397+ )
398+
399+ val jws: JwtInstance .Jws = Jwt .builder()
400+ .subject(" user-123" )
401+ .header(MyHeader (tenant = " acme" , version = 2 )) // merges all fields
402+ .signWith(signingKey)
403+ ```
404+
405+ Each field in the object is written as a header parameter, overwriting any existing value with the same name.
406+
292407## Key ID (` kid ` )
293408
294409The ` kid ` header parameter identifies which key was used to sign or encrypt a token — defined in RFC 7515 §4.1.4 for JWS
@@ -603,6 +718,27 @@ println(payload.subject)
603718
604719` getPayload<T>() ` is available on both ` JwtInstance.Jws ` and ` JwtInstance.Jwe ` .
605720
721+ ## Custom Header Types
722+
723+ The same pattern works for the JOSE header. Define a ` @Serializable ` data class whose fields map
724+ to the header parameter names you care about, then call ` getHeader<T>() ` :
725+
726+ ``` kotlin
727+ @Serializable
728+ data class MyHeader (
729+ @SerialName(" alg" ) val algorithm : String? = null ,
730+ @SerialName(" kid" ) val keyId : String? = null ,
731+ @SerialName(" x-tenant" ) val tenant : String? = null ,
732+ )
733+
734+ val jws: JwtInstance .Jws = parser.parseSigned(token)
735+ val header: MyHeader = jws.getHeader<MyHeader >()
736+ println (header.keyId)
737+ println (header.tenant)
738+ ```
739+
740+ ` getHeader<T>() ` is available on both ` JwtInstance.Jws ` and ` JwtInstance.Jwe ` .
741+
606742## JWE with Direct Key (` dir ` )
607743
608744For symmetric encryption where the key is used directly as the CEK (no key wrapping):
0 commit comments