JSON Web Token (JWT) and JSON Web Signature (JWS)
This library provides limited functionality for validating JSON Web Tokens as specified in RFC 7519 [RFC7519]. At present, it supports encoding, decoding, and verifying JWTs that use the Compact JWS Serialization, as described in RFC 7515 [RFC7515].
1 JWTs as Racket data
type
type
type
JWTs support the following accessors, which correspond to the JWT claim names specified in RFC 7519 [RFC7519].
procedure
(expiration-date jwt) → (Option date)
jwt : JWT
procedure
(not-before jwt) → (Option date)
jwt : JWT
procedure
(claims-ref jwt key) → (Option JSExpr)
jwt : JWT key : Symbol
2 Encoding and signing JWTs
procedure
(encode/sign algorithm secret [ #:extra-headers headers #:iss iss #:sub sub #:aud aud #:exp exp #:nbf nbf #:iat iat #:jti jti #:other other]) → String algorithm : String secret : String headers : JSXHash = (hasheq) iss : (Option String) = #f sub : (Option String) = #f aud : (U String (Listof String)) = '() exp : (Option (U date Exact-Integer)) = #f nbf : (Option (U date Exact-Integer)) = #f iat : (Option (U date Exact-Integer)) = #f jti : (Option String) = #f other : JSXHash = (hasheq)
Raises an exn:fail:unsupported-algorithm if algorithm is not supported.
If a date struct is given for the exp, nbf, or iat parameter, it should represent a date after the epoch (1/1/1970); if it represents an earlier date, encode/sign will ignore the field.
Notably, encode/sign does not examine any of the key/value pairs in headers or other, so it is possible to create invalid JWTs by providing invalid values for the header parameters and claims defined in the JWS RFC. Refer to the RFC if you need to customize your header parameters or use the other parameter.
The RFC prohibits duplicate claims with the same keyword; here, if any of the parameters iss, sub, aud, exp, nbf, iat, or jti are provided, not #f or '(), and also occur as keys in other, the keyword parameters will replace the entries in other.
> (define compact-jwt1 (encode/sign "HS256" "swordfish" #:extra-headers (ann (hasheq 'kid "1234xbzsfgd54321") JSXHash) #:iss "http://fellowhuman.com/" #:sub "jmj" #:aud "http://example.com/" #:exp (+ (current-seconds) 86400) #:iat (current-seconds) #:other (ann (hasheq 'uid 12345) JSXHash)))
> compact-jwt1 - : String
"eyJraWQiOiIxMjM0eGJ6c2ZnZDU0MzIxIiwiYWxnIjoiSFMyNTYifQ.eyJleHAiOjE0NTQ1Mjk2NDMsImlzcyI6Imh0dHA6Ly9mZWxsb3dodW1hbi5jb20vIiwic3ViIjoiam1qIiwiYXVkIjoiaHR0cDovL2V4YW1wbGUuY29tLyIsImlhdCI6MTQ1NDQ0MzI0MywidWlkIjoxMjM0NX0.yaeEckcFyc0wZBV79X3ev_b29wlBg0UAPmqNe7uO5Hs"
procedure
(encode-jwt [ #:headers headers #:iss iss #:sub sub #:aud aud #:exp exp #:nbf nbf #:iat iat #:jti jti #:other other]) → String headers : JSXHash = (hasheq) iss : (Option String) = #f sub : (Option String) = #f aud : (U String (Listof String)) = '() exp : (Option (U date Exact-Integer)) = #f nbf : (Option (U date Exact-Integer)) = #f iat : (Option (U date Exact-Integer)) = #f jti : (Option String) = #f other : JSXHash = (hasheq)
> (define compact-jwt2 (encode-jwt #:iss "http://example.com/" #:sub "user12345" #:aud '("http://fellowhuman.com/" "http://www.fellowhuman.com/")))
> compact-jwt2 - : String
"eyJhbGciOiJub25lIn0.eyJpc3MiOiJodHRwOi8vZXhhbXBsZS5jb20vIiwic3ViIjoidXNlcjEyMzQ1IiwiYXVkIjpbImh0dHA6Ly9mZWxsb3dodW1hbi5jb20vIiwiaHR0cDovL3d3dy5mZWxsb3dodW1hbi5jb20vIl19."
3 Decoding JWTs (Compact JSON Serialization)
procedure
(decode-jwt jwt) → (Option JWT)
jwt : String
> (define uv-jwt1 (let ([decoded (decode-jwt compact-jwt1)]) (if decoded decoded (error "couldn't decode"))))
> (JWT? uv-jwt1) - : Boolean
#t
> (VerifiedJWT? uv-jwt1) - : Boolean
#f
> (header uv-jwt1) - : JSXHash
'#hasheq((kid . "1234xbzsfgd54321") (alg . "HS256"))
> (issuer uv-jwt1) - : (U False String)
"http://fellowhuman.com/"
> (subject uv-jwt1) - : (U False String)
"jmj"
> (expiration-date uv-jwt1) - : (U False date)
(date* 43 0 13 3 2 2016 3 33 #f -25200 0 "MST")
> (audiences uv-jwt1) - : (Listof String)
'("http://example.com/")
> (not-before uv-jwt1) - : (U False date)
#f
> (define uv-jwt2 (let ([decoded (decode-jwt compact-jwt2)]) (if decoded decoded (error "couldn't decode"))))
> (issuer uv-jwt2) - : (U False String)
"http://example.com/"
> (audiences uv-jwt2) - : (Listof String)
'("http://fellowhuman.com/" "http://www.fellowhuman.com/")
procedure
(verify-jwt jwt algorithm secret [ #:aud audience #:iss expected-issuer #:clock-skew skew]) → (Option VerifiedJWT) jwt : JWT algorithm : String secret : String audience : (Option String) = #f expected-issuer : (Option String) = #f skew : Exact-Nonnegative-Integer = 30
If audience is not #f and the JWT has an aud field, checks that audience matches one of the JWT’s audiences. If expected-issuer is not #f and the JWT has an iss field, checks that the JWT’s issuer matches expected-issuer. Also checks the current time against the JWT’s exp and nbf fields, if those are present. skew specifies a tolerance for those checks; a token will be accepted up to skew seconds after its expiration, and up to skew seconds before its nbf time.
> (verify-jwt uv-jwt1 "HS256" "wrong password") - : (U False verified-jwt)
#f
> (verify-jwt uv-jwt1 "HS256" "swordfish" #:aud "wrong audience") - : (U False verified-jwt)
#f
> (verify-jwt uv-jwt1 "HS256" "swordfish" #:iss "wrong issuer") - : (U False verified-jwt)
#f
> (define v-jwt1 (verify-jwt uv-jwt1 "HS256" "swordfish" #:aud "http://example.com/" #:iss "http://fellowhuman.com/"))
> (JWT? v-jwt1) - : Boolean
#t
> (VerifiedJWT? v-jwt1) - : Boolean
#t
> (and v-jwt1 (subject v-jwt1)) - : (U False String)
"jmj"
> (equal? (verify-jwt uv-jwt1 "HS256" "swordfish") v-jwt1) - : Boolean
#t
procedure
(decode/verify jwt algorithm secret [ #:aud audience #:iss expected-issuer #:clock-skew skew]) → (Option VerifiedJWT) jwt : String algorithm : String secret : String audience : (Option String) = #f expected-issuer : (Option String) = #f skew : Exact-Nonnegative-Integer = 30
> (equal? (decode/verify compact-jwt1 "HS256" "swordfish") v-jwt1) - : Boolean
#t
4 Algorithms: Signing and Verifying
(require net/jwt/algorithms) | package: net-jwt |
struct
type
procedure
(ok-signature? sig secret message [sign]) → Boolean
sig : String secret : String message : String sign : SigningFunction = hs256
procedure
(supported? algorithm-name) → Boolean
algorithm-name : String
procedure
(signing-function algorithm-name) → (Option SigningFunction)
algorithm-name : String
5 Base-64 URL Encoding
(require net/jwt/base64) | package: net-jwt |
procedure
(base64-url-encode bs) → String
bs : Bytes
"-" and "+" are replaced by "_" and "/", respectively,
whitespace is removed, and
the end is padded with "=" if needed.
procedure
(base64-url-decode s) → (Option Bytes)
s : String
Bibliography
[RFC4648] | S. Josefsson, “The Base16, Base32, and Base64 Data Encodings,” RFC, 1987. http://tools.ietf.org/html/rfc4648 | |
[RFC7515] | M. Jones, J. Bradley, and N. Sakimura, “JSON Web Signature (JWS),” RFC, 1987. http://tools.ietf.org/html/rfc7515 | |
[RFC7516] | M. Jones and J. Hildebrand, “JSON Web Encryption (JWE),” RFC, 1987. http://tools.ietf.org/html/rfc7516 | |
[RFC7518] | M. Jones, “JSON Web Algorithms (JWA),” RFC, 1987. http://tools.ietf.org/html/rfc7518 | |
[RFC7519] | M. Jones, J. Bradley, and N. Sakimura, “JSON Web Token (JWT),” RFC, 1987. http://tools.ietf.org/html/rfc7519 |