JSON Web Token (JWT) and JSON Web Signature (JWS)
1 JWTs as Racket data
JSXHash
JWT
Verified  JWT
JWT?
Verified  JWT?
header
signature
issuer
subject
audiences
expiration-date
not-before
issued-at
jwt-id
claims-ref
2 Encoding and signing JWTs
encode/  sign
encode-jwt
3 Decoding JWTs (Compact JSON Serialization)
decode-jwt
verify-jwt
decode/  verify
4 Algorithms:   Signing and Verifying
exn:  fail:  unsupported-algorithm
Signing  Function
none
hs256
ok-signature?
supported?
signing-function
5 Base-64 URL Encoding
base64-url-encode
base64-url-decode
Bibliography
6.12

JSON Web Token (JWT) and JSON Web Signature (JWS)

Jordan Johnson <[email protected]>

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].

 (require net/jwt) package: net-jwt
Provides functions for decoding and verifying tokens using the Compact JWS Serialization.

1 JWTs as Racket data

Alias for (HashTable Symbol JSExpr), for use in encoding and signing tokens.

type

JWT

Represents an unverified JWT. This is the result of decoding a JWT without checking the signature.

Subtype of JWT, for which the JWT’s signature has been verified.

procedure

(JWT? x)  Boolean

  x : Any

procedure

(VerifiedJWT? x)  Boolean

  x : Any
Type predicates for JWT and VerifiedJWT data.

JWTs support the following accessors, which correspond to the JWT claim names specified in RFC 7519 [RFC7519].

procedure

(header jwt)  JSXHash

  jwt : JWT
Produces the JWT’s header as a hashtable.

procedure

(signature jwt)  String

  jwt : JWT
Produces the signature that was presented with the JWT.

procedure

(issuer jwt)  (Option String)

  jwt : JWT
Produces an identifier for the entity that issued the JWT, or #f if unspecified by the JWT.

procedure

(subject jwt)  (Option String)

  jwt : JWT
Produces a string identifying the principal that is the subject of jwt, or #f if unspecified by the JWT.

procedure

(audiences jwt)  (Listof String)

  jwt : JWT
Produces a (possibly empty) list of identifiers for the intended audience(s) for this JWT.

procedure

(expiration-date jwt)  (Option date)

  jwt : JWT
Produces the date/time (as a date struct) after which jwt should no longer be accepted, or #f if jwt carries no expiration date.

procedure

(not-before jwt)  (Option date)

  jwt : JWT
Produces a date/time before which jwt should not be considered valid, or #f if jwt does not specify such a time.

procedure

(issued-at jwt)  (Option date)

  jwt : JWT
Produces the date/time at which jwt was produced by its issuer, or #f if jwt does not carry that information.

procedure

(jwt-id jwt)  (Option String)

  jwt : JWT
Produces the JWT ID for jwt, if one is present, or #f otherwise.

procedure

(claims-ref jwt key)  (Option JSExpr)

  jwt : JWT
  key : Symbol
Looks up key in the claims presented by jwt, where key is some symbol other than the ones defined by RFC 7515 [RFC7515]; that is, some symbol other than iss, sub, aud, exp, nbf, iat, and jti. Produces #f if key is not present in the claims.

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)
Encodes a JWT using the Compact JSON Serialization, signing it using the given secret and algorithm. Any data passed in headers will become a part of the JWT’s header (along with algorithm, for the alg key); any data passed in other will become a part of the payload; and all other keyword parameters are for passing values for the claims defined by the JWS RFC [RFC7515].

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.

Examples:
> (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

"eyJhbGciOiJIUzI1NiIsImtpZCI6IjEyMzR4YnpzZmdkNTQzMjEifQ.eyJpYXQiOjE1MjM5Njc4OTksImlzcyI6Imh0dHA6Ly9mZWxsb3dodW1hbi5jb20vIiwiZXhwIjoxNTI0MDU0Mjk5LCJhdWQiOiJodHRwOi8vZXhhbXBsZS5jb20vIiwic3ViIjoiam1qIiwidWlkIjoxMjM0NX0.PGFjBzA9NY1b31oY7uA-YMg9kwr6jFN5MeWSte024b8"

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)
Encodes an unsecured JWT as a string using the Compact JSON Serialization; the resulting string does not contain a signature, and the alg header will contain "none". Equivalent to (encode/sign "none" "" ...).

Examples:
> (define compact-jwt2
    (encode-jwt #:iss "http://example.com/"
                #:sub "user12345"
                #:aud '("http://fellowhuman.com/"
                        "http://www.fellowhuman.com/")))
> compact-jwt2

- : String

"eyJhbGciOiJub25lIn0.eyJpc3MiOiJodHRwOi8vZXhhbXBsZS5jb20vIiwiYXVkIjpbImh0dHA6Ly9mZWxsb3dodW1hbi5jb20vIiwiaHR0cDovL3d3dy5mZWxsb3dodW1hbi5jb20vIl0sInN1YiI6InVzZXIxMjM0NSJ9."

3 Decoding JWTs (Compact JSON Serialization)

procedure

(decode-jwt jwt)  (Option JWT)

  jwt : String
Decodes the given Compact JWS Serialization, producing an unverified JWT.

Examples:
> (define uv-jwt1
    (let ([decoded (decode-jwt compact-jwt1)])
      (if decoded decoded (error "couldn't decode"))))
> (JWT? uv-jwt1)

- : Boolean [more precisely: True]

#t

> (VerifiedJWT? uv-jwt1)

- : Boolean

#f

> (header uv-jwt1)

- : JSXHash

'#hasheq((alg . "HS256") (kid . "1234xbzsfgd54321"))

> (issuer uv-jwt1)

- : (U False String)

"http://fellowhuman.com/"

> (subject uv-jwt1)

- : (U False String)

"jmj"

> (expiration-date uv-jwt1)

- : (U False date)

(date* 59 24 6 18 4 2018 3 107 #t -21600 0 "MDT")

> (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
Checks a decoded JWT to verify that the signature can be verified using the given algorithm and secret.

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.

This procedure produces #f if for any reason the JWT can’t be verified. This includes when the given algorithm or the JWT’s algorithm is "none"; in that circumstance the JWT is unsecured and can’t truthfully be said to be verified.

Examples:
> (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"

Note that the aud and iss checks won’t be performed unless the #:aud and #:iss keyword arguments are present, although the JWT will pass verification nonetheless:

Example:
> (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
Decodes and verifies a JWS compact serialization. Checks the signature using algorithm, and produces a verified JWT if possible. The keyword parameters are interpreted as in verify-jwt, and like that function, this one produces #f if the JWT fails decoding or verification for any reason (including when the JWT’s algorithm is "none").

Example:
> (equal? (decode/verify compact-jwt1 "HS256" "swordfish")
          v-jwt1)

- : Boolean

#t

4 Algorithms: Signing and Verifying

Provides functions related to signing JWTs and verifying JWT signatures. Currently the only supported algorithms are HMAC-SHA256, via hs256, and the no-op algorithm none (see RFC7515 Appendix A.5 [RFC7515]). Any additional algorithms that may be implemented in future will be accessible via a SigningFunction defined in this module.
All of the names listed for this module are also exported by net/jwt.

Exception indicating that encoding cannot proceed because the requested algorithm is not supported.

Represents a signing function, which takes two strings (a secret and a message) and produces a byte string representing a message signature.

procedure

(none secret message)  Bytes

  secret : String
  message : String

procedure

(hs256 secret message)  Bytes

  secret : String
  message : String
A SigningFunction for the HMAC-SHA256 algorithm.

procedure

(ok-signature? sig secret message [sign])  Boolean

  sig : String
  secret : String
  message : String
  sign : SigningFunction = hs256
Produces true iff the given message produces the given signature sig when signed with sign using the given secret.

procedure

(supported? algorithm-name)  Boolean

  algorithm-name : String
Produces true iff the algorithm with the given alg Header Parameter name, defined in RFC 7518 [RFC7518], is supported. Currently only "HS256" is supported.

procedure

(signing-function algorithm-name)  (Option SigningFunction)

  algorithm-name : String
Produces a SigningFunction if algorithm-name (again, an RFC 7518 [RFC7518] Header Parameter name) is supported, #f otherwise.

5 Base-64 URL Encoding

Provides functions for the URL-safe base-64 encoding/decoding required by RFC 7515 [RFC7515], as described in Section 5 of RFC 4648 [RFC4648]. These functions are used internally and by net/jwt/algorithms, but are not likely necessary for client code using the net/jwt library.

procedure

(base64-url-encode bs)  String

  bs : Bytes
Base64-URL-encodes the byte string, differing from net/base64’s encoding as follows:
  • "-" 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
Decodes a string s that was encoded by a process equivalent to base64-url-encode. Produces #f if s is not a valid encoding.

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