···1+ISC License
2+3+Copyright (c) 2026 Anil Madhavapeddy <anil@recoil.org>
4+5+Permission to use, copy, modify, and distribute this software for any
6+purpose with or without fee is hereby granted, provided that the above
7+copyright notice and this permission notice appear in all copies.
8+9+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
···1+(** JSON Web Token (JWT) - RFC 7519 *)
2+3+(* Error types *)
4+type error =
5+ | Invalid_json of string
6+ | Invalid_base64url of string
7+ | Invalid_structure of string
8+ | Invalid_header of string
9+ | Invalid_claims of string
10+ | Invalid_uri of string
11+ | Duplicate_claim of string
12+ | Unsupported_algorithm of string
13+ | Algorithm_not_allowed of string
14+ | Signature_mismatch
15+ | Token_expired
16+ | Token_not_yet_valid
17+ | Invalid_issuer
18+ | Invalid_audience
19+ | Key_type_mismatch of string
20+ | Unsecured_not_allowed
21+ | Nesting_too_deep
22+23+let pp_error fmt = function
24+ | Invalid_json s -> Format.fprintf fmt "Invalid JSON: %s" s
25+ | Invalid_base64url s -> Format.fprintf fmt "Invalid base64url: %s" s
26+ | Invalid_structure s -> Format.fprintf fmt "Invalid structure: %s" s
27+ | Invalid_header s -> Format.fprintf fmt "Invalid header: %s" s
28+ | Invalid_claims s -> Format.fprintf fmt "Invalid claims: %s" s
29+ | Invalid_uri s -> Format.fprintf fmt "Invalid URI: %s" s
30+ | Duplicate_claim s -> Format.fprintf fmt "Duplicate claim: %s" s
31+ | Unsupported_algorithm s -> Format.fprintf fmt "Unsupported algorithm: %s" s
32+ | Algorithm_not_allowed s -> Format.fprintf fmt "Algorithm not allowed: %s" s
33+ | Signature_mismatch -> Format.fprintf fmt "Signature mismatch"
34+ | Token_expired -> Format.fprintf fmt "Token expired"
35+ | Token_not_yet_valid -> Format.fprintf fmt "Token not yet valid"
36+ | Invalid_issuer -> Format.fprintf fmt "Invalid issuer"
37+ | Invalid_audience -> Format.fprintf fmt "Invalid audience"
38+ | Key_type_mismatch s -> Format.fprintf fmt "Key type mismatch: %s" s
39+ | Unsecured_not_allowed -> Format.fprintf fmt "Unsecured JWT not allowed"
40+ | Nesting_too_deep -> Format.fprintf fmt "Nested JWT too deep"
41+42+let error_to_string e =
43+ Format.asprintf "%a" pp_error e
44+45+(* Base64url encoding/decoding per RFC 7515 Appendix C *)
46+let base64url_encode s =
47+ Base64.encode_string ~pad:false ~alphabet:Base64.uri_safe_alphabet s
48+49+let base64url_decode s =
50+ (* Add padding if needed *)
51+ let len = String.length s in
52+ let pad_len = (4 - (len mod 4)) mod 4 in
53+ let padded = s ^ String.make pad_len '=' in
54+ match Base64.decode ~alphabet:Base64.uri_safe_alphabet padded with
55+ | Ok v -> Ok v
56+ | Error (`Msg m) -> Error (Invalid_base64url m)
57+58+(* StringOrURI validation per RFC 7519 Section 2 *)
59+let validate_string_or_uri s =
60+ if String.contains s ':' then
61+ (* Must be a valid URI - basic check for scheme *)
62+ match String.index_opt s ':' with
63+ | Some i when i > 0 ->
64+ let scheme = String.sub s 0 i in
65+ (* Check scheme is alphanumeric with +.- allowed after first char *)
66+ let valid_scheme =
67+ String.length scheme > 0 &&
68+ (match scheme.[0] with 'a'..'z' | 'A'..'Z' -> true | _ -> false) &&
69+ String.for_all (fun c ->
70+ match c with
71+ | 'a'..'z' | 'A'..'Z' | '0'..'9' | '+' | '-' | '.' -> true
72+ | _ -> false
73+ ) scheme
74+ in
75+ if valid_scheme then Ok s
76+ else Error (Invalid_uri (Printf.sprintf "Invalid URI scheme in: %s" s))
77+ | _ -> Error (Invalid_uri (Printf.sprintf "Invalid URI: %s" s))
78+ else
79+ Ok s
80+81+(* Algorithm module *)
82+module Algorithm = struct
83+ type t =
84+ | None
85+ | HS256
86+ | HS384
87+ | HS512
88+ | RS256
89+ | RS384
90+ | RS512
91+ | ES256
92+ | ES384
93+ | ES512
94+ | EdDSA
95+96+ let to_string = function
97+ | None -> "none"
98+ | HS256 -> "HS256"
99+ | HS384 -> "HS384"
100+ | HS512 -> "HS512"
101+ | RS256 -> "RS256"
102+ | RS384 -> "RS384"
103+ | RS512 -> "RS512"
104+ | ES256 -> "ES256"
105+ | ES384 -> "ES384"
106+ | ES512 -> "ES512"
107+ | EdDSA -> "EdDSA"
108+109+ let of_string = function
110+ | "none" -> Ok None
111+ | "HS256" -> Ok HS256
112+ | "HS384" -> Ok HS384
113+ | "HS512" -> Ok HS512
114+ | "RS256" -> Ok RS256
115+ | "RS384" -> Ok RS384
116+ | "RS512" -> Ok RS512
117+ | "ES256" -> Ok ES256
118+ | "ES384" -> Ok ES384
119+ | "ES512" -> Ok ES512
120+ | "EdDSA" -> Ok EdDSA
121+ | s -> Error (Unsupported_algorithm s)
122+123+ let all = [ HS256; HS384; HS512; RS256; RS384; RS512; ES256; ES384; ES512; EdDSA ]
124+ let all_with_none = None :: all
125+end
126+127+(* JWK module *)
128+module Jwk = struct
129+ type kty = Oct | Rsa | Ec | Okp
130+131+ type crv = P256 | P384 | P521 | Ed25519
132+133+ type key_data =
134+ | Symmetric of { k : string }
135+ | Ed25519_pub of { x : string }
136+ | Ed25519_priv of { x : string; d : string }
137+ | P256_pub of { x : string; y : string }
138+ | P256_priv of { x : string; y : string; d : string }
139+ | P384_pub of { x : string; y : string }
140+ | P384_priv of { x : string; y : string; d : string }
141+ | P521_pub of { x : string; y : string }
142+ | P521_priv of { x : string; y : string; d : string }
143+ | Rsa_pub of { n : string; e : string }
144+ | Rsa_priv of {
145+ n : string;
146+ e : string;
147+ d : string;
148+ p : string;
149+ q : string;
150+ dp : string;
151+ dq : string;
152+ qi : string;
153+ }
154+155+ type t = {
156+ key_data : key_data;
157+ kid : string option;
158+ alg : Algorithm.t option;
159+ }
160+161+ let symmetric k = { key_data = Symmetric { k }; kid = None; alg = None }
162+163+ let ed25519_pub x =
164+ { key_data = Ed25519_pub { x }; kid = None; alg = Some Algorithm.EdDSA }
165+166+ let ed25519_priv ~pub ~priv =
167+ { key_data = Ed25519_priv { x = pub; d = priv }; kid = None; alg = Some Algorithm.EdDSA }
168+169+ let p256_pub ~x ~y =
170+ { key_data = P256_pub { x; y }; kid = None; alg = Some Algorithm.ES256 }
171+172+ let p256_priv ~x ~y ~d =
173+ { key_data = P256_priv { x; y; d }; kid = None; alg = Some Algorithm.ES256 }
174+175+ let p384_pub ~x ~y =
176+ { key_data = P384_pub { x; y }; kid = None; alg = Some Algorithm.ES384 }
177+178+ let p384_priv ~x ~y ~d =
179+ { key_data = P384_priv { x; y; d }; kid = None; alg = Some Algorithm.ES384 }
180+181+ let p521_pub ~x ~y =
182+ { key_data = P521_pub { x; y }; kid = None; alg = Some Algorithm.ES512 }
183+184+ let p521_priv ~x ~y ~d =
185+ { key_data = P521_priv { x; y; d }; kid = None; alg = Some Algorithm.ES512 }
186+187+ let rsa_pub ~n ~e =
188+ { key_data = Rsa_pub { n; e }; kid = None; alg = Some Algorithm.RS256 }
189+190+ let rsa_priv ~n ~e ~d ~p ~q ~dp ~dq ~qi =
191+ { key_data = Rsa_priv { n; e; d; p; q; dp; dq; qi }; kid = None; alg = Some Algorithm.RS256 }
192+193+ let kty t =
194+ match t.key_data with
195+ | Symmetric _ -> Oct
196+ | Ed25519_pub _ | Ed25519_priv _ -> Okp
197+ | P256_pub _ | P256_priv _ | P384_pub _ | P384_priv _ | P521_pub _ | P521_priv _ -> Ec
198+ | Rsa_pub _ | Rsa_priv _ -> Rsa
199+200+ let kid t = t.kid
201+ let alg t = t.alg
202+203+ let with_kid id t = { t with kid = Some id }
204+ let with_alg a t = { t with alg = Some a }
205+206+ (* Helper to extract string from Jsont.json object members *)
207+ let get_json_string members name =
208+ List.find_map (fun ((n, _), v) ->
209+ if n = name then
210+ match v with
211+ | Jsont.String (s, _) -> Some s
212+ | _ -> None
213+ else None
214+ ) members
215+216+ let get_json_string_req members name =
217+ match get_json_string members name with
218+ | Some s -> Ok s
219+ | None -> Error (Invalid_json (Printf.sprintf "missing required field: %s" name))
220+221+ let of_json s =
222+ (* Parse the JSON to determine key type first *)
223+ match Jsont_bytesrw.decode_string Jsont.json s with
224+ | Error e -> Error (Invalid_json e)
225+ | Ok (Jsont.Null _) -> Error (Invalid_json "null is not a valid JWK")
226+ | Ok (Jsont.Object (members, _)) ->
227+ let ( let* ) = Result.bind in
228+ let* kty_s = get_json_string_req members "kty" in
229+ let kid = get_json_string members "kid" in
230+ let alg_opt =
231+ match get_json_string members "alg" with
232+ | None -> Ok None
233+ | Some s ->
234+ match Algorithm.of_string s with
235+ | Ok a -> Ok (Some a)
236+ | Error _ -> Ok None (* ignore unknown alg in JWK *)
237+ in
238+ let* alg = alg_opt in
239+ (match kty_s with
240+ | "oct" ->
241+ let* k_b64 = get_json_string_req members "k" in
242+ let* k = base64url_decode k_b64 in
243+ Ok { key_data = Symmetric { k }; kid; alg }
244+ | "OKP" ->
245+ let* crv = get_json_string_req members "crv" in
246+ if crv <> "Ed25519" then
247+ Error (Invalid_json (Printf.sprintf "unsupported curve: %s" crv))
248+ else
249+ let* x_b64 = get_json_string_req members "x" in
250+ let* x = base64url_decode x_b64 in
251+ (match get_json_string members "d" with
252+ | None -> Ok { key_data = Ed25519_pub { x }; kid; alg }
253+ | Some d_b64 ->
254+ let* d = base64url_decode d_b64 in
255+ Ok { key_data = Ed25519_priv { x; d }; kid; alg })
256+ | "EC" ->
257+ let* crv = get_json_string_req members "crv" in
258+ let* x_b64 = get_json_string_req members "x" in
259+ let* y_b64 = get_json_string_req members "y" in
260+ let* x = base64url_decode x_b64 in
261+ let* y = base64url_decode y_b64 in
262+ let has_d = Option.is_some (get_json_string members "d") in
263+ let get_d () =
264+ let* d_b64 = get_json_string_req members "d" in
265+ base64url_decode d_b64
266+ in
267+ (match crv with
268+ | "P-256" ->
269+ if has_d then
270+ let* d = get_d () in
271+ Ok { key_data = P256_priv { x; y; d }; kid; alg }
272+ else
273+ Ok { key_data = P256_pub { x; y }; kid; alg }
274+ | "P-384" ->
275+ if has_d then
276+ let* d = get_d () in
277+ Ok { key_data = P384_priv { x; y; d }; kid; alg }
278+ else
279+ Ok { key_data = P384_pub { x; y }; kid; alg }
280+ | "P-521" ->
281+ if has_d then
282+ let* d = get_d () in
283+ Ok { key_data = P521_priv { x; y; d }; kid; alg }
284+ else
285+ Ok { key_data = P521_pub { x; y }; kid; alg }
286+ | _ -> Error (Invalid_json (Printf.sprintf "unsupported curve: %s" crv)))
287+ | "RSA" ->
288+ let* n_b64 = get_json_string_req members "n" in
289+ let* e_b64 = get_json_string_req members "e" in
290+ let* n = base64url_decode n_b64 in
291+ let* e = base64url_decode e_b64 in
292+ (match get_json_string members "d" with
293+ | None -> Ok { key_data = Rsa_pub { n; e }; kid; alg }
294+ | Some d_b64 ->
295+ let* d = base64url_decode d_b64 in
296+ let* p_b64 = get_json_string_req members "p" in
297+ let* q_b64 = get_json_string_req members "q" in
298+ let* dp_b64 = get_json_string_req members "dp" in
299+ let* dq_b64 = get_json_string_req members "dq" in
300+ let* qi_b64 = get_json_string_req members "qi" in
301+ let* p = base64url_decode p_b64 in
302+ let* q = base64url_decode q_b64 in
303+ let* dp = base64url_decode dp_b64 in
304+ let* dq = base64url_decode dq_b64 in
305+ let* qi = base64url_decode qi_b64 in
306+ Ok { key_data = Rsa_priv { n; e; d; p; q; dp; dq; qi }; kid; alg })
307+ | _ -> Error (Invalid_json (Printf.sprintf "unsupported kty: %s" kty_s)))
308+ | Ok _ -> Error (Invalid_json "JWK must be a JSON object")
309+310+ (* Helper to create JSON members *)
311+ let meta = Jsont.Meta.none
312+ let json_string s = Jsont.String (s, meta)
313+ let json_mem name value = ((name, meta), value)
314+315+ let to_json t =
316+ let add_opt name v_opt acc =
317+ match v_opt with
318+ | None -> acc
319+ | Some v -> json_mem name (json_string v) :: acc
320+ in
321+ let members = [] in
322+ let members = add_opt "kid" t.kid members in
323+ let members = add_opt "alg" (Option.map Algorithm.to_string t.alg) members in
324+ let members =
325+ match t.key_data with
326+ | Symmetric { k } ->
327+ json_mem "kty" (json_string "oct") ::
328+ json_mem "k" (json_string (base64url_encode k)) :: members
329+ | Ed25519_pub { x } ->
330+ json_mem "kty" (json_string "OKP") ::
331+ json_mem "crv" (json_string "Ed25519") ::
332+ json_mem "x" (json_string (base64url_encode x)) :: members
333+ | Ed25519_priv { x; d } ->
334+ json_mem "kty" (json_string "OKP") ::
335+ json_mem "crv" (json_string "Ed25519") ::
336+ json_mem "x" (json_string (base64url_encode x)) ::
337+ json_mem "d" (json_string (base64url_encode d)) :: members
338+ | P256_pub { x; y } ->
339+ json_mem "kty" (json_string "EC") ::
340+ json_mem "crv" (json_string "P-256") ::
341+ json_mem "x" (json_string (base64url_encode x)) ::
342+ json_mem "y" (json_string (base64url_encode y)) :: members
343+ | P256_priv { x; y; d } ->
344+ json_mem "kty" (json_string "EC") ::
345+ json_mem "crv" (json_string "P-256") ::
346+ json_mem "x" (json_string (base64url_encode x)) ::
347+ json_mem "y" (json_string (base64url_encode y)) ::
348+ json_mem "d" (json_string (base64url_encode d)) :: members
349+ | P384_pub { x; y } ->
350+ json_mem "kty" (json_string "EC") ::
351+ json_mem "crv" (json_string "P-384") ::
352+ json_mem "x" (json_string (base64url_encode x)) ::
353+ json_mem "y" (json_string (base64url_encode y)) :: members
354+ | P384_priv { x; y; d } ->
355+ json_mem "kty" (json_string "EC") ::
356+ json_mem "crv" (json_string "P-384") ::
357+ json_mem "x" (json_string (base64url_encode x)) ::
358+ json_mem "y" (json_string (base64url_encode y)) ::
359+ json_mem "d" (json_string (base64url_encode d)) :: members
360+ | P521_pub { x; y } ->
361+ json_mem "kty" (json_string "EC") ::
362+ json_mem "crv" (json_string "P-521") ::
363+ json_mem "x" (json_string (base64url_encode x)) ::
364+ json_mem "y" (json_string (base64url_encode y)) :: members
365+ | P521_priv { x; y; d } ->
366+ json_mem "kty" (json_string "EC") ::
367+ json_mem "crv" (json_string "P-521") ::
368+ json_mem "x" (json_string (base64url_encode x)) ::
369+ json_mem "y" (json_string (base64url_encode y)) ::
370+ json_mem "d" (json_string (base64url_encode d)) :: members
371+ | Rsa_pub { n; e } ->
372+ json_mem "kty" (json_string "RSA") ::
373+ json_mem "n" (json_string (base64url_encode n)) ::
374+ json_mem "e" (json_string (base64url_encode e)) :: members
375+ | Rsa_priv { n; e; d; p; q; dp; dq; qi } ->
376+ json_mem "kty" (json_string "RSA") ::
377+ json_mem "n" (json_string (base64url_encode n)) ::
378+ json_mem "e" (json_string (base64url_encode e)) ::
379+ json_mem "d" (json_string (base64url_encode d)) ::
380+ json_mem "p" (json_string (base64url_encode p)) ::
381+ json_mem "q" (json_string (base64url_encode q)) ::
382+ json_mem "dp" (json_string (base64url_encode dp)) ::
383+ json_mem "dq" (json_string (base64url_encode dq)) ::
384+ json_mem "qi" (json_string (base64url_encode qi)) :: members
385+ in
386+ match Jsont_bytesrw.encode_string Jsont.json (Jsont.Object (members, meta)) with
387+ | Ok s -> s
388+ | Error _ -> "{}" (* Should not happen *)
389+end
390+391+(* Header module *)
392+module Header = struct
393+ type t = {
394+ alg : Algorithm.t;
395+ typ : string option;
396+ kid : string option;
397+ cty : string option;
398+ }
399+400+ let make ?typ ?kid ?cty alg = { alg; typ; kid; cty }
401+402+ let is_nested t =
403+ match t.cty with
404+ | Some s -> String.uppercase_ascii s = "JWT"
405+ | None -> false
406+407+ (* Helper to extract string from Jsont.json object members *)
408+ let get_json_string members name =
409+ List.find_map (fun ((n, _), v) ->
410+ if n = name then
411+ match v with
412+ | Jsont.String (s, _) -> Some s
413+ | _ -> None
414+ else None
415+ ) members
416+417+ let of_json s =
418+ match Jsont_bytesrw.decode_string Jsont.json s with
419+ | Error e -> Error (Invalid_json e)
420+ | Ok (Jsont.Null _) -> Error (Invalid_header "null is not a valid header")
421+ | Ok (Jsont.Object (members, _)) ->
422+ let ( let* ) = Result.bind in
423+ let alg_s = get_json_string members "alg" in
424+ (match alg_s with
425+ | None -> Error (Invalid_header "missing required 'alg' field")
426+ | Some alg_str ->
427+ let* alg = Algorithm.of_string alg_str in
428+ let typ = get_json_string members "typ" in
429+ let kid = get_json_string members "kid" in
430+ let cty = get_json_string members "cty" in
431+ Ok { alg; typ; kid; cty })
432+ | Ok _ -> Error (Invalid_header "header must be a JSON object")
433+434+ let meta = Jsont.Meta.none
435+ let json_string s = Jsont.String (s, meta)
436+ let json_mem name value = ((name, meta), value)
437+438+ let to_json h =
439+ let members = [ json_mem "alg" (json_string (Algorithm.to_string h.alg)) ] in
440+ let add_opt name v_opt acc =
441+ match v_opt with
442+ | None -> acc
443+ | Some v -> json_mem name (json_string v) :: acc
444+ in
445+ let members = add_opt "typ" h.typ members in
446+ let members = add_opt "kid" h.kid members in
447+ let members = add_opt "cty" h.cty members in
448+ match Jsont_bytesrw.encode_string Jsont.json (Jsont.Object (List.rev members, meta)) with
449+ | Ok s -> s
450+ | Error _ -> "{}"
451+end
452+453+(* Claims module *)
454+module Claims = struct
455+ type t = {
456+ iss : string option;
457+ sub : string option;
458+ aud : string list;
459+ exp : Ptime.t option;
460+ nbf : Ptime.t option;
461+ iat : Ptime.t option;
462+ jti : string option;
463+ custom : (string * Jsont.json) list;
464+ }
465+466+ let iss t = t.iss
467+ let sub t = t.sub
468+ let aud t = t.aud
469+ let exp t = t.exp
470+ let nbf t = t.nbf
471+ let iat t = t.iat
472+ let jti t = t.jti
473+474+ let get name t = List.assoc_opt name t.custom
475+476+ let get_string name t =
477+ match get name t with
478+ | Some (Jsont.String (s, _)) -> Some s
479+ | _ -> None
480+481+ let get_int name t =
482+ match get name t with
483+ | Some (Jsont.Number (n, _)) -> (try Some (int_of_float n) with _ -> None)
484+ | _ -> None
485+486+ let get_bool name t =
487+ match get name t with
488+ | Some (Jsont.Bool (b, _)) -> Some b
489+ | _ -> None
490+491+ let meta = Jsont.Meta.none
492+ let json_string s = Jsont.String (s, meta)
493+ let json_number n = Jsont.Number (n, meta)
494+ let json_bool b = Jsont.Bool (b, meta)
495+ let json_mem name value = ((name, meta), value)
496+497+ type builder = t
498+499+ let empty = {
500+ iss = None;
501+ sub = None;
502+ aud = [];
503+ exp = None;
504+ nbf = None;
505+ iat = None;
506+ jti = None;
507+ custom = [];
508+ }
509+510+ let set_iss v t = { t with iss = Some v }
511+ let set_sub v t = { t with sub = Some v }
512+ let set_aud v t = { t with aud = v }
513+ let set_exp v t = { t with exp = Some v }
514+ let set_nbf v t = { t with nbf = Some v }
515+ let set_iat v t = { t with iat = Some v }
516+ let set_jti v t = { t with jti = Some v }
517+ let set name value t = { t with custom = (name, value) :: t.custom }
518+ let set_string name value t = set name (json_string value) t
519+ let set_int name value t = set name (json_number (float_of_int value)) t
520+ let set_bool name value t = set name (json_bool value) t
521+ let build t = t
522+523+ let ptime_of_numeric_date n =
524+ let span = Ptime.Span.of_float_s n in
525+ Option.bind span (fun s -> Ptime.of_span s)
526+527+ let numeric_date_of_ptime t =
528+ Ptime.to_span t |> Ptime.Span.to_float_s
529+530+ (* Helper to extract values from Jsont.json object members *)
531+ let get_json_string members name =
532+ List.find_map (fun ((n, _), v) ->
533+ if n = name then
534+ match v with
535+ | Jsont.String (s, _) -> Some s
536+ | _ -> None
537+ else None
538+ ) members
539+540+ let get_json_number members name =
541+ List.find_map (fun ((n, _), v) ->
542+ if n = name then
543+ match v with
544+ | Jsont.Number (n, _) -> Some n
545+ | _ -> None
546+ else None
547+ ) members
548+549+ let get_json_aud members =
550+ List.find_map (fun ((n, _), v) ->
551+ if n = "aud" then
552+ match v with
553+ | Jsont.String (s, _) -> Some [ s ]
554+ | Jsont.Array (arr, _) ->
555+ Some (List.filter_map (function
556+ | Jsont.String (s, _) -> Some s
557+ | _ -> None
558+ ) arr)
559+ | _ -> None
560+ else None
561+ ) members |> Option.value ~default:[]
562+563+ let of_json ?(strict = true) s =
564+ match Jsont_bytesrw.decode_string Jsont.json s with
565+ | Error e -> Error (Invalid_json e)
566+ | Ok (Jsont.Null _) -> Error (Invalid_claims "null is not a valid claims set")
567+ | Ok (Jsont.Object (members, _)) ->
568+ let ( let* ) = Result.bind in
569+ (* Check for duplicates in strict mode *)
570+ let* () =
571+ if strict then
572+ let names = List.map (fun ((n, _), _) -> n) members in
573+ let rec check_dups = function
574+ | [] -> Ok ()
575+ | n :: rest ->
576+ if List.mem n rest then Error (Duplicate_claim n)
577+ else check_dups rest
578+ in
579+ check_dups names
580+ else Ok ()
581+ in
582+ (* Validate StringOrURI for iss and sub *)
583+ let* iss =
584+ match get_json_string members "iss" with
585+ | None -> Ok None
586+ | Some s ->
587+ let* _ = validate_string_or_uri s in
588+ Ok (Some s)
589+ in
590+ let* sub =
591+ match get_json_string members "sub" with
592+ | None -> Ok None
593+ | Some s ->
594+ let* _ = validate_string_or_uri s in
595+ Ok (Some s)
596+ in
597+ let exp = Option.bind (get_json_number members "exp") ptime_of_numeric_date in
598+ let nbf = Option.bind (get_json_number members "nbf") ptime_of_numeric_date in
599+ let iat = Option.bind (get_json_number members "iat") ptime_of_numeric_date in
600+ let jti = get_json_string members "jti" in
601+ let aud = get_json_aud members in
602+ (* Collect custom claims (everything not registered) *)
603+ let registered = [ "iss"; "sub"; "aud"; "exp"; "nbf"; "iat"; "jti" ] in
604+ let custom =
605+ List.filter_map (fun ((n, _), v) ->
606+ if List.mem n registered then None
607+ else Some (n, v)
608+ ) members
609+ in
610+ Ok { iss; sub; aud; exp; nbf; iat; jti; custom }
611+ | Ok _ -> Error (Invalid_claims "claims must be a JSON object")
612+613+ let to_json t =
614+ let members = [] in
615+ let add_string name v_opt acc =
616+ match v_opt with
617+ | None -> acc
618+ | Some v -> json_mem name (json_string v) :: acc
619+ in
620+ let add_time name v_opt acc =
621+ match v_opt with
622+ | None -> acc
623+ | Some v -> json_mem name (json_number (numeric_date_of_ptime v)) :: acc
624+ in
625+ let members = add_string "iss" t.iss members in
626+ let members = add_string "sub" t.sub members in
627+ let members =
628+ match t.aud with
629+ | [] -> members
630+ | [ single ] -> json_mem "aud" (json_string single) :: members
631+ | many ->
632+ let arr = List.map json_string many in
633+ json_mem "aud" (Jsont.Array (arr, meta)) :: members
634+ in
635+ let members = add_time "exp" t.exp members in
636+ let members = add_time "nbf" t.nbf members in
637+ let members = add_time "iat" t.iat members in
638+ let members = add_string "jti" t.jti members in
639+ let members =
640+ List.fold_left (fun acc (name, value) ->
641+ json_mem name value :: acc
642+ ) members t.custom
643+ in
644+ match Jsont_bytesrw.encode_string Jsont.json (Jsont.Object (List.rev members, meta)) with
645+ | Ok s -> s
646+ | Error _ -> "{}"
647+end
648+649+(* JWT type *)
650+type t = {
651+ header : Header.t;
652+ claims : Claims.t;
653+ signature : string;
654+ raw : string;
655+}
656+657+let header t = t.header
658+let claims t = t.claims
659+let signature t = t.signature
660+let raw t = t.raw
661+662+let is_nested t = Header.is_nested t.header
663+664+(* Parsing *)
665+let parse ?(strict = true) token =
666+ let ( let* ) = Result.bind in
667+ (* RFC 7519 Section 7.2 step 1: verify at least one period *)
668+ if not (String.contains token '.') then
669+ Error (Invalid_structure "JWT must contain at least one period character")
670+ else
671+ match String.split_on_char '.' token with
672+ | [ header_b64; payload_b64; sig_b64 ] ->
673+ (* JWS compact serialization: 3 parts *)
674+ let* header_json = base64url_decode header_b64 in
675+ let* payload_json = base64url_decode payload_b64 in
676+ let* signature = base64url_decode sig_b64 in
677+ let* header = Header.of_json header_json in
678+ let* claims = Claims.of_json ~strict payload_json in
679+ Ok { header; claims; signature; raw = token }
680+ | parts when List.length parts = 5 ->
681+ (* JWE compact serialization - not yet supported *)
682+ Error (Invalid_structure "JWE (encrypted JWT) not yet supported")
683+ | _ ->
684+ Error (Invalid_structure "JWT must have 3 parts (JWS) or 5 parts (JWE)")
685+686+let parse_unsafe = parse ~strict:false
687+688+let parse_nested ?(strict = true) ?(max_depth = 5) token =
689+ let ( let* ) = Result.bind in
690+ let rec loop depth acc tok =
691+ if depth > max_depth then
692+ Error Nesting_too_deep
693+ else
694+ let* jwt = parse ~strict tok in
695+ let acc = jwt :: acc in
696+ if is_nested jwt then
697+ (* The payload is another JWT - decode and parse it *)
698+ match String.split_on_char '.' tok with
699+ | [ _; payload_b64; _ ] ->
700+ let* inner_token = base64url_decode payload_b64 in
701+ loop (depth + 1) acc inner_token
702+ | _ -> Ok (List.rev acc)
703+ else
704+ Ok (List.rev acc)
705+ in
706+ loop 1 [] token
707+708+(* Signature operations *)
709+module Sign = struct
710+ let hmac_sha256 ~key data =
711+ let key = Cstruct.of_string key in
712+ let data = Cstruct.of_string data in
713+ Digestif.SHA256.hmac_string ~key:(Cstruct.to_string key) (Cstruct.to_string data)
714+ |> Digestif.SHA256.to_raw_string
715+716+ let hmac_sha384 ~key data =
717+ let key = Cstruct.of_string key in
718+ let data = Cstruct.of_string data in
719+ Digestif.SHA384.hmac_string ~key:(Cstruct.to_string key) (Cstruct.to_string data)
720+ |> Digestif.SHA384.to_raw_string
721+722+ let hmac_sha512 ~key data =
723+ let key = Cstruct.of_string key in
724+ let data = Cstruct.of_string data in
725+ Digestif.SHA512.hmac_string ~key:(Cstruct.to_string key) (Cstruct.to_string data)
726+ |> Digestif.SHA512.to_raw_string
727+728+ (* EdDSA signing using mirage-crypto-ec *)
729+ let ed25519_sign ~priv data =
730+ match Mirage_crypto_ec.Ed25519.priv_of_octets priv with
731+ | Error _ -> Error (Key_type_mismatch "Invalid Ed25519 private key")
732+ | Ok priv ->
733+ let sig_ = Mirage_crypto_ec.Ed25519.sign ~key:priv data in
734+ Ok sig_
735+736+ let ed25519_verify ~pub ~signature data =
737+ match Mirage_crypto_ec.Ed25519.pub_of_octets pub with
738+ | Error _ -> Error (Key_type_mismatch "Invalid Ed25519 public key")
739+ | Ok pub ->
740+ let valid = Mirage_crypto_ec.Ed25519.verify ~key:pub signature ~msg:data in
741+ if valid then Ok () else Error Signature_mismatch
742+743+ (* P-256 ECDSA *)
744+ let p256_sign ~priv data =
745+ match Mirage_crypto_ec.P256.Dsa.priv_of_octets priv with
746+ | Error _ -> Error (Key_type_mismatch "Invalid P-256 private key")
747+ | Ok priv ->
748+ let hash = Digestif.SHA256.digest_string data |> Digestif.SHA256.to_raw_string in
749+ let (r, s) = Mirage_crypto_ec.P256.Dsa.sign ~key:priv hash in
750+ (* JWS uses raw R||S format, each 32 bytes for P-256 *)
751+ (* Pad to 32 bytes each *)
752+ let pad32 s =
753+ let len = String.length s in
754+ if len >= 32 then String.sub s (len - 32) 32
755+ else String.make (32 - len) '\x00' ^ s
756+ in
757+ Ok (pad32 r ^ pad32 s)
758+759+ let p256_verify ~pub ~signature data =
760+ if String.length signature <> 64 then
761+ Error Signature_mismatch
762+ else
763+ let r = String.sub signature 0 32 in
764+ let s = String.sub signature 32 32 in
765+ match Mirage_crypto_ec.P256.Dsa.pub_of_octets pub with
766+ | Error _ -> Error (Key_type_mismatch "Invalid P-256 public key")
767+ | Ok pub ->
768+ let hash = Digestif.SHA256.digest_string data |> Digestif.SHA256.to_raw_string in
769+ let valid = Mirage_crypto_ec.P256.Dsa.verify ~key:pub (r, s) hash in
770+ if valid then Ok () else Error Signature_mismatch
771+772+ (* P-384 ECDSA *)
773+ let p384_sign ~priv data =
774+ match Mirage_crypto_ec.P384.Dsa.priv_of_octets priv with
775+ | Error _ -> Error (Key_type_mismatch "Invalid P-384 private key")
776+ | Ok priv ->
777+ let hash = Digestif.SHA384.digest_string data |> Digestif.SHA384.to_raw_string in
778+ let (r, s) = Mirage_crypto_ec.P384.Dsa.sign ~key:priv hash in
779+ let pad48 s =
780+ let len = String.length s in
781+ if len >= 48 then String.sub s (len - 48) 48
782+ else String.make (48 - len) '\x00' ^ s
783+ in
784+ Ok (pad48 r ^ pad48 s)
785+786+ let p384_verify ~pub ~signature data =
787+ if String.length signature <> 96 then
788+ Error Signature_mismatch
789+ else
790+ let r = String.sub signature 0 48 in
791+ let s = String.sub signature 48 48 in
792+ match Mirage_crypto_ec.P384.Dsa.pub_of_octets pub with
793+ | Error _ -> Error (Key_type_mismatch "Invalid P-384 public key")
794+ | Ok pub ->
795+ let hash = Digestif.SHA384.digest_string data |> Digestif.SHA384.to_raw_string in
796+ let valid = Mirage_crypto_ec.P384.Dsa.verify ~key:pub (r, s) hash in
797+ if valid then Ok () else Error Signature_mismatch
798+799+ (* P-521 ECDSA *)
800+ let p521_sign ~priv data =
801+ match Mirage_crypto_ec.P521.Dsa.priv_of_octets priv with
802+ | Error _ -> Error (Key_type_mismatch "Invalid P-521 private key")
803+ | Ok priv ->
804+ let hash = Digestif.SHA512.digest_string data |> Digestif.SHA512.to_raw_string in
805+ let (r, s) = Mirage_crypto_ec.P521.Dsa.sign ~key:priv hash in
806+ let pad66 s =
807+ let len = String.length s in
808+ if len >= 66 then String.sub s (len - 66) 66
809+ else String.make (66 - len) '\x00' ^ s
810+ in
811+ Ok (pad66 r ^ pad66 s)
812+813+ let p521_verify ~pub ~signature data =
814+ if String.length signature <> 132 then
815+ Error Signature_mismatch
816+ else
817+ let r = String.sub signature 0 66 in
818+ let s = String.sub signature 66 66 in
819+ match Mirage_crypto_ec.P521.Dsa.pub_of_octets pub with
820+ | Error _ -> Error (Key_type_mismatch "Invalid P-521 public key")
821+ | Ok pub ->
822+ let hash = Digestif.SHA512.digest_string data |> Digestif.SHA512.to_raw_string in
823+ let valid = Mirage_crypto_ec.P521.Dsa.verify ~key:pub (r, s) hash in
824+ if valid then Ok () else Error Signature_mismatch
825+826+ (* RSA PKCS#1 v1.5 - stub implementations *)
827+ (* TODO: Implement proper RSA signing/verification with JWK key parsing *)
828+ let _rsa_sign _hash_type ~priv:_ _data =
829+ Error (Key_type_mismatch "RSA signing not yet implemented")
830+831+ let _rsa_verify _hash_type ~pub:_ ~signature:_ _data =
832+ Error (Key_type_mismatch "RSA verification not yet implemented")
833+end
834+835+(* Get signing input from token *)
836+let signing_input token =
837+ match String.rindex_opt token '.' with
838+ | None -> token
839+ | Some i -> String.sub token 0 i
840+841+(* Verification *)
842+let verify ~key ?(allow_none = false) ?(allowed_algs = Algorithm.all) t =
843+ let ( let* ) = Result.bind in
844+ let alg = t.header.alg in
845+ let alg_str = Algorithm.to_string alg in
846+ (* Check if algorithm is allowed *)
847+ let* () =
848+ if alg = Algorithm.None then
849+ (* For alg:none, only allow_none flag matters *)
850+ if allow_none then Ok ()
851+ else Error Unsecured_not_allowed
852+ else if List.mem alg allowed_algs then Ok ()
853+ else Error (Algorithm_not_allowed alg_str)
854+ in
855+ let input = signing_input t.raw in
856+ match alg, key.Jwk.key_data with
857+ | Algorithm.None, _ ->
858+ (* Unsecured JWT - signature must be empty *)
859+ if t.signature = "" then Ok ()
860+ else Error Signature_mismatch
861+ | Algorithm.HS256, Jwk.Symmetric { k } ->
862+ let expected = Sign.hmac_sha256 ~key:k input in
863+ if Eqaf.equal expected t.signature then Ok ()
864+ else Error Signature_mismatch
865+ | Algorithm.HS384, Jwk.Symmetric { k } ->
866+ let expected = Sign.hmac_sha384 ~key:k input in
867+ if Eqaf.equal expected t.signature then Ok ()
868+ else Error Signature_mismatch
869+ | Algorithm.HS512, Jwk.Symmetric { k } ->
870+ let expected = Sign.hmac_sha512 ~key:k input in
871+ if Eqaf.equal expected t.signature then Ok ()
872+ else Error Signature_mismatch
873+ | Algorithm.EdDSA, Jwk.Ed25519_pub { x } ->
874+ Sign.ed25519_verify ~pub:x ~signature:t.signature input
875+ | Algorithm.EdDSA, Jwk.Ed25519_priv { x; d = _ } ->
876+ Sign.ed25519_verify ~pub:x ~signature:t.signature input
877+ | Algorithm.ES256, Jwk.P256_pub { x; y } ->
878+ let pub = x ^ y in (* Uncompressed point *)
879+ Sign.p256_verify ~pub ~signature:t.signature input
880+ | Algorithm.ES256, Jwk.P256_priv { x; y; d = _ } ->
881+ let pub = x ^ y in
882+ Sign.p256_verify ~pub ~signature:t.signature input
883+ | Algorithm.ES384, Jwk.P384_pub { x; y } ->
884+ let pub = x ^ y in
885+ Sign.p384_verify ~pub ~signature:t.signature input
886+ | Algorithm.ES384, Jwk.P384_priv { x; y; d = _ } ->
887+ let pub = x ^ y in
888+ Sign.p384_verify ~pub ~signature:t.signature input
889+ | Algorithm.ES512, Jwk.P521_pub { x; y } ->
890+ let pub = x ^ y in
891+ Sign.p521_verify ~pub ~signature:t.signature input
892+ | Algorithm.ES512, Jwk.P521_priv { x; y; d = _ } ->
893+ let pub = x ^ y in
894+ Sign.p521_verify ~pub ~signature:t.signature input
895+ | Algorithm.RS256, Jwk.Rsa_pub _ ->
896+ Error (Key_type_mismatch "RSA verification not yet implemented")
897+ | Algorithm.RS384, Jwk.Rsa_pub _ ->
898+ Error (Key_type_mismatch "RSA verification not yet implemented")
899+ | Algorithm.RS512, Jwk.Rsa_pub _ ->
900+ Error (Key_type_mismatch "RSA verification not yet implemented")
901+ | alg, _ ->
902+ Error (Key_type_mismatch
903+ (Printf.sprintf "Key type doesn't match algorithm %s" (Algorithm.to_string alg)))
904+905+(* Claims validation *)
906+let validate ~now ?iss ?aud ?(leeway = Ptime.Span.zero) t =
907+ let ( let* ) = Result.bind in
908+ let claims = t.claims in
909+ (* Check exp claim *)
910+ let* () =
911+ match Claims.exp claims with
912+ | None -> Ok ()
913+ | Some exp_time ->
914+ let exp_with_leeway = Ptime.add_span exp_time leeway |> Option.value ~default:exp_time in
915+ if Ptime.is_later now ~than:exp_with_leeway then
916+ Error Token_expired
917+ else Ok ()
918+ in
919+ (* Check nbf claim *)
920+ let* () =
921+ match Claims.nbf claims with
922+ | None -> Ok ()
923+ | Some nbf_time ->
924+ let nbf_with_leeway = Ptime.sub_span nbf_time leeway |> Option.value ~default:nbf_time in
925+ if Ptime.is_earlier now ~than:nbf_with_leeway then
926+ Error Token_not_yet_valid
927+ else Ok ()
928+ in
929+ (* Check iss claim *)
930+ let* () =
931+ match iss with
932+ | None -> Ok ()
933+ | Some expected_iss ->
934+ match Claims.iss claims with
935+ | None -> Error Invalid_issuer
936+ | Some actual_iss ->
937+ if String.equal expected_iss actual_iss then Ok ()
938+ else Error Invalid_issuer
939+ in
940+ (* Check aud claim *)
941+ let* () =
942+ match aud with
943+ | None -> Ok ()
944+ | Some expected_aud ->
945+ let actual_aud = Claims.aud claims in
946+ if List.mem expected_aud actual_aud then Ok ()
947+ else Error Invalid_audience
948+ in
949+ Ok ()
950+951+let verify_and_validate ~key ~now ?allow_none ?allowed_algs ?iss ?aud ?leeway t =
952+ let ( let* ) = Result.bind in
953+ let* () = verify ~key ?allow_none ?allowed_algs t in
954+ validate ~now ?iss ?aud ?leeway t
955+956+(* Creation *)
957+let create ~header ~claims ~key =
958+ let ( let* ) = Result.bind in
959+ let header_json = Header.to_json header in
960+ let claims_json = Claims.to_json claims in
961+ let header_b64 = base64url_encode header_json in
962+ let payload_b64 = base64url_encode claims_json in
963+ let signing_input = header_b64 ^ "." ^ payload_b64 in
964+ let* signature =
965+ match header.Header.alg, key.Jwk.key_data with
966+ | Algorithm.None, _ -> Ok ""
967+ | Algorithm.HS256, Jwk.Symmetric { k } ->
968+ Ok (Sign.hmac_sha256 ~key:k signing_input)
969+ | Algorithm.HS384, Jwk.Symmetric { k } ->
970+ Ok (Sign.hmac_sha384 ~key:k signing_input)
971+ | Algorithm.HS512, Jwk.Symmetric { k } ->
972+ Ok (Sign.hmac_sha512 ~key:k signing_input)
973+ | Algorithm.EdDSA, Jwk.Ed25519_priv { x = _; d } ->
974+ Sign.ed25519_sign ~priv:d signing_input
975+ | Algorithm.ES256, Jwk.P256_priv { x = _; y = _; d } ->
976+ Sign.p256_sign ~priv:d signing_input
977+ | Algorithm.ES384, Jwk.P384_priv { x = _; y = _; d } ->
978+ Sign.p384_sign ~priv:d signing_input
979+ | Algorithm.ES512, Jwk.P521_priv { x = _; y = _; d } ->
980+ Sign.p521_sign ~priv:d signing_input
981+ | alg, _ ->
982+ Error (Key_type_mismatch
983+ (Printf.sprintf "Cannot sign with algorithm %s and given key"
984+ (Algorithm.to_string alg)))
985+ in
986+ let sig_b64 = base64url_encode signature in
987+ let raw = signing_input ^ "." ^ sig_b64 in
988+ Ok { header; claims; signature; raw }
989+990+let encode t = t.raw
991+992+(* Utilities *)
993+let is_expired ~now ?(leeway = Ptime.Span.zero) t =
994+ match Claims.exp t.claims with
995+ | None -> false
996+ | Some exp_time ->
997+ let exp_with_leeway = Ptime.add_span exp_time leeway |> Option.value ~default:exp_time in
998+ Ptime.is_later now ~than:exp_with_leeway
999+1000+let time_to_expiry ~now t =
1001+ match Claims.exp t.claims with
1002+ | None -> None
1003+ | Some exp_time ->
1004+ let diff = Ptime.diff exp_time now in
1005+ if Ptime.Span.compare diff Ptime.Span.zero <= 0 then None
1006+ else Some diff
···1+2+3+4+5+6+7+Internet Engineering Task Force (IETF) M. Jones
8+Request for Comments: 7519 Microsoft
9+Category: Standards Track J. Bradley
10+ISSN: 2070-1721 Ping Identity
11+ N. Sakimura
12+ NRI
13+ May 2015
14+15+16+ JSON Web Token (JWT)
17+18+Abstract
19+20+ JSON Web Token (JWT) is a compact, URL-safe means of representing
21+ claims to be transferred between two parties. The claims in a JWT
22+ are encoded as a JSON object that is used as the payload of a JSON
23+ Web Signature (JWS) structure or as the plaintext of a JSON Web
24+ Encryption (JWE) structure, enabling the claims to be digitally
25+ signed or integrity protected with a Message Authentication Code
26+ (MAC) and/or encrypted.
27+28+Status of This Memo
29+30+ This is an Internet Standards Track document.
31+32+ This document is a product of the Internet Engineering Task Force
33+ (IETF). It represents the consensus of the IETF community. It has
34+ received public review and has been approved for publication by the
35+ Internet Engineering Steering Group (IESG). Further information on
36+ Internet Standards is available in Section 2 of RFC 5741.
37+38+ Information about the current status of this document, any errata,
39+ and how to provide feedback on it may be obtained at
40+ http://www.rfc-editor.org/info/rfc7519.
41+42+43+44+45+46+47+48+49+50+51+52+53+54+55+56+57+58+Jones, et al. Standards Track [Page 1]
59+60+RFC 7519 JSON Web Token (JWT) May 2015
61+62+63+Copyright Notice
64+65+ Copyright (c) 2015 IETF Trust and the persons identified as the
66+ document authors. All rights reserved.
67+68+ This document is subject to BCP 78 and the IETF Trust's Legal
69+ Provisions Relating to IETF Documents
70+ (http://trustee.ietf.org/license-info) in effect on the date of
71+ publication of this document. Please review these documents
72+ carefully, as they describe your rights and restrictions with respect
73+ to this document. Code Components extracted from this document must
74+ include Simplified BSD License text as described in Section 4.e of
75+ the Trust Legal Provisions and are provided without warranty as
76+ described in the Simplified BSD License.
77+78+79+80+81+82+83+84+85+86+87+88+89+90+91+92+93+94+95+96+97+98+99+100+101+102+103+104+105+106+107+108+109+110+111+112+113+114+Jones, et al. Standards Track [Page 2]
115+116+RFC 7519 JSON Web Token (JWT) May 2015
117+118+119+Table of Contents
120+121+ 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
122+ 1.1. Notational Conventions . . . . . . . . . . . . . . . . . 4
123+ 2. Terminology . . . . . . . . . . . . . . . . . . . . . . . . . 4
124+ 3. JSON Web Token (JWT) Overview . . . . . . . . . . . . . . . . 6
125+ 3.1. Example JWT . . . . . . . . . . . . . . . . . . . . . . . 7
126+ 4. JWT Claims . . . . . . . . . . . . . . . . . . . . . . . . . 8
127+ 4.1. Registered Claim Names . . . . . . . . . . . . . . . . . 9
128+ 4.1.1. "iss" (Issuer) Claim . . . . . . . . . . . . . . . . 9
129+ 4.1.2. "sub" (Subject) Claim . . . . . . . . . . . . . . . . 9
130+ 4.1.3. "aud" (Audience) Claim . . . . . . . . . . . . . . . 9
131+ 4.1.4. "exp" (Expiration Time) Claim . . . . . . . . . . . . 9
132+ 4.1.5. "nbf" (Not Before) Claim . . . . . . . . . . . . . . 10
133+ 4.1.6. "iat" (Issued At) Claim . . . . . . . . . . . . . . . 10
134+ 4.1.7. "jti" (JWT ID) Claim . . . . . . . . . . . . . . . . 10
135+ 4.2. Public Claim Names . . . . . . . . . . . . . . . . . . . 10
136+ 4.3. Private Claim Names . . . . . . . . . . . . . . . . . . . 10
137+ 5. JOSE Header . . . . . . . . . . . . . . . . . . . . . . . . . 11
138+ 5.1. "typ" (Type) Header Parameter . . . . . . . . . . . . . . 11
139+ 5.2. "cty" (Content Type) Header Parameter . . . . . . . . . . 11
140+ 5.3. Replicating Claims as Header Parameters . . . . . . . . . 12
141+ 6. Unsecured JWTs . . . . . . . . . . . . . . . . . . . . . . . 12
142+ 6.1. Example Unsecured JWT . . . . . . . . . . . . . . . . . . 12
143+ 7. Creating and Validating JWTs . . . . . . . . . . . . . . . . 13
144+ 7.1. Creating a JWT . . . . . . . . . . . . . . . . . . . . . 13
145+ 7.2. Validating a JWT . . . . . . . . . . . . . . . . . . . . 14
146+ 7.3. String Comparison Rules . . . . . . . . . . . . . . . . . 15
147+ 8. Implementation Requirements . . . . . . . . . . . . . . . . . 16
148+ 9. URI for Declaring that Content is a JWT . . . . . . . . . . . 17
149+ 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 17
150+ 10.1. JSON Web Token Claims Registry . . . . . . . . . . . . . 17
151+ 10.1.1. Registration Template . . . . . . . . . . . . . . . 18
152+ 10.1.2. Initial Registry Contents . . . . . . . . . . . . . 18
153+ 10.2. Sub-Namespace Registration of
154+ urn:ietf:params:oauth:token-type:jwt . . . . . . . . . . 19
155+ 10.2.1. Registry Contents . . . . . . . . . . . . . . . . . 19
156+ 10.3. Media Type Registration . . . . . . . . . . . . . . . . 20
157+ 10.3.1. Registry Contents . . . . . . . . . . . . . . . . . 20
158+ 10.4. Header Parameter Names Registration . . . . . . . . . . 20
159+ 10.4.1. Registry Contents . . . . . . . . . . . . . . . . . 21
160+ 11. Security Considerations . . . . . . . . . . . . . . . . . . . 21
161+ 11.1. Trust Decisions . . . . . . . . . . . . . . . . . . . . 21
162+ 11.2. Signing and Encryption Order . . . . . . . . . . . . . . 21
163+ 12. Privacy Considerations . . . . . . . . . . . . . . . . . . . 22
164+ 13. References . . . . . . . . . . . . . . . . . . . . . . . . . 22
165+ 13.1. Normative References . . . . . . . . . . . . . . . . . . 22
166+ 13.2. Informative References . . . . . . . . . . . . . . . . . 23
167+168+169+170+Jones, et al. Standards Track [Page 3]
171+172+RFC 7519 JSON Web Token (JWT) May 2015
173+174+175+ Appendix A. JWT Examples . . . . . . . . . . . . . . . . . . . . 26
176+ A.1. Example Encrypted JWT . . . . . . . . . . . . . . . . . . 26
177+ A.2. Example Nested JWT . . . . . . . . . . . . . . . . . . . 26
178+ Appendix B. Relationship of JWTs to SAML Assertions . . . . . . 28
179+ Appendix C. Relationship of JWTs to Simple Web Tokens (SWTs) . . 28
180+ Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . 28
181+ Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 29
182+183+1. Introduction
184+185+ JSON Web Token (JWT) is a compact claims representation format
186+ intended for space constrained environments such as HTTP
187+ Authorization headers and URI query parameters. JWTs encode claims
188+ to be transmitted as a JSON [RFC7159] object that is used as the
189+ payload of a JSON Web Signature (JWS) [JWS] structure or as the
190+ plaintext of a JSON Web Encryption (JWE) [JWE] structure, enabling
191+ the claims to be digitally signed or integrity protected with a
192+ Message Authentication Code (MAC) and/or encrypted. JWTs are always
193+ represented using the JWS Compact Serialization or the JWE Compact
194+ Serialization.
195+196+ The suggested pronunciation of JWT is the same as the English word
197+ "jot".
198+199+1.1. Notational Conventions
200+201+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
202+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
203+ "OPTIONAL" in this document are to be interpreted as described in
204+ "Key words for use in RFCs to Indicate Requirement Levels" [RFC2119].
205+ The interpretation should only be applied when the terms appear in
206+ all capital letters.
207+208+2. Terminology
209+210+ The terms "JSON Web Signature (JWS)", "Base64url Encoding", "Header
211+ Parameter", "JOSE Header", "JWS Compact Serialization", "JWS
212+ Payload", "JWS Signature", and "Unsecured JWS" are defined by the JWS
213+ specification [JWS].
214+215+ The terms "JSON Web Encryption (JWE)", "Content Encryption Key
216+ (CEK)", "JWE Compact Serialization", "JWE Encrypted Key", and "JWE
217+ Initialization Vector" are defined by the JWE specification [JWE].
218+219+ The terms "Ciphertext", "Digital Signature", "Message Authentication
220+ Code (MAC)", and "Plaintext" are defined by the "Internet Security
221+ Glossary, Version 2" [RFC4949].
222+223+224+225+226+Jones, et al. Standards Track [Page 4]
227+228+RFC 7519 JSON Web Token (JWT) May 2015
229+230+231+ These terms are defined by this specification:
232+233+ JSON Web Token (JWT)
234+ A string representing a set of claims as a JSON object that is
235+ encoded in a JWS or JWE, enabling the claims to be digitally
236+ signed or MACed and/or encrypted.
237+238+ JWT Claims Set
239+ A JSON object that contains the claims conveyed by the JWT.
240+241+ Claim
242+ A piece of information asserted about a subject. A claim is
243+ represented as a name/value pair consisting of a Claim Name and a
244+ Claim Value.
245+246+ Claim Name
247+ The name portion of a claim representation. A Claim Name is
248+ always a string.
249+250+ Claim Value
251+ The value portion of a claim representation. A Claim Value can be
252+ any JSON value.
253+254+ Nested JWT
255+ A JWT in which nested signing and/or encryption are employed. In
256+ Nested JWTs, a JWT is used as the payload or plaintext value of an
257+ enclosing JWS or JWE structure, respectively.
258+259+ Unsecured JWT
260+ A JWT whose claims are not integrity protected or encrypted.
261+262+ Collision-Resistant Name
263+ A name in a namespace that enables names to be allocated in a
264+ manner such that they are highly unlikely to collide with other
265+ names. Examples of collision-resistant namespaces include: Domain
266+ Names, Object Identifiers (OIDs) as defined in the ITU-T X.660 and
267+ X.670 Recommendation series, and Universally Unique IDentifiers
268+ (UUIDs) [RFC4122]. When using an administratively delegated
269+ namespace, the definer of a name needs to take reasonable
270+ precautions to ensure they are in control of the portion of the
271+ namespace they use to define the name.
272+273+ StringOrURI
274+ A JSON string value, with the additional requirement that while
275+ arbitrary string values MAY be used, any value containing a ":"
276+ character MUST be a URI [RFC3986]. StringOrURI values are
277+ compared as case-sensitive strings with no transformations or
278+ canonicalizations applied.
279+280+281+282+Jones, et al. Standards Track [Page 5]
283+284+RFC 7519 JSON Web Token (JWT) May 2015
285+286+287+ NumericDate
288+ A JSON numeric value representing the number of seconds from
289+ 1970-01-01T00:00:00Z UTC until the specified UTC date/time,
290+ ignoring leap seconds. This is equivalent to the IEEE Std 1003.1,
291+ 2013 Edition [POSIX.1] definition "Seconds Since the Epoch", in
292+ which each day is accounted for by exactly 86400 seconds, other
293+ than that non-integer values can be represented. See RFC 3339
294+ [RFC3339] for details regarding date/times in general and UTC in
295+ particular.
296+297+3. JSON Web Token (JWT) Overview
298+299+ JWTs represent a set of claims as a JSON object that is encoded in a
300+ JWS and/or JWE structure. This JSON object is the JWT Claims Set.
301+ As per Section 4 of RFC 7159 [RFC7159], the JSON object consists of
302+ zero or more name/value pairs (or members), where the names are
303+ strings and the values are arbitrary JSON values. These members are
304+ the claims represented by the JWT. This JSON object MAY contain
305+ whitespace and/or line breaks before or after any JSON values or
306+ structural characters, in accordance with Section 2 of RFC 7159
307+ [RFC7159].
308+309+ The member names within the JWT Claims Set are referred to as Claim
310+ Names. The corresponding values are referred to as Claim Values.
311+312+ The contents of the JOSE Header describe the cryptographic operations
313+ applied to the JWT Claims Set. If the JOSE Header is for a JWS, the
314+ JWT is represented as a JWS and the claims are digitally signed or
315+ MACed, with the JWT Claims Set being the JWS Payload. If the JOSE
316+ Header is for a JWE, the JWT is represented as a JWE and the claims
317+ are encrypted, with the JWT Claims Set being the plaintext encrypted
318+ by the JWE. A JWT may be enclosed in another JWE or JWS structure to
319+ create a Nested JWT, enabling nested signing and encryption to be
320+ performed.
321+322+ A JWT is represented as a sequence of URL-safe parts separated by
323+ period ('.') characters. Each part contains a base64url-encoded
324+ value. The number of parts in the JWT is dependent upon the
325+ representation of the resulting JWS using the JWS Compact
326+ Serialization or JWE using the JWE Compact Serialization.
327+328+329+330+331+332+333+334+335+336+337+338+Jones, et al. Standards Track [Page 6]
339+340+RFC 7519 JSON Web Token (JWT) May 2015
341+342+343+3.1. Example JWT
344+345+ The following example JOSE Header declares that the encoded object is
346+ a JWT, and the JWT is a JWS that is MACed using the HMAC SHA-256
347+ algorithm:
348+349+ {"typ":"JWT",
350+ "alg":"HS256"}
351+352+ To remove potential ambiguities in the representation of the JSON
353+ object above, the octet sequence for the actual UTF-8 representation
354+ used in this example for the JOSE Header above is also included
355+ below. (Note that ambiguities can arise due to differing platform
356+ representations of line breaks (CRLF versus LF), differing spacing at
357+ the beginning and ends of lines, whether the last line has a
358+ terminating line break or not, and other causes. In the
359+ representation used in this example, the first line has no leading or
360+ trailing spaces, a CRLF line break (13, 10) occurs between the first
361+ and second lines, the second line has one leading space (32) and no
362+ trailing spaces, and the last line does not have a terminating line
363+ break.) The octets representing the UTF-8 representation of the JOSE
364+ Header in this example (using JSON array notation) are:
365+366+ [123, 34, 116, 121, 112, 34, 58, 34, 74, 87, 84, 34, 44, 13, 10, 32,
367+ 34, 97, 108, 103, 34, 58, 34, 72, 83, 50, 53, 54, 34, 125]
368+369+ Base64url encoding the octets of the UTF-8 representation of the JOSE
370+ Header yields this encoded JOSE Header value:
371+372+ eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
373+374+ The following is an example of a JWT Claims Set:
375+376+ {"iss":"joe",
377+ "exp":1300819380,
378+ "http://example.com/is_root":true}
379+380+ The following octet sequence, which is the UTF-8 representation used
381+ in this example for the JWT Claims Set above, is the JWS Payload:
382+383+ [123, 34, 105, 115, 115, 34, 58, 34, 106, 111, 101, 34, 44, 13, 10,
384+ 32, 34, 101, 120, 112, 34, 58, 49, 51, 48, 48, 56, 49, 57, 51, 56,
385+ 48, 44, 13, 10, 32, 34, 104, 116, 116, 112, 58, 47, 47, 101, 120, 97,
386+ 109, 112, 108, 101, 46, 99, 111, 109, 47, 105, 115, 95, 114, 111,
387+ 111, 116, 34, 58, 116, 114, 117, 101, 125]
388+389+390+391+392+393+394+Jones, et al. Standards Track [Page 7]
395+396+RFC 7519 JSON Web Token (JWT) May 2015
397+398+399+ Base64url encoding the JWS Payload yields this encoded JWS Payload
400+ (with line breaks for display purposes only):
401+402+ eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly
403+ 9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
404+405+ Computing the MAC of the encoded JOSE Header and encoded JWS Payload
406+ with the HMAC SHA-256 algorithm and base64url encoding the HMAC value
407+ in the manner specified in [JWS] yields this encoded JWS Signature:
408+409+ dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
410+411+ Concatenating these encoded parts in this order with period ('.')
412+ characters between the parts yields this complete JWT (with line
413+ breaks for display purposes only):
414+415+ eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
416+ .
417+ eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt
418+ cGxlLmNvbS9pc19yb290Ijp0cnVlfQ
419+ .
420+ dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
421+422+ This computation is illustrated in more detail in Appendix A.1 of
423+ [JWS]. See Appendix A.1 for an example of an encrypted JWT.
424+425+4. JWT Claims
426+427+ The JWT Claims Set represents a JSON object whose members are the
428+ claims conveyed by the JWT. The Claim Names within a JWT Claims Set
429+ MUST be unique; JWT parsers MUST either reject JWTs with duplicate
430+ Claim Names or use a JSON parser that returns only the lexically last
431+ duplicate member name, as specified in Section 15.12 ("The JSON
432+ Object") of ECMAScript 5.1 [ECMAScript].
433+434+ The set of claims that a JWT must contain to be considered valid is
435+ context dependent and is outside the scope of this specification.
436+ Specific applications of JWTs will require implementations to
437+ understand and process some claims in particular ways. However, in
438+ the absence of such requirements, all claims that are not understood
439+ by implementations MUST be ignored.
440+441+ There are three classes of JWT Claim Names: Registered Claim Names,
442+ Public Claim Names, and Private Claim Names.
443+444+445+446+447+448+449+450+Jones, et al. Standards Track [Page 8]
451+452+RFC 7519 JSON Web Token (JWT) May 2015
453+454+455+4.1. Registered Claim Names
456+457+ The following Claim Names are registered in the IANA "JSON Web Token
458+ Claims" registry established by Section 10.1. None of the claims
459+ defined below are intended to be mandatory to use or implement in all
460+ cases, but rather they provide a starting point for a set of useful,
461+ interoperable claims. Applications using JWTs should define which
462+ specific claims they use and when they are required or optional. All
463+ the names are short because a core goal of JWTs is for the
464+ representation to be compact.
465+466+4.1.1. "iss" (Issuer) Claim
467+468+ The "iss" (issuer) claim identifies the principal that issued the
469+ JWT. The processing of this claim is generally application specific.
470+ The "iss" value is a case-sensitive string containing a StringOrURI
471+ value. Use of this claim is OPTIONAL.
472+473+4.1.2. "sub" (Subject) Claim
474+475+ The "sub" (subject) claim identifies the principal that is the
476+ subject of the JWT. The claims in a JWT are normally statements
477+ about the subject. The subject value MUST either be scoped to be
478+ locally unique in the context of the issuer or be globally unique.
479+ The processing of this claim is generally application specific. The
480+ "sub" value is a case-sensitive string containing a StringOrURI
481+ value. Use of this claim is OPTIONAL.
482+483+4.1.3. "aud" (Audience) Claim
484+485+ The "aud" (audience) claim identifies the recipients that the JWT is
486+ intended for. Each principal intended to process the JWT MUST
487+ identify itself with a value in the audience claim. If the principal
488+ processing the claim does not identify itself with a value in the
489+ "aud" claim when this claim is present, then the JWT MUST be
490+ rejected. In the general case, the "aud" value is an array of case-
491+ sensitive strings, each containing a StringOrURI value. In the
492+ special case when the JWT has one audience, the "aud" value MAY be a
493+ single case-sensitive string containing a StringOrURI value. The
494+ interpretation of audience values is generally application specific.
495+ Use of this claim is OPTIONAL.
496+497+4.1.4. "exp" (Expiration Time) Claim
498+499+ The "exp" (expiration time) claim identifies the expiration time on
500+ or after which the JWT MUST NOT be accepted for processing. The
501+ processing of the "exp" claim requires that the current date/time
502+ MUST be before the expiration date/time listed in the "exp" claim.
503+504+505+506+Jones, et al. Standards Track [Page 9]
507+508+RFC 7519 JSON Web Token (JWT) May 2015
509+510+511+ Implementers MAY provide for some small leeway, usually no more than
512+ a few minutes, to account for clock skew. Its value MUST be a number
513+ containing a NumericDate value. Use of this claim is OPTIONAL.
514+515+4.1.5. "nbf" (Not Before) Claim
516+517+ The "nbf" (not before) claim identifies the time before which the JWT
518+ MUST NOT be accepted for processing. The processing of the "nbf"
519+ claim requires that the current date/time MUST be after or equal to
520+ the not-before date/time listed in the "nbf" claim. Implementers MAY
521+ provide for some small leeway, usually no more than a few minutes, to
522+ account for clock skew. Its value MUST be a number containing a
523+ NumericDate value. Use of this claim is OPTIONAL.
524+525+4.1.6. "iat" (Issued At) Claim
526+527+ The "iat" (issued at) claim identifies the time at which the JWT was
528+ issued. This claim can be used to determine the age of the JWT. Its
529+ value MUST be a number containing a NumericDate value. Use of this
530+ claim is OPTIONAL.
531+532+4.1.7. "jti" (JWT ID) Claim
533+534+ The "jti" (JWT ID) claim provides a unique identifier for the JWT.
535+ The identifier value MUST be assigned in a manner that ensures that
536+ there is a negligible probability that the same value will be
537+ accidentally assigned to a different data object; if the application
538+ uses multiple issuers, collisions MUST be prevented among values
539+ produced by different issuers as well. The "jti" claim can be used
540+ to prevent the JWT from being replayed. The "jti" value is a case-
541+ sensitive string. Use of this claim is OPTIONAL.
542+543+4.2. Public Claim Names
544+545+ Claim Names can be defined at will by those using JWTs. However, in
546+ order to prevent collisions, any new Claim Name should either be
547+ registered in the IANA "JSON Web Token Claims" registry established
548+ by Section 10.1 or be a Public Name: a value that contains a
549+ Collision-Resistant Name. In each case, the definer of the name or
550+ value needs to take reasonable precautions to make sure they are in
551+ control of the part of the namespace they use to define the Claim
552+ Name.
553+554+4.3. Private Claim Names
555+556+ A producer and consumer of a JWT MAY agree to use Claim Names that
557+ are Private Names: names that are not Registered Claim Names
558+ (Section 4.1) or Public Claim Names (Section 4.2). Unlike Public
559+560+561+562+Jones, et al. Standards Track [Page 10]
563+564+RFC 7519 JSON Web Token (JWT) May 2015
565+566+567+ Claim Names, Private Claim Names are subject to collision and should
568+ be used with caution.
569+570+5. JOSE Header
571+572+ For a JWT object, the members of the JSON object represented by the
573+ JOSE Header describe the cryptographic operations applied to the JWT
574+ and optionally, additional properties of the JWT. Depending upon
575+ whether the JWT is a JWS or JWE, the corresponding rules for the JOSE
576+ Header values apply.
577+578+ This specification further specifies the use of the following Header
579+ Parameters in both the cases where the JWT is a JWS and where it is a
580+ JWE.
581+582+5.1. "typ" (Type) Header Parameter
583+584+ The "typ" (type) Header Parameter defined by [JWS] and [JWE] is used
585+ by JWT applications to declare the media type [IANA.MediaTypes] of
586+ this complete JWT. This is intended for use by the JWT application
587+ when values that are not JWTs could also be present in an application
588+ data structure that can contain a JWT object; the application can use
589+ this value to disambiguate among the different kinds of objects that
590+ might be present. It will typically not be used by applications when
591+ it is already known that the object is a JWT. This parameter is
592+ ignored by JWT implementations; any processing of this parameter is
593+ performed by the JWT application. If present, it is RECOMMENDED that
594+ its value be "JWT" to indicate that this object is a JWT. While
595+ media type names are not case sensitive, it is RECOMMENDED that "JWT"
596+ always be spelled using uppercase characters for compatibility with
597+ legacy implementations. Use of this Header Parameter is OPTIONAL.
598+599+5.2. "cty" (Content Type) Header Parameter
600+601+ The "cty" (content type) Header Parameter defined by [JWS] and [JWE]
602+ is used by this specification to convey structural information about
603+ the JWT.
604+605+ In the normal case in which nested signing or encryption operations
606+ are not employed, the use of this Header Parameter is NOT
607+ RECOMMENDED. In the case that nested signing or encryption is
608+ employed, this Header Parameter MUST be present; in this case, the
609+ value MUST be "JWT", to indicate that a Nested JWT is carried in this
610+ JWT. While media type names are not case sensitive, it is
611+ RECOMMENDED that "JWT" always be spelled using uppercase characters
612+ for compatibility with legacy implementations. See Appendix A.2 for
613+ an example of a Nested JWT.
614+615+616+617+618+Jones, et al. Standards Track [Page 11]
619+620+RFC 7519 JSON Web Token (JWT) May 2015
621+622+623+5.3. Replicating Claims as Header Parameters
624+625+ In some applications using encrypted JWTs, it is useful to have an
626+ unencrypted representation of some claims. This might be used, for
627+ instance, in application processing rules to determine whether and
628+ how to process the JWT before it is decrypted.
629+630+ This specification allows claims present in the JWT Claims Set to be
631+ replicated as Header Parameters in a JWT that is a JWE, as needed by
632+ the application. If such replicated claims are present, the
633+ application receiving them SHOULD verify that their values are
634+ identical, unless the application defines other specific processing
635+ rules for these claims. It is the responsibility of the application
636+ to ensure that only claims that are safe to be transmitted in an
637+ unencrypted manner are replicated as Header Parameter values in the
638+ JWT.
639+640+ Section 10.4.1 of this specification registers the "iss" (issuer),
641+ "sub" (subject), and "aud" (audience) Header Parameter names for the
642+ purpose of providing unencrypted replicas of these claims in
643+ encrypted JWTs for applications that need them. Other specifications
644+ MAY similarly register other names that are registered Claim Names as
645+ Header Parameter names, as needed.
646+647+6. Unsecured JWTs
648+649+ To support use cases in which the JWT content is secured by a means
650+ other than a signature and/or encryption contained within the JWT
651+ (such as a signature on a data structure containing the JWT), JWTs
652+ MAY also be created without a signature or encryption. An Unsecured
653+ JWT is a JWS using the "alg" Header Parameter value "none" and with
654+ the empty string for its JWS Signature value, as defined in the JWA
655+ specification [JWA]; it is an Unsecured JWS with the JWT Claims Set
656+ as its JWS Payload.
657+658+6.1. Example Unsecured JWT
659+660+ The following example JOSE Header declares that the encoded object is
661+ an Unsecured JWT:
662+663+ {"alg":"none"}
664+665+ Base64url encoding the octets of the UTF-8 representation of the JOSE
666+ Header yields this encoded JOSE Header value:
667+668+ eyJhbGciOiJub25lIn0
669+670+671+672+673+674+Jones, et al. Standards Track [Page 12]
675+676+RFC 7519 JSON Web Token (JWT) May 2015
677+678+679+ The following is an example of a JWT Claims Set:
680+681+ {"iss":"joe",
682+ "exp":1300819380,
683+ "http://example.com/is_root":true}
684+685+ Base64url encoding the octets of the UTF-8 representation of the JWT
686+ Claims Set yields this encoded JWS Payload (with line breaks for
687+ display purposes only):
688+689+ eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt
690+ cGxlLmNvbS9pc19yb290Ijp0cnVlfQ
691+692+ The encoded JWS Signature is the empty string.
693+694+ Concatenating these encoded parts in this order with period ('.')
695+ characters between the parts yields this complete JWT (with line
696+ breaks for display purposes only):
697+698+ eyJhbGciOiJub25lIn0
699+ .
700+ eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFt
701+ cGxlLmNvbS9pc19yb290Ijp0cnVlfQ
702+ .
703+704+7. Creating and Validating JWTs
705+706+7.1. Creating a JWT
707+708+ To create a JWT, the following steps are performed. The order of the
709+ steps is not significant in cases where there are no dependencies
710+ between the inputs and outputs of the steps.
711+712+ 1. Create a JWT Claims Set containing the desired claims. Note that
713+ whitespace is explicitly allowed in the representation and no
714+ canonicalization need be performed before encoding.
715+716+ 2. Let the Message be the octets of the UTF-8 representation of the
717+ JWT Claims Set.
718+719+ 3. Create a JOSE Header containing the desired set of Header
720+ Parameters. The JWT MUST conform to either the [JWS] or [JWE]
721+ specification. Note that whitespace is explicitly allowed in the
722+ representation and no canonicalization need be performed before
723+ encoding.
724+725+726+727+728+729+730+Jones, et al. Standards Track [Page 13]
731+732+RFC 7519 JSON Web Token (JWT) May 2015
733+734+735+ 4. Depending upon whether the JWT is a JWS or JWE, there are two
736+ cases:
737+738+ * If the JWT is a JWS, create a JWS using the Message as the JWS
739+ Payload; all steps specified in [JWS] for creating a JWS MUST
740+ be followed.
741+742+ * Else, if the JWT is a JWE, create a JWE using the Message as
743+ the plaintext for the JWE; all steps specified in [JWE] for
744+ creating a JWE MUST be followed.
745+746+ 5. If a nested signing or encryption operation will be performed,
747+ let the Message be the JWS or JWE, and return to Step 3, using a
748+ "cty" (content type) value of "JWT" in the new JOSE Header
749+ created in that step.
750+751+ 6. Otherwise, let the resulting JWT be the JWS or JWE.
752+753+7.2. Validating a JWT
754+755+ When validating a JWT, the following steps are performed. The order
756+ of the steps is not significant in cases where there are no
757+ dependencies between the inputs and outputs of the steps. If any of
758+ the listed steps fail, then the JWT MUST be rejected -- that is,
759+ treated by the application as an invalid input.
760+761+ 1. Verify that the JWT contains at least one period ('.')
762+ character.
763+764+ 2. Let the Encoded JOSE Header be the portion of the JWT before the
765+ first period ('.') character.
766+767+ 3. Base64url decode the Encoded JOSE Header following the
768+ restriction that no line breaks, whitespace, or other additional
769+ characters have been used.
770+771+ 4. Verify that the resulting octet sequence is a UTF-8-encoded
772+ representation of a completely valid JSON object conforming to
773+ RFC 7159 [RFC7159]; let the JOSE Header be this JSON object.
774+775+ 5. Verify that the resulting JOSE Header includes only parameters
776+ and values whose syntax and semantics are both understood and
777+ supported or that are specified as being ignored when not
778+ understood.
779+780+ 6. Determine whether the JWT is a JWS or a JWE using any of the
781+ methods described in Section 9 of [JWE].
782+783+784+785+786+Jones, et al. Standards Track [Page 14]
787+788+RFC 7519 JSON Web Token (JWT) May 2015
789+790+791+ 7. Depending upon whether the JWT is a JWS or JWE, there are two
792+ cases:
793+794+ * If the JWT is a JWS, follow the steps specified in [JWS] for
795+ validating a JWS. Let the Message be the result of base64url
796+ decoding the JWS Payload.
797+798+ * Else, if the JWT is a JWE, follow the steps specified in
799+ [JWE] for validating a JWE. Let the Message be the resulting
800+ plaintext.
801+802+ 8. If the JOSE Header contains a "cty" (content type) value of
803+ "JWT", then the Message is a JWT that was the subject of nested
804+ signing or encryption operations. In this case, return to Step
805+ 1, using the Message as the JWT.
806+807+ 9. Otherwise, base64url decode the Message following the
808+ restriction that no line breaks, whitespace, or other additional
809+ characters have been used.
810+811+ 10. Verify that the resulting octet sequence is a UTF-8-encoded
812+ representation of a completely valid JSON object conforming to
813+ RFC 7159 [RFC7159]; let the JWT Claims Set be this JSON object.
814+815+ Finally, note that it is an application decision which algorithms may
816+ be used in a given context. Even if a JWT can be successfully
817+ validated, unless the algorithms used in the JWT are acceptable to
818+ the application, it SHOULD reject the JWT.
819+820+7.3. String Comparison Rules
821+822+ Processing a JWT inevitably requires comparing known strings to
823+ members and values in JSON objects. For example, in checking what
824+ the algorithm is, the Unicode [UNICODE] string encoding "alg" will be
825+ checked against the member names in the JOSE Header to see if there
826+ is a matching Header Parameter name.
827+828+ The JSON rules for doing member name comparison are described in
829+ Section 8.3 of RFC 7159 [RFC7159]. Since the only string comparison
830+ operations that are performed are equality and inequality, the same
831+ rules can be used for comparing both member names and member values
832+ against known strings.
833+834+ These comparison rules MUST be used for all JSON string comparisons
835+ except in cases where the definition of the member explicitly calls
836+ out that a different comparison rule is to be used for that member
837+ value. In this specification, only the "typ" and "cty" member values
838+ do not use these comparison rules.
839+840+841+842+Jones, et al. Standards Track [Page 15]
843+844+RFC 7519 JSON Web Token (JWT) May 2015
845+846+847+ Some applications may include case-insensitive information in a case-
848+ sensitive value, such as including a DNS name as part of the "iss"
849+ (issuer) claim value. In those cases, the application may need to
850+ define a convention for the canonical case to use for representing
851+ the case-insensitive portions, such as lowercasing them, if more than
852+ one party might need to produce the same value so that they can be
853+ compared. (However, if all other parties consume whatever value the
854+ producing party emitted verbatim without attempting to compare it to
855+ an independently produced value, then the case used by the producer
856+ will not matter.)
857+858+8. Implementation Requirements
859+860+ This section defines which algorithms and features of this
861+ specification are mandatory to implement. Applications using this
862+ specification can impose additional requirements upon implementations
863+ that they use. For instance, one application might require support
864+ for encrypted JWTs and Nested JWTs, while another might require
865+ support for signing JWTs with the Elliptic Curve Digital Signature
866+ Algorithm (ECDSA) using the P-256 curve and the SHA-256 hash
867+ algorithm ("ES256").
868+869+ Of the signature and MAC algorithms specified in JSON Web Algorithms
870+ [JWA], only HMAC SHA-256 ("HS256") and "none" MUST be implemented by
871+ conforming JWT implementations. It is RECOMMENDED that
872+ implementations also support RSASSA-PKCS1-v1_5 with the SHA-256 hash
873+ algorithm ("RS256") and ECDSA using the P-256 curve and the SHA-256
874+ hash algorithm ("ES256"). Support for other algorithms and key sizes
875+ is OPTIONAL.
876+877+ Support for encrypted JWTs is OPTIONAL. If an implementation
878+ provides encryption capabilities, of the encryption algorithms
879+ specified in [JWA], only RSAES-PKCS1-v1_5 with 2048-bit keys
880+ ("RSA1_5"), AES Key Wrap with 128- and 256-bit keys ("A128KW" and
881+ "A256KW"), and the composite authenticated encryption algorithm using
882+ AES-CBC and HMAC SHA-2 ("A128CBC-HS256" and "A256CBC-HS512") MUST be
883+ implemented by conforming implementations. It is RECOMMENDED that
884+ implementations also support using Elliptic Curve Diffie-Hellman
885+ Ephemeral Static (ECDH-ES) to agree upon a key used to wrap the
886+ Content Encryption Key ("ECDH-ES+A128KW" and "ECDH-ES+A256KW") and
887+ AES in Galois/Counter Mode (GCM) with 128- and 256-bit keys
888+ ("A128GCM" and "A256GCM"). Support for other algorithms and key
889+ sizes is OPTIONAL.
890+891+ Support for Nested JWTs is OPTIONAL.
892+893+894+895+896+897+898+Jones, et al. Standards Track [Page 16]
899+900+RFC 7519 JSON Web Token (JWT) May 2015
901+902+903+9. URI for Declaring that Content is a JWT
904+905+ This specification registers the URN
906+ "urn:ietf:params:oauth:token-type:jwt" for use by applications that
907+ declare content types using URIs (rather than, for instance, media
908+ types) to indicate that the content referred to is a JWT.
909+910+10. IANA Considerations
911+912+10.1. JSON Web Token Claims Registry
913+914+ This section establishes the IANA "JSON Web Token Claims" registry
915+ for JWT Claim Names. The registry records the Claim Name and a
916+ reference to the specification that defines it. This section
917+ registers the Claim Names defined in Section 4.1.
918+919+ Values are registered on a Specification Required [RFC5226] basis
920+ after a three-week review period on the jwt-reg-review@ietf.org
921+ mailing list, on the advice of one or more Designated Experts.
922+ However, to allow for the allocation of values prior to publication,
923+ the Designated Experts may approve registration once they are
924+ satisfied that such a specification will be published.
925+926+ Registration requests sent to the mailing list for review should use
927+ an appropriate subject (e.g., "Request to register claim: example").
928+929+ Within the review period, the Designated Experts will either approve
930+ or deny the registration request, communicating this decision to the
931+ review list and IANA. Denials should include an explanation and, if
932+ applicable, suggestions as to how to make the request successful.
933+ Registration requests that are undetermined for a period longer than
934+ 21 days can be brought to the IESG's attention (using the
935+ iesg@ietf.org mailing list) for resolution.
936+937+ Criteria that should be applied by the Designated Experts includes
938+ determining whether the proposed registration duplicates existing
939+ functionality, whether it is likely to be of general applicability or
940+ whether it is useful only for a single application, and whether the
941+ registration description is clear.
942+943+ IANA must only accept registry updates from the Designated Experts
944+ and should direct all requests for registration to the review mailing
945+ list.
946+947+ It is suggested that multiple Designated Experts be appointed who are
948+ able to represent the perspectives of different applications using
949+ this specification, in order to enable broadly informed review of
950+ registration decisions. In cases where a registration decision could
951+952+953+954+Jones, et al. Standards Track [Page 17]
955+956+RFC 7519 JSON Web Token (JWT) May 2015
957+958+959+ be perceived as creating a conflict of interest for a particular
960+ Expert, that Expert should defer to the judgment of the other
961+ Experts.
962+963+10.1.1. Registration Template
964+965+ Claim Name:
966+ The name requested (e.g., "iss"). Because a core goal of this
967+ specification is for the resulting representations to be compact,
968+ it is RECOMMENDED that the name be short -- that is, not to exceed
969+ 8 characters without a compelling reason to do so. This name is
970+ case sensitive. Names may not match other registered names in a
971+ case-insensitive manner unless the Designated Experts state that
972+ there is a compelling reason to allow an exception.
973+974+ Claim Description:
975+ Brief description of the claim (e.g., "Issuer").
976+977+ Change Controller:
978+ For Standards Track RFCs, list the "IESG". For others, give the
979+ name of the responsible party. Other details (e.g., postal
980+ address, email address, home page URI) may also be included.
981+982+ Specification Document(s):
983+ Reference to the document or documents that specify the parameter,
984+ preferably including URIs that can be used to retrieve copies of
985+ the documents. An indication of the relevant sections may also be
986+ included but is not required.
987+988+10.1.2. Initial Registry Contents
989+990+ o Claim Name: "iss"
991+ o Claim Description: Issuer
992+ o Change Controller: IESG
993+ o Specification Document(s): Section 4.1.1 of RFC 7519
994+995+ o Claim Name: "sub"
996+ o Claim Description: Subject
997+ o Change Controller: IESG
998+ o Specification Document(s): Section 4.1.2 of RFC 7519
999+1000+ o Claim Name: "aud"
1001+ o Claim Description: Audience
1002+ o Change Controller: IESG
1003+ o Specification Document(s): Section 4.1.3 of RFC 7519
1004+1005+1006+1007+1008+1009+1010+Jones, et al. Standards Track [Page 18]
1011+1012+RFC 7519 JSON Web Token (JWT) May 2015
1013+1014+1015+ o Claim Name: "exp"
1016+ o Claim Description: Expiration Time
1017+ o Change Controller: IESG
1018+ o Specification Document(s): Section 4.1.4 of RFC 7519
1019+1020+ o Claim Name: "nbf"
1021+ o Claim Description: Not Before
1022+ o Change Controller: IESG
1023+ o Specification Document(s): Section 4.1.5 of RFC 7519
1024+1025+ o Claim Name: "iat"
1026+ o Claim Description: Issued At
1027+ o Change Controller: IESG
1028+ o Specification Document(s): Section 4.1.6 of RFC 7519
1029+1030+ o Claim Name: "jti"
1031+ o Claim Description: JWT ID
1032+ o Change Controller: IESG
1033+ o Specification Document(s): Section 4.1.7 of RFC 7519
1034+1035+10.2. Sub-Namespace Registration of
1036+ urn:ietf:params:oauth:token-type:jwt
1037+1038+10.2.1. Registry Contents
1039+1040+ This section registers the value "token-type:jwt" in the IANA "OAuth
1041+ URI" registry established by "An IETF URN Sub-Namespace for OAuth"
1042+ [RFC6755], which can be used to indicate that the content is a JWT.
1043+1044+ o URN: urn:ietf:params:oauth:token-type:jwt
1045+ o Common Name: JSON Web Token (JWT) Token Type
1046+ o Change Controller: IESG
1047+ o Specification Document(s): RFC 7519
1048+1049+1050+1051+1052+1053+1054+1055+1056+1057+1058+1059+1060+1061+1062+1063+1064+1065+1066+Jones, et al. Standards Track [Page 19]
1067+1068+RFC 7519 JSON Web Token (JWT) May 2015
1069+1070+1071+10.3. Media Type Registration
1072+1073+10.3.1. Registry Contents
1074+1075+ This section registers the "application/jwt" media type [RFC2046] in
1076+ the "Media Types" registry [IANA.MediaTypes] in the manner described
1077+ in RFC 6838 [RFC6838], which can be used to indicate that the content
1078+ is a JWT.
1079+1080+ o Type name: application
1081+ o Subtype name: jwt
1082+ o Required parameters: n/a
1083+ o Optional parameters: n/a
1084+ o Encoding considerations: 8bit; JWT values are encoded as a series
1085+ of base64url-encoded values (some of which may be the empty
1086+ string) separated by period ('.') characters.
1087+ o Security considerations: See the Security Considerations section
1088+ of RFC 7519
1089+ o Interoperability considerations: n/a
1090+ o Published specification: RFC 7519
1091+ o Applications that use this media type: OpenID Connect, Mozilla
1092+ Persona, Salesforce, Google, Android, Windows Azure, Amazon Web
1093+ Services, and numerous others
1094+ o Fragment identifier considerations: n/a
1095+ o Additional information:
1096+1097+ Magic number(s): n/a
1098+ File extension(s): n/a
1099+ Macintosh file type code(s): n/a
1100+1101+ o Person & email address to contact for further information:
1102+ Michael B. Jones, mbj@microsoft.com
1103+ o Intended usage: COMMON
1104+ o Restrictions on usage: none
1105+ o Author: Michael B. Jones, mbj@microsoft.com
1106+ o Change controller: IESG
1107+ o Provisional registration? No
1108+1109+10.4. Header Parameter Names Registration
1110+1111+ This section registers specific Claim Names defined in Section 4.1 in
1112+ the IANA "JSON Web Signature and Encryption Header Parameters"
1113+ registry established by [JWS] for use by claims replicated as Header
1114+ Parameters in JWEs, per Section 5.3.
1115+1116+1117+1118+1119+1120+1121+1122+Jones, et al. Standards Track [Page 20]
1123+1124+RFC 7519 JSON Web Token (JWT) May 2015
1125+1126+1127+10.4.1. Registry Contents
1128+1129+ o Header Parameter Name: "iss"
1130+ o Header Parameter Description: Issuer
1131+ o Header Parameter Usage Location(s): JWE
1132+ o Change Controller: IESG
1133+ o Specification Document(s): Section 4.1.1 of RFC 7519
1134+1135+ o Header Parameter Name: "sub"
1136+ o Header Parameter Description: Subject
1137+ o Header Parameter Usage Location(s): JWE
1138+ o Change Controller: IESG
1139+ o Specification Document(s): Section 4.1.2 of RFC 7519
1140+1141+ o Header Parameter Name: "aud"
1142+ o Header Parameter Description: Audience
1143+ o Header Parameter Usage Location(s): JWE
1144+ o Change Controller: IESG
1145+ o Specification Document(s): Section 4.1.3 of RFC 7519
1146+1147+11. Security Considerations
1148+1149+ All of the security issues that are pertinent to any cryptographic
1150+ application must be addressed by JWT/JWS/JWE/JWK agents. Among these
1151+ issues are protecting the user's asymmetric private and symmetric
1152+ secret keys and employing countermeasures to various attacks.
1153+1154+ All the security considerations in the JWS specification also apply
1155+ to JWT, as do the JWE security considerations when encryption is
1156+ employed. In particular, Sections 10.12 ("JSON Security
1157+ Considerations") and 10.13 ("Unicode Comparison Security
1158+ Considerations") of [JWS] apply equally to the JWT Claims Set in the
1159+ same manner that they do to the JOSE Header.
1160+1161+11.1. Trust Decisions
1162+1163+ The contents of a JWT cannot be relied upon in a trust decision
1164+ unless its contents have been cryptographically secured and bound to
1165+ the context necessary for the trust decision. In particular, the
1166+ key(s) used to sign and/or encrypt the JWT will typically need to
1167+ verifiably be under the control of the party identified as the issuer
1168+ of the JWT.
1169+1170+11.2. Signing and Encryption Order
1171+1172+ While syntactically the signing and encryption operations for Nested
1173+ JWTs may be applied in any order, if both signing and encryption are
1174+ necessary, normally producers should sign the message and then
1175+1176+1177+1178+Jones, et al. Standards Track [Page 21]
1179+1180+RFC 7519 JSON Web Token (JWT) May 2015
1181+1182+1183+ encrypt the result (thus encrypting the signature). This prevents
1184+ attacks in which the signature is stripped, leaving just an encrypted
1185+ message, as well as providing privacy for the signer. Furthermore,
1186+ signatures over encrypted text are not considered valid in many
1187+ jurisdictions.
1188+1189+ Note that potential concerns about security issues related to the
1190+ order of signing and encryption operations are already addressed by
1191+ the underlying JWS and JWE specifications; in particular, because JWE
1192+ only supports the use of authenticated encryption algorithms,
1193+ cryptographic concerns about the potential need to sign after
1194+ encryption that apply in many contexts do not apply to this
1195+ specification.
1196+1197+12. Privacy Considerations
1198+1199+ A JWT may contain privacy-sensitive information. When this is the
1200+ case, measures MUST be taken to prevent disclosure of this
1201+ information to unintended parties. One way to achieve this is to use
1202+ an encrypted JWT and authenticate the recipient. Another way is to
1203+ ensure that JWTs containing unencrypted privacy-sensitive information
1204+ are only transmitted using protocols utilizing encryption that
1205+ support endpoint authentication, such as Transport Layer Security
1206+ (TLS). Omitting privacy-sensitive information from a JWT is the
1207+ simplest way of minimizing privacy issues.
1208+1209+13. References
1210+1211+13.1. Normative References
1212+1213+ [ECMAScript]
1214+ Ecma International, "ECMAScript Language Specification,
1215+ 5.1 Edition", ECMA Standard 262, June 2011,
1216+ <http://www.ecma-international.org/ecma-262/5.1/
1217+ ECMA-262.pdf>.
1218+1219+ [IANA.MediaTypes]
1220+ IANA, "Media Types",
1221+ <http://www.iana.org/assignments/media-types>.
1222+1223+ [JWA] Jones, M., "JSON Web Algorithms (JWA)", RFC 7518,
1224+ DOI 10.17487/RFC7518, May 2015,
1225+ <http://www.rfc-editor.org/info/rfc7518>.
1226+1227+ [JWE] Jones, M. and J. Hildebrand, "JSON Web Encryption (JWE)",
1228+ RFC 7516, DOI 10.17487/RFC7516, May 2015,
1229+ <http://www.rfc-editor.org/info/rfc7516>.
1230+1231+1232+1233+1234+Jones, et al. Standards Track [Page 22]
1235+1236+RFC 7519 JSON Web Token (JWT) May 2015
1237+1238+1239+ [JWS] Jones, M., Bradley, J., and N. Sakimura, "JSON Web
1240+ Signature (JWS)", RFC 7515, DOI 10.17487/RFC, May 2015,
1241+ <http://www.rfc-editor.org/info/rfc7515>.
1242+1243+ [RFC20] Cerf, V., "ASCII format for Network Interchange", STD 80,
1244+ RFC 20, DOI 10.17487/RFC0020, October 1969,
1245+ <http://www.rfc-editor.org/info/rfc20>.
1246+1247+ [RFC2046] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
1248+ Extensions (MIME) Part Two: Media Types", RFC 2046,
1249+ DOI 10.17487/RFC2046, November 1996,
1250+ <http://www.rfc-editor.org/info/rfc2046>.
1251+1252+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
1253+ Requirement Levels", BCP 14, RFC 2119,
1254+ DOI 10.17487/RFC2119, March 1997,
1255+ <http://www.rfc-editor.org/info/rfc2119>.
1256+1257+ [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform
1258+ Resource Identifier (URI): Generic Syntax", STD 66,
1259+ RFC 3986, DOI 10.17487/RFC3986, January 2005,
1260+ <http://www.rfc-editor.org/info/rfc3986>.
1261+1262+ [RFC4949] Shirey, R., "Internet Security Glossary, Version 2",
1263+ FYI 36, RFC 4949, DOI 10.17487/RFC4949, August 2007,
1264+ <http://www.rfc-editor.org/info/rfc4949>.
1265+1266+ [RFC7159] Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
1267+ Interchange Format", RFC 7159, DOI 10.17487/RFC7159, March
1268+ 2014, <http://www.rfc-editor.org/info/rfc7159>.
1269+1270+ [UNICODE] The Unicode Consortium, "The Unicode Standard",
1271+ <http://www.unicode.org/versions/latest/>.
1272+1273+13.2. Informative References
1274+1275+ [CanvasApp]
1276+ Facebook, "Canvas Applications", 2010,
1277+ <http://developers.facebook.com/docs/authentication/
1278+ canvas>.
1279+1280+ [JSS] Bradley, J. and N. Sakimura (editor), "JSON Simple Sign",
1281+ September 2010, <http://jsonenc.info/jss/1.0/>.
1282+1283+1284+1285+1286+1287+1288+1289+1290+Jones, et al. Standards Track [Page 23]
1291+1292+RFC 7519 JSON Web Token (JWT) May 2015
1293+1294+1295+ [MagicSignatures]
1296+ Panzer, J., Ed., Laurie, B., and D. Balfanz, "Magic
1297+ Signatures", January 2011,
1298+ <http://salmon-protocol.googlecode.com/svn/
1299+ trunk/draft-panzer-magicsig-01.html>.
1300+1301+ [OASIS.saml-core-2.0-os]
1302+ Cantor, S., Kemp, J., Philpott, R., and E. Maler,
1303+ "Assertions and Protocols for the OASIS Security Assertion
1304+ Markup Language (SAML) V2.0", OASIS Standard
1305+ saml-core-2.0-os, March 2005,
1306+ <http://docs.oasis-open.org/security/saml/v2.0/
1307+ saml-core-2.0-os.pdf>.
1308+1309+ [POSIX.1] IEEE, "The Open Group Base Specifications Issue 7", IEEE
1310+ Std 1003.1, 2013 Edition, 2013,
1311+ <http://pubs.opengroup.org/onlinepubs/9699919799/
1312+ basedefs/V1_chap04.html#tag_04_15>.
1313+1314+ [RFC3275] Eastlake 3rd, D., Reagle, J., and D. Solo, "(Extensible
1315+ Markup Language) XML-Signature Syntax and Processing",
1316+ RFC 3275, DOI 10.17487/RFC3275, March 2002,
1317+ <http://www.rfc-editor.org/info/rfc3275>.
1318+1319+ [RFC3339] Klyne, G. and C. Newman, "Date and Time on the Internet:
1320+ Timestamps", RFC 3339, DOI 10.17487/RFC3339, July 2002,
1321+ <http://www.rfc-editor.org/info/rfc3339>.
1322+1323+ [RFC4122] Leach, P., Mealling, M., and R. Salz, "A Universally
1324+ Unique IDentifier (UUID) URN Namespace", RFC 4122,
1325+ DOI 10.17487/RFC4122, July 2005,
1326+ <http://www.rfc-editor.org/info/rfc4122>.
1327+1328+ [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing an
1329+ IANA Considerations Section in RFCs", BCP 26, RFC 5226,
1330+ DOI 10.17487/RFC5226, May 2008,
1331+ <http://www.rfc-editor.org/info/rfc5226>.
1332+1333+ [RFC6755] Campbell, B. and H. Tschofenig, "An IETF URN Sub-Namespace
1334+ for OAuth", RFC 6755, DOI 10.17487/RFC6755, October 2012,
1335+ <http://www.rfc-editor.org/info/rfc6755>.
1336+1337+ [RFC6838] Freed, N., Klensin, J., and T. Hansen, "Media Type
1338+ Specifications and Registration Procedures", BCP 13,
1339+ RFC 6838, DOI 10.17487/RFC6838, January 2013,
1340+ <http://www.rfc-editor.org/info/rfc6838>.
1341+1342+1343+1344+1345+1346+Jones, et al. Standards Track [Page 24]
1347+1348+RFC 7519 JSON Web Token (JWT) May 2015
1349+1350+1351+ [SWT] Hardt, D. and Y. Goland, "Simple Web Token (SWT)", Version
1352+ 0.9.5.1, November 2009, <http://msdn.microsoft.com/en-us/
1353+ library/windowsazure/hh781551.aspx>.
1354+1355+ [W3C.CR-xml11-20060816]
1356+ Cowan, J., "Extensible Markup Language (XML) 1.1 (Second
1357+ Edition)", World Wide Web Consortium Recommendation
1358+ REC-xml11-20060816, August 2006,
1359+ <http://www.w3.org/TR/2006/REC-xml11-20060816>.
1360+1361+ [W3C.REC-xml-c14n-20010315]
1362+ Boyer, J., "Canonical XML Version 1.0", World Wide Web
1363+ Consortium Recommendation REC-xml-c14n-20010315, March
1364+ 2001, <http://www.w3.org/TR/2001/REC-xml-c14n-20010315>.
1365+1366+1367+1368+1369+1370+1371+1372+1373+1374+1375+1376+1377+1378+1379+1380+1381+1382+1383+1384+1385+1386+1387+1388+1389+1390+1391+1392+1393+1394+1395+1396+1397+1398+1399+1400+1401+1402+Jones, et al. Standards Track [Page 25]
1403+1404+RFC 7519 JSON Web Token (JWT) May 2015
1405+1406+1407+Appendix A. JWT Examples
1408+1409+ This section contains examples of JWTs. For other example JWTs, see
1410+ Section 6.1 of this document and Appendices A.1 - A.3 of [JWS].
1411+1412+A.1. Example Encrypted JWT
1413+1414+ This example encrypts the same claims as used in Section 3.1 to the
1415+ recipient using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256.
1416+1417+ The following example JOSE Header declares that:
1418+1419+ o The Content Encryption Key is encrypted to the recipient using the
1420+ RSAES-PKCS1-v1_5 algorithm to produce the JWE Encrypted Key.
1421+ o Authenticated encryption is performed on the plaintext using the
1422+ AES_128_CBC_HMAC_SHA_256 algorithm to produce the JWE Ciphertext
1423+ and the JWE Authentication Tag.
1424+1425+ {"alg":"RSA1_5","enc":"A128CBC-HS256"}
1426+1427+ Other than using the octets of the UTF-8 representation of the JWT
1428+ Claims Set from Section 3.1 as the plaintext value, the computation
1429+ of this JWT is identical to the computation of the JWE in
1430+ Appendix A.2 of [JWE], including the keys used.
1431+1432+ The final result in this example (with line breaks for display
1433+ purposes only) is:
1434+1435+ eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.
1436+ QR1Owv2ug2WyPBnbQrRARTeEk9kDO2w8qDcjiHnSJflSdv1iNqhWXaKH4MqAkQtM
1437+ oNfABIPJaZm0HaA415sv3aeuBWnD8J-Ui7Ah6cWafs3ZwwFKDFUUsWHSK-IPKxLG
1438+ TkND09XyjORj_CHAgOPJ-Sd8ONQRnJvWn_hXV1BNMHzUjPyYwEsRhDhzjAD26ima
1439+ sOTsgruobpYGoQcXUwFDn7moXPRfDE8-NoQX7N7ZYMmpUDkR-Cx9obNGwJQ3nM52
1440+ YCitxoQVPzjbl7WBuB7AohdBoZOdZ24WlN1lVIeh8v1K4krB8xgKvRU8kgFrEn_a
1441+ 1rZgN5TiysnmzTROF869lQ.
1442+ AxY8DCtDaGlsbGljb3RoZQ.
1443+ MKOle7UQrG6nSxTLX6Mqwt0orbHvAKeWnDYvpIAeZ72deHxz3roJDXQyhxx0wKaM
1444+ HDjUEOKIwrtkHthpqEanSBNYHZgmNOV7sln1Eu9g3J8.
1445+ fiK51VwhsxJ-siBMR-YFiA
1446+1447+A.2. Example Nested JWT
1448+1449+ This example shows how a JWT can be used as the payload of a JWE or
1450+ JWS to create a Nested JWT. In this case, the JWT Claims Set is
1451+ first signed, and then encrypted.
1452+1453+1454+1455+1456+1457+1458+Jones, et al. Standards Track [Page 26]
1459+1460+RFC 7519 JSON Web Token (JWT) May 2015
1461+1462+1463+ The inner signed JWT is identical to the example in Appendix A.2 of
1464+ [JWS]. Therefore, its computation is not repeated here. This
1465+ example then encrypts this inner JWT to the recipient using
1466+ RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256.
1467+1468+ The following example JOSE Header declares that:
1469+1470+ o The Content Encryption Key is encrypted to the recipient using the
1471+ RSAES-PKCS1-v1_5 algorithm to produce the JWE Encrypted Key.
1472+ o Authenticated encryption is performed on the plaintext using the
1473+ AES_128_CBC_HMAC_SHA_256 algorithm to produce the JWE Ciphertext
1474+ and the JWE Authentication Tag.
1475+ o The plaintext is itself a JWT.
1476+1477+ {"alg":"RSA1_5","enc":"A128CBC-HS256","cty":"JWT"}
1478+1479+ Base64url encoding the octets of the UTF-8 representation of the JOSE
1480+ Header yields this encoded JOSE Header value:
1481+1482+ eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5IjoiSldUIn0
1483+1484+ The computation of this JWT is identical to the computation of the
1485+ JWE in Appendix A.2 of [JWE], other than that different JOSE Header,
1486+ plaintext, JWE Initialization Vector, and Content Encryption Key
1487+ values are used. (The RSA key used is the same.)
1488+1489+ The plaintext used is the octets of the ASCII [RFC20] representation
1490+ of the JWT at the end of Appendix A.2.1 of [JWS] (with all whitespace
1491+ and line breaks removed), which is a sequence of 458 octets.
1492+1493+ The JWE Initialization Vector value used (using JSON array notation)
1494+ is:
1495+1496+ [82, 101, 100, 109, 111, 110, 100, 32, 87, 65, 32, 57, 56, 48, 53,
1497+ 50]
1498+1499+ This example uses the Content Encryption Key represented by the
1500+ base64url-encoded value below:
1501+1502+ GawgguFyGrWKav7AX4VKUg
1503+1504+1505+1506+1507+1508+1509+1510+1511+1512+1513+1514+Jones, et al. Standards Track [Page 27]
1515+1516+RFC 7519 JSON Web Token (JWT) May 2015
1517+1518+1519+ The final result for this Nested JWT (with line breaks for display
1520+ purposes only) is:
1521+1522+ eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5IjoiSldU
1523+ In0.
1524+ g_hEwksO1Ax8Qn7HoN-BVeBoa8FXe0kpyk_XdcSmxvcM5_P296JXXtoHISr_DD_M
1525+ qewaQSH4dZOQHoUgKLeFly-9RI11TG-_Ge1bZFazBPwKC5lJ6OLANLMd0QSL4fYE
1526+ b9ERe-epKYE3xb2jfY1AltHqBO-PM6j23Guj2yDKnFv6WO72tteVzm_2n17SBFvh
1527+ DuR9a2nHTE67pe0XGBUS_TK7ecA-iVq5COeVdJR4U4VZGGlxRGPLRHvolVLEHx6D
1528+ YyLpw30Ay9R6d68YCLi9FYTq3hIXPK_-dmPlOUlKvPr1GgJzRoeC9G5qCvdcHWsq
1529+ JGTO_z3Wfo5zsqwkxruxwA.
1530+ UmVkbW9uZCBXQSA5ODA1Mg.
1531+ VwHERHPvCNcHHpTjkoigx3_ExK0Qc71RMEParpatm0X_qpg-w8kozSjfNIPPXiTB
1532+ BLXR65CIPkFqz4l1Ae9w_uowKiwyi9acgVztAi-pSL8GQSXnaamh9kX1mdh3M_TT
1533+ -FZGQFQsFhu0Z72gJKGdfGE-OE7hS1zuBD5oEUfk0Dmb0VzWEzpxxiSSBbBAzP10
1534+ l56pPfAtrjEYw-7ygeMkwBl6Z_mLS6w6xUgKlvW6ULmkV-uLC4FUiyKECK4e3WZY
1535+ Kw1bpgIqGYsw2v_grHjszJZ-_I5uM-9RA8ycX9KqPRp9gc6pXmoU_-27ATs9XCvr
1536+ ZXUtK2902AUzqpeEUJYjWWxSNsS-r1TJ1I-FMJ4XyAiGrfmo9hQPcNBYxPz3GQb2
1537+ 8Y5CLSQfNgKSGt0A4isp1hBUXBHAndgtcslt7ZoQJaKe_nNJgNliWtWpJ_ebuOpE
1538+ l8jdhehdccnRMIwAmU1n7SPkmhIl1HlSOpvcvDfhUN5wuqU955vOBvfkBOh5A11U
1539+ zBuo2WlgZ6hYi9-e3w29bR0C2-pp3jbqxEDw3iWaf2dc5b-LnR0FEYXvI_tYk5rd
1540+ _J9N0mg0tQ6RbpxNEMNoA9QWk5lgdPvbh9BaO195abQ.
1541+ AVO9iT5AV4CzvDJCdhSFlQ
1542+1543+Appendix B. Relationship of JWTs to SAML Assertions
1544+1545+ Security Assertion Markup Language (SAML) 2.0
1546+ [OASIS.saml-core-2.0-os] provides a standard for creating security
1547+ tokens with greater expressivity and more security options than
1548+ supported by JWTs. However, the cost of this flexibility and
1549+ expressiveness is both size and complexity. SAML's use of XML
1550+ [W3C.CR-xml11-20060816] and XML Digital Signature (DSIG) [RFC3275]
1551+ contributes to the size of SAML Assertions; its use of XML and
1552+ especially XML Canonicalization [W3C.REC-xml-c14n-20010315]
1553+ contributes to their complexity.
1554+1555+ JWTs are intended to provide a simple security token format that is
1556+ small enough to fit into HTTP headers and query arguments in URIs.
1557+ It does this by supporting a much simpler token model than SAML and
1558+ using the JSON [RFC7159] object encoding syntax. It also supports
1559+ securing tokens using Message Authentication Codes (MACs) and digital
1560+ signatures using a smaller (and less flexible) format than XML DSIG.
1561+1562+ Therefore, while JWTs can do some of the things SAML Assertions do,
1563+ JWTs are not intended as a full replacement for SAML Assertions, but
1564+ rather as a token format to be used when ease of implementation or
1565+ compactness are considerations.
1566+1567+1568+1569+1570+Jones, et al. Standards Track [Page 28]
1571+1572+RFC 7519 JSON Web Token (JWT) May 2015
1573+1574+1575+ SAML Assertions are always statements made by an entity about a
1576+ subject. JWTs are often used in the same manner, with the entity
1577+ making the statements being represented by the "iss" (issuer) claim,
1578+ and the subject being represented by the "sub" (subject) claim.
1579+ However, with these claims being optional, other uses of the JWT
1580+ format are also permitted.
1581+1582+Appendix C. Relationship of JWTs to Simple Web Tokens (SWTs)
1583+1584+ Both JWTs and SWTs [SWT], at their core, enable sets of claims to be
1585+ communicated between applications. For SWTs, both the claim names
1586+ and claim values are strings. For JWTs, while claim names are
1587+ strings, claim values can be any JSON type. Both token types offer
1588+ cryptographic protection of their content: SWTs with HMAC SHA-256 and
1589+ JWTs with a choice of algorithms, including signature, MAC, and
1590+ encryption algorithms.
1591+1592+Acknowledgements
1593+1594+ The authors acknowledge that the design of JWTs was intentionally
1595+ influenced by the design and simplicity of SWTs [SWT] and ideas for
1596+ JSON tokens that Dick Hardt discussed within the OpenID community.
1597+1598+ Solutions for signing JSON content were previously explored by Magic
1599+ Signatures [MagicSignatures], JSON Simple Sign [JSS], and Canvas
1600+ Applications [CanvasApp], all of which influenced this document.
1601+1602+ This specification is the work of the OAuth working group, which
1603+ includes dozens of active and dedicated participants. In particular,
1604+ the following individuals contributed ideas, feedback, and wording
1605+ that influenced this specification:
1606+1607+ Dirk Balfanz, Richard Barnes, Brian Campbell, Alissa Cooper, Breno de
1608+ Medeiros, Stephen Farrell, Yaron Y. Goland, Dick Hardt, Joe
1609+ Hildebrand, Jeff Hodges, Edmund Jay, Warren Kumari, Ben Laurie, Barry
1610+ Leiba, Ted Lemon, James Manger, Prateek Mishra, Kathleen Moriarty,
1611+ Tony Nadalin, Axel Nennker, John Panzer, Emmanuel Raviart, David
1612+ Recordon, Eric Rescorla, Jim Schaad, Paul Tarjan, Hannes Tschofenig,
1613+ Sean Turner, and Tom Yu.
1614+1615+ Hannes Tschofenig and Derek Atkins chaired the OAuth working group
1616+ and Sean Turner, Stephen Farrell, and Kathleen Moriarty served as
1617+ Security Area Directors during the creation of this specification.
1618+1619+1620+1621+1622+1623+1624+1625+1626+Jones, et al. Standards Track [Page 29]
1627+1628+RFC 7519 JSON Web Token (JWT) May 2015
1629+1630+1631+Authors' Addresses
1632+1633+ Michael B. Jones
1634+ Microsoft
1635+1636+ EMail: mbj@microsoft.com
1637+ URI: http://self-issued.info/
1638+1639+1640+ John Bradley
1641+ Ping Identity
1642+1643+ EMail: ve7jtb@ve7jtb.com
1644+ URI: http://www.thread-safe.com/
1645+1646+1647+ Nat Sakimura
1648+ Nomura Research Institute
1649+1650+ EMail: n-sakimura@nri.co.jp
1651+ URI: http://nat.sakimura.org/
1652+1653+1654+1655+1656+1657+1658+1659+1660+1661+1662+1663+1664+1665+1666+1667+1668+1669+1670+1671+1672+1673+1674+1675+1676+1677+1678+1679+1680+1681+1682+Jones, et al. Standards Track [Page 30]
1683+