Skip to content

Commit 3fc4a1a

Browse files
committed
Adding kdocs
1 parent 84c6f8c commit 3fc4a1a

File tree

19 files changed

+804
-29
lines changed

19 files changed

+804
-29
lines changed

build-logic/src/main/kotlin/kjwt/docs.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ fun DokkaSourceSetSpec.registerExternalDocumentation() {
2626
externalDocumentationLinks.register("cryptography-kotlin") {
2727
url("https://whyoleg.github.io/cryptography-kotlin/api/")
2828
}
29+
externalDocumentationLinks.register("kotlinx-serialization") {
30+
url("https://kotlinlang.org/api/kotlinx.serialization/")
31+
}
2932
}
3033

3134
fun DokkaExtension.registerVersioningPlugin(project: Project) {

lib/src/commonMain/kotlin/co/touchlab/kjwt/builder/JwtBuilder.kt

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,35 +41,147 @@ public class JwtBuilder {
4141
internal val payloadBuilder: JwtPayload.Builder = JwtPayload.Builder()
4242
private val headerBuilder: JwtHeader.Builder = JwtHeader.Builder()
4343

44+
/**
45+
* Sets the issuer (`iss`) claim.
46+
*
47+
* @param iss the issuer identifier
48+
* @return this builder for chaining
49+
*/
4450
public fun issuer(iss: String): JwtBuilder = apply { payloadBuilder.issuer = iss }
51+
52+
/**
53+
* Sets the subject (`sub`) claim.
54+
*
55+
* @param sub the subject identifier
56+
* @return this builder for chaining
57+
*/
4558
public fun subject(sub: String): JwtBuilder = apply { payloadBuilder.subject = sub }
59+
60+
/**
61+
* Sets the audience (`aud`) claim.
62+
*
63+
* @param aud one or more audience identifiers
64+
* @return this builder for chaining
65+
*/
4666
public fun audience(vararg aud: String): JwtBuilder = apply { payloadBuilder.audience = aud.toSet() }
67+
68+
/**
69+
* Sets the expiration time (`exp`) claim.
70+
*
71+
* @param exp the absolute instant at which the token expires
72+
* @return this builder for chaining
73+
*/
4774
public fun expiration(exp: Instant): JwtBuilder = apply { payloadBuilder.expiration = exp }
75+
76+
/**
77+
* Sets the expiration time (`exp`) claim relative to the current time.
78+
*
79+
* @param duration the duration from now until the token expires
80+
* @return this builder for chaining
81+
*/
4882
public fun expiresIn(duration: Duration): JwtBuilder = apply { payloadBuilder.expiresIn(duration) }
83+
84+
/**
85+
* Sets the not-before (`nbf`) claim.
86+
*
87+
* @param nbf the absolute instant before which the token must not be accepted
88+
* @return this builder for chaining
89+
*/
4990
public fun notBefore(nbf: Instant): JwtBuilder = apply { payloadBuilder.notBefore = nbf }
91+
92+
/**
93+
* Sets the not-before (`nbf`) claim to the current time.
94+
*
95+
* @return this builder for chaining
96+
*/
5097
public fun notBeforeNow(): JwtBuilder = apply { payloadBuilder.notBeforeNow() }
98+
99+
/**
100+
* Sets the issued-at (`iat`) claim.
101+
*
102+
* @param iat the instant at which the token was issued
103+
* @return this builder for chaining
104+
*/
51105
public fun issuedAt(iat: Instant): JwtBuilder = apply { payloadBuilder.issuedAt = iat }
106+
107+
/**
108+
* Sets the issued-at (`iat`) claim to the current time.
109+
*
110+
* @return this builder for chaining
111+
*/
52112
public fun issuedNow(): JwtBuilder = apply { payloadBuilder.issuedNow() }
113+
114+
/**
115+
* Sets the JWT ID (`jti`) claim.
116+
*
117+
* @param jti the unique identifier for this token
118+
* @return this builder for chaining
119+
*/
53120
public fun id(jti: String): JwtBuilder = apply { payloadBuilder.id = jti }
54121

122+
/**
123+
* Sets the JWT ID (`jti`) claim to a randomly generated UUID.
124+
*
125+
* @return this builder for chaining
126+
*/
55127
@ExperimentalUuidApi
56128
public fun randomId(): JwtBuilder = apply { payloadBuilder.randomId() }
57129

130+
/**
131+
* Sets a raw claim using a pre-built [JsonElement].
132+
*
133+
* @param name the claim name
134+
* @param value the claim value as a [JsonElement]
135+
* @return this builder for chaining
136+
*/
58137
public fun claim(name: String, value: JsonElement): JwtBuilder =
59138
apply { payloadBuilder.claim(name, value) }
60139

140+
/**
141+
* Sets a typed claim using an explicit [SerializationStrategy].
142+
*
143+
* @param name the claim name
144+
* @param serializer the serialization strategy for [T]
145+
* @param value the claim value, or `null` to remove the claim
146+
* @return this builder for chaining
147+
*/
61148
public fun <T> claim(name: String, serializer: SerializationStrategy<T>, value: T?): JwtBuilder =
62149
apply { payloadBuilder.claim(name, serializer, value) }
63150

151+
/**
152+
* Sets a typed claim, inferring the serializer from the reified type [T].
153+
*
154+
* @param name the claim name
155+
* @param value the claim value
156+
* @return this builder for chaining
157+
*/
64158
public inline fun <reified T> claim(name: String, value: T): JwtBuilder =
65159
apply { payloadBuilder.claim(name, value) }
66160

161+
/**
162+
* Configures multiple claims at once using a DSL block applied to [JwtPayload.Builder].
163+
*
164+
* @param block the configuration block
165+
* @return this builder for chaining
166+
*/
67167
public fun claims(block: JwtPayload.Builder.() -> Unit): JwtBuilder =
68168
apply { payloadBuilder.block() }
69169

170+
/**
171+
* Configures JOSE header fields using a DSL block applied to [JwtHeader.Builder].
172+
*
173+
* @param block the configuration block
174+
* @return this builder for chaining
175+
*/
70176
public fun header(block: JwtHeader.Builder.() -> Unit): JwtBuilder =
71177
apply { headerBuilder.block() }
72178

179+
/**
180+
* Sets the key ID (`kid`) header parameter.
181+
*
182+
* @param kid the key identifier
183+
* @return this builder for chaining
184+
*/
73185
public fun keyId(kid: String): JwtBuilder =
74186
apply { headerBuilder.keyId = kid }
75187

@@ -91,6 +203,13 @@ public class JwtBuilder {
91203
return JwtInstance.Jws(header, payload, signature.encodeBase64Url())
92204
}
93205

206+
/**
207+
* Builds and returns an unsecured JWS token with `alg=none` and an empty signature.
208+
*
209+
* @param algorithm the [SigningAlgorithm.None] sentinel value
210+
* @return the resulting [JwtInstance.Jws] with an empty signature segment
211+
* @see co.touchlab.kjwt.parser.JwtParserBuilder.allowUnsecured
212+
*/
94213
public suspend fun signWith(algorithm: SigningAlgorithm.None): JwtInstance.Jws =
95214
signWith(algorithm, SimpleKey.Empty)
96215

lib/src/commonMain/kotlin/co/touchlab/kjwt/cryptography/SimpleKey.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@ import dev.whyoleg.cryptography.CryptographyProviderApi
44
import dev.whyoleg.cryptography.materials.key.Key
55

66
@OptIn(CryptographyProviderApi::class)
7-
public class SimpleKey(public val value: ByteArray) : Key {
7+
public class SimpleKey(
8+
/** The raw key bytes that back this key material. */
9+
public val value: ByteArray,
10+
) : Key {
811
public companion object {
12+
/**
13+
* An empty (zero-length) key singleton, used as a placeholder for direct-key (`dir`) JWE encryption where no
14+
* wrapping key is needed.
15+
*/
916
public val Empty: SimpleKey = SimpleKey(ByteArray(0))
1017
}
1118
}

lib/src/commonMain/kotlin/co/touchlab/kjwt/exception/JwtException.kt

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,66 @@ package co.touchlab.kjwt.exception
33
import co.touchlab.kjwt.model.JwtHeader
44
import co.touchlab.kjwt.model.JwtPayload
55

6+
/** Base class for all exceptions thrown by the KJWT library. */
67
public open class JwtException(message: String, cause: Throwable? = null) : Exception(message, cause)
78

9+
/**
10+
* Thrown when a JWT string is structurally invalid, such as having the wrong number of parts, containing invalid
11+
* Base64URL encoding, or being unparseable as JSON.
12+
*/
813
public class MalformedJwtException(message: String, cause: Throwable? = null) : JwtException(message, cause)
914

15+
/**
16+
* Thrown when a JWK JSON object is structurally invalid or is missing one or more required fields needed to
17+
* reconstruct the key.
18+
*/
1019
public class MalformedJwkException(message: String, cause: Throwable? = null) : JwtException(message, cause)
1120

21+
/**
22+
* Thrown when signature verification of a JWS token fails or when decryption of a JWE token fails, indicating the
23+
* token may have been tampered with or was encrypted with a different key.
24+
*/
1225
public class SignatureException(message: String, cause: Throwable? = null) : JwtException(message, cause)
1326

27+
/** Thrown when a token uses an algorithm, key type, or feature that is not supported by this library. */
1428
public class UnsupportedJwtException(message: String, cause: Throwable? = null) : JwtException(message, cause)
1529

30+
/** Thrown when a JWT's `exp` (expiration time) claim indicates the token has already expired. */
1631
public class ExpiredJwtException(
32+
/** The header of the expired token. */
1733
public val header: JwtHeader,
34+
/** The claims of the expired token whose `exp` value has passed. */
1835
public val claims: JwtPayload,
1936
message: String,
2037
) : JwtException(message)
2138

39+
/** Thrown when a JWT's `nbf` (not-before) claim indicates the token is not yet valid. */
2240
public class PrematureJwtException(
41+
/** The header of the premature token. */
2342
public val header: JwtHeader,
43+
/** The claims of the premature token whose `nbf` value has not yet been reached. */
2444
public val claims: JwtPayload,
2545
message: String,
2646
) : JwtException(message)
2747

48+
/** Thrown when a required claim is absent from the token's payload. */
2849
public class MissingClaimException(
50+
/** The name of the required claim that was absent from the token. */
2951
public val claimName: String,
3052
) : JwtException("Missing required claim: '$claimName'")
3153

54+
/** Thrown when a required header parameter is absent from the token's header. */
3255
public class MissingHeaderException(
56+
/** The name of the required header parameter that was absent from the token. */
3357
public val headerName: String,
3458
) : JwtException("Missing required header: '$headerName'")
3559

60+
/** Thrown when a claim is present in the token but its value does not match the expected value. */
3661
public class IncorrectClaimException(
62+
/** The name of the claim whose value did not match. */
3763
public val claimName: String,
64+
/** The expected value that the claim should have had. */
3865
public val expected: Any?,
66+
/** The actual value found in the token for this claim. */
3967
public val actual: Any?,
4068
) : JwtException("Claim '$claimName' expected '$expected' but was '$actual'")

lib/src/commonMain/kotlin/co/touchlab/kjwt/ext/JwkBuilderExt.kt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,34 +11,70 @@ import co.touchlab.kjwt.model.jwk.Jwk
1111
// signWith — HMAC (oct)
1212
// ---------------------------------------------------------------------------
1313

14+
/**
15+
* Signs the JWT using an HMAC key derived from the given [Jwk.Oct] symmetric JWK.
16+
*
17+
* @param algorithm The HMAC-based signing algorithm (HS256, HS384, or HS512).
18+
* @param jwk The Oct JWK containing the raw symmetric key material.
19+
* @return The signed [JwtInstance.Jws] token.
20+
*/
1421
public suspend fun JwtBuilder.signWith(algorithm: SigningAlgorithm.HashBased, jwk: Jwk.Oct): JwtInstance.Jws =
1522
signWith(algorithm, jwk.toHmacKey(algorithm.digest))
1623

1724
// ---------------------------------------------------------------------------
1825
// signWith — RSA PKCS1 (RS*)
1926
// ---------------------------------------------------------------------------
2027

28+
/**
29+
* Signs the JWT using an RSA PKCS#1 private key derived from the given [Jwk.Rsa] JWK.
30+
*
31+
* @param algorithm The RSA PKCS#1-based signing algorithm (RS256, RS384, or RS512).
32+
* @param jwk The RSA JWK containing the private key parameters.
33+
* @return The signed [JwtInstance.Jws] token.
34+
*/
2135
public suspend fun JwtBuilder.signWith(algorithm: SigningAlgorithm.PKCS1Based, jwk: Jwk.Rsa): JwtInstance.Jws =
2236
signWith(algorithm, jwk.toRsaPkcs1PrivateKey(algorithm.digest))
2337

2438
// ---------------------------------------------------------------------------
2539
// signWith — RSA PSS (PS*)
2640
// ---------------------------------------------------------------------------
2741

42+
/**
43+
* Signs the JWT using an RSA PSS private key derived from the given [Jwk.Rsa] JWK.
44+
*
45+
* @param algorithm The RSA PSS-based signing algorithm (PS256, PS384, or PS512).
46+
* @param jwk The RSA JWK containing the private key parameters.
47+
* @return The signed [JwtInstance.Jws] token.
48+
*/
2849
public suspend fun JwtBuilder.signWith(algorithm: SigningAlgorithm.PSSBased, jwk: Jwk.Rsa): JwtInstance.Jws =
2950
signWith(algorithm, jwk.toRsaPssPrivateKey(algorithm.digest))
3051

3152
// ---------------------------------------------------------------------------
3253
// signWith — ECDSA (ES*)
3354
// ---------------------------------------------------------------------------
3455

56+
/**
57+
* Signs the JWT using an ECDSA private key derived from the given [Jwk.Ec] JWK.
58+
*
59+
* @param algorithm The ECDSA-based signing algorithm (ES256, ES384, or ES512).
60+
* @param jwk The EC JWK containing the private key parameter `d`.
61+
* @return The signed [JwtInstance.Jws] token.
62+
*/
3563
public suspend fun JwtBuilder.signWith(algorithm: SigningAlgorithm.ECDSABased, jwk: Jwk.Ec): JwtInstance.Jws =
3664
signWith(algorithm, jwk.toEcdsaPrivateKey())
3765

3866
// ---------------------------------------------------------------------------
3967
// encryptWith — RSA-OAEP / RSA-OAEP-256
4068
// ---------------------------------------------------------------------------
4169

70+
/**
71+
* Encrypts the JWT using an RSA OAEP public key derived from the given [Jwk.Rsa] JWK.
72+
*
73+
* @param jwk The RSA JWK containing the public key parameters `n` and `e`.
74+
* @param keyAlgorithm The OAEP-based key encryption algorithm (RSA-OAEP or RSA-OAEP-256).
75+
* @param contentAlgorithm The content encryption algorithm to use for the JWE payload.
76+
* @return The encrypted [JwtInstance.Jwe] token.
77+
*/
4278
@OptIn(dev.whyoleg.cryptography.DelicateCryptographyApi::class)
4379
public suspend fun JwtBuilder.encryptWith(
4480
jwk: Jwk.Rsa,

lib/src/commonMain/kotlin/co/touchlab/kjwt/ext/JwkExt.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ import dev.whyoleg.cryptography.serialization.asn1.modules.RsaPrivateKey
2727
import dev.whyoleg.cryptography.serialization.asn1.modules.RsaPublicKey
2828
import dev.whyoleg.cryptography.serialization.asn1.modules.SubjectPublicKeyInfo
2929

30+
/**
31+
* Computes the base64url-encoded SHA-256 hash of this JWK Thumbprint as defined by RFC 7638.
32+
*
33+
* @return The base64url-encoded SHA-256 digest of the canonical JSON representation of this thumbprint.
34+
*/
3035
public suspend fun Jwk.Thumbprint.hashed(): String {
3136
val bytes = JwtJson.encodeToString(this).encodeToByteArray()
3237
val hash = CryptographyProvider.Default.get(SHA256).hasher().hash(bytes)

0 commit comments

Comments
 (0)