gose/cose/cwt
CBOR Web Token (CWT) RFC 8392
CWT is the CBOR equivalent of JWT, providing claims-based tokens using COSE for signing and verification.
Example
import gleam/time/duration
import gleam/time/timestamp
import gose/algorithm
import gose/cose/cwt
import gose/key
import kryptos/ec
let signing_key = key.generate_ec(ec.P256)
let now = timestamp.system_time()
let exp = timestamp.add(now, duration.hours(1))
let claims = cwt.new()
|> cwt.with_subject("user123")
|> cwt.with_issuer("my-app")
|> cwt.with_expiration(exp)
let assert Ok(token) =
cwt.sign(
claims,
alg: algorithm.Ecdsa(algorithm.EcdsaP256),
key: signing_key,
)
let assert Ok(verifier) =
cwt.verifier(algorithm.Ecdsa(algorithm.EcdsaP256), keys: [signing_key])
let assert Ok(verified) = cwt.verify_and_validate(verifier, token:, now:)
let verified_claims = cwt.verified_claims(verified)
Phantom Types
Cwt(state) uses a phantom type to track verification state:
Unverified: parsed but not yet verifiedVerified: signature verified and claims validated, safe to trust
Types
The set of claims carried by a CWT (registered + custom).
pub opaque type CwtClaims
Errors from CWT operations, covering both COSE-layer failures and claim validation.
pub type CwtError {
CoseError(gose.GoseError)
InvalidSignature
MalformedToken(reason: String)
TokenExpired(expired_at: timestamp.Timestamp)
TokenNotYetValid(valid_from: timestamp.Timestamp)
IssuerMismatch(expected: String, actual: option.Option(String))
AudienceMismatch(
expected: String,
actual: option.Option(List(String)),
)
MissingExpiration
DecryptionFailed(reason: String)
InvalidClaim(reason: String)
}
Constructors
-
CoseError(gose.GoseError)An error from the underlying COSE layer (signing, verification, decryption, key validation).
-
InvalidSignatureThe COSE_Sign1 signature did not verify against any of the provided keys.
-
MalformedToken(reason: String)The token could not be parsed (invalid CBOR, unexpected claim types, etc.).
-
TokenExpired(expired_at: timestamp.Timestamp)The
expclaim is in the past. -
TokenNotYetValid(valid_from: timestamp.Timestamp)The
nbfclaim is in the future. -
IssuerMismatch(expected: String, actual: option.Option(String))The
issclaim does not match the expected issuer. -
AudienceMismatch( expected: String, actual: option.Option(List(String)), )The
audclaim does not contain the expected audience. -
MissingExpirationThe
expclaim is required by the verifier but absent. -
DecryptionFailed(reason: String)COSE decryption failed (wrong key, corrupted ciphertext, etc.).
-
InvalidClaim(reason: String)A claim value is invalid (empty audience list, etc.).
Phantom type for a CWT that has been parsed but not yet verified.
pub type Unverified
Phantom type for a CWT whose signature and claims have been validated.
pub type Verified
Values
pub fn audience(claims: CwtClaims) -> Result(List(String), Nil)
Read the audience claim as a list of strings.
pub fn custom_claim(
claims: CwtClaims,
key key: cbor.Value,
) -> Result(cbor.Value, Nil)
Look up a custom claim by its CBOR key.
pub fn expiration(
claims: CwtClaims,
) -> Result(timestamp.Timestamp, Nil)
Read the expiration time as a timestamp.
pub fn issued_at(
claims: CwtClaims,
) -> Result(timestamp.Timestamp, Nil)
Read the issued-at time as a timestamp.
pub fn not_before(
claims: CwtClaims,
) -> Result(timestamp.Timestamp, Nil)
Read the not-before time as a timestamp.
pub fn sign(
claims: CwtClaims,
alg alg: algorithm.DigitalSignatureAlg,
key key: key.Key(BitArray),
) -> Result(BitArray, CwtError)
Sign a set of claims as a COSE_Sign1-wrapped CWT, returning the serialized CBOR bytes.
pub fn verified_claims(cwt: Cwt(Verified)) -> CwtClaims
Extract the validated claims from a verified CWT.
pub fn verifier(
alg: algorithm.DigitalSignatureAlg,
keys keys: List(key.Key(BitArray)),
) -> Result(Verifier, CwtError)
Build a CWT verifier pinned to a single signature algorithm and one or more keys.
pub fn verify_and_validate(
verifier: Verifier,
token token: BitArray,
now now: timestamp.Timestamp,
) -> Result(Cwt(Verified), CwtError)
Parse, verify the signature, and validate claims in one step.
pub fn with_audience(
claims: CwtClaims,
audience: String,
) -> CwtClaims
Set a single audience (aud, label 3) claim.
pub fn with_audience_validation(
verifier: Verifier,
audience: String,
) -> Verifier
Require the token’s aud claim to include the given audience.
pub fn with_audiences(
claims: CwtClaims,
audiences: List(String),
) -> Result(CwtClaims, CwtError)
Set multiple audiences (aud, label 3) as an array.
pub fn with_clock_skew(
verifier: Verifier,
seconds: Int,
) -> Verifier
Set the allowed clock skew in seconds (default: 60).
Tokens are accepted up to seconds past exp or before nbf.
pub fn with_custom_claim(
claims: CwtClaims,
key key: cbor.Value,
value value: cbor.Value,
) -> Result(CwtClaims, CwtError)
Add a custom (non-registered) claim keyed by an arbitrary CBOR value.
Returns an error if the key collides with a registered CWT label (1-7). If the key already exists in custom claims, the value is replaced.
pub fn with_expiration(
claims: CwtClaims,
exp: timestamp.Timestamp,
) -> CwtClaims
Set the expiration time (exp, label 4) claim.
pub fn with_issued_at(
claims: CwtClaims,
iat: timestamp.Timestamp,
) -> CwtClaims
Set the issued-at time (iat, label 6) claim.
pub fn with_issuer(
claims: CwtClaims,
issuer: String,
) -> CwtClaims
Set the issuer (iss, label 1) claim.
pub fn with_issuer_validation(
verifier: Verifier,
issuer: String,
) -> Verifier
Require the token’s iss claim to match the given issuer.
pub fn with_not_before(
claims: CwtClaims,
nbf: timestamp.Timestamp,
) -> CwtClaims
Set the not-before time (nbf, label 5) claim.
pub fn with_require_expiration(
verifier: Verifier,
required: Bool,
) -> Verifier
Control whether the exp claim is required (default: True).