objective categorical abstract machine language personal data server
at main 160 lines 5.5 kB view raw
1open Alcotest 2 3(** helpers *) 4let test_string = testable Fmt.string String.equal 5 6(* create a minimal jwt 7 we only care about the (base64url encoded json) payload, so header and signature can be anything *) 8let make_jwt payload_json = 9 let header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" in 10 (* {"alg":"HS256","typ":"JWT"} *) 11 let payload = 12 Base64.encode_string ~alphabet:Base64.uri_safe_alphabet ~pad:false 13 payload_json 14 in 15 let signature = "signature" in 16 header ^ "." ^ payload ^ "." ^ signature 17 18(* decoding a valid JWT *) 19let test_decode_valid () = 20 let now = int_of_float (Unix.time ()) in 21 let payload_json = 22 Printf.sprintf {|{"sub":"did:plc:test","iat":%d,"exp":%d}|} now (now + 3600) 23 in 24 let jwt = make_jwt payload_json in 25 match Hermes.Jwt.decode_payload jwt with 26 | Ok payload -> 27 check (option test_string) "sub" (Some "did:plc:test") payload.sub ; 28 check (option int) "iat" (Some now) payload.iat ; 29 check (option int) "exp" (Some (now + 3600)) payload.exp 30 | Error e -> 31 fail ("decode failed: " ^ e) 32 33(* decoding JWT with additional fields *) 34let test_decode_with_extra_fields () = 35 let now = int_of_float (Unix.time ()) in 36 let payload_json = 37 Printf.sprintf 38 {|{"sub":"did:plc:extra","iat":%d,"exp":%d,"scope":"atproto","aud":"did:web:server"}|} 39 now (now + 3600) 40 in 41 let jwt = make_jwt payload_json in 42 match Hermes.Jwt.decode_payload jwt with 43 | Ok payload -> 44 check (option test_string) "sub" (Some "did:plc:extra") payload.sub ; 45 check (option int) "exp" (Some (now + 3600)) payload.exp ; 46 check (option test_string) "aud" (Some "did:web:server") payload.aud 47 | Error e -> 48 fail ("decode failed: " ^ e) 49 50(* decoding invalid JWT format *) 51let test_decode_invalid_format () = 52 let invalid = "not.a.jwt.with.wrong.parts" in 53 match Hermes.Jwt.decode_payload invalid with 54 | Ok _ -> 55 fail "should have failed" 56 | Error e -> 57 check bool "has error" true (String.length e > 0) 58 59let test_decode_no_dots () = 60 let invalid = "nodots" in 61 match Hermes.Jwt.decode_payload invalid with 62 | Ok _ -> 63 fail "should have failed" 64 | Error _ -> 65 () 66 67(* decoding JWT with invalid base64 *) 68let test_decode_invalid_base64 () = 69 let invalid = "header.!!!invalid!!!.signature" in 70 match Hermes.Jwt.decode_payload invalid with 71 | Ok _ -> 72 fail "should have failed" 73 | Error _ -> 74 () 75 76(* decoding JWT with invalid JSON payload *) 77let test_decode_invalid_json () = 78 let header = "eyJhbGciOiJIUzI1NiJ9" in 79 let payload = 80 Base64.encode_string ~alphabet:Base64.uri_safe_alphabet ~pad:false 81 "not json" 82 in 83 let jwt = header ^ "." ^ payload ^ ".sig" in 84 match Hermes.Jwt.decode_payload jwt with 85 | Ok _ -> 86 fail "should have failed" 87 | Error _ -> 88 () 89 90(* test is_expired with expired token *) 91let test_expired_token () = 92 let past = int_of_float (Unix.time ()) - 3600 in 93 (* 1 hour ago *) 94 let payload_json = 95 Printf.sprintf {|{"sub":"test","iat":%d,"exp":%d}|} past past 96 in 97 let jwt = make_jwt payload_json in 98 check bool "is expired" true (Hermes.Jwt.is_expired jwt) 99 100(* test is_expired with valid token *) 101let test_valid_token () = 102 let now = int_of_float (Unix.time ()) in 103 let future = now + 3600 in 104 (* 1 hour from now *) 105 let payload_json = 106 Printf.sprintf {|{"sub":"test","iat":%d,"exp":%d}|} now future 107 in 108 let jwt = make_jwt payload_json in 109 check bool "is not expired" false (Hermes.Jwt.is_expired jwt) 110 111(* test is_expired with buffer *) 112let test_expired_with_buffer () = 113 let now = int_of_float (Unix.time ()) in 114 let almost_expired = now + 30 in 115 (* expires in 30 seconds *) 116 let payload_json = 117 Printf.sprintf {|{"sub":"test","iat":%d,"exp":%d}|} now almost_expired 118 in 119 let jwt = make_jwt payload_json in 120 (* default buffer is 60 seconds, so this should be considered expired *) 121 check bool "expired with buffer" true (Hermes.Jwt.is_expired jwt) 122 123(* is_expired with invalid JWT returns true *) 124let test_expired_invalid_jwt () = 125 check bool "invalid JWT treated as expired" true 126 (Hermes.Jwt.is_expired "invalid") 127 128(* test get_expiration *) 129let test_get_expiration () = 130 let now = int_of_float (Unix.time ()) in 131 let exp_time = now + 3600 in 132 let payload_json = Printf.sprintf {|{"sub":"test","exp":%d}|} exp_time in 133 let jwt = make_jwt payload_json in 134 check (option int) "expiration" (Some exp_time) 135 (Hermes.Jwt.get_expiration jwt) 136 137let test_get_expiration_missing () = 138 let payload_json = {|{"sub":"test"}|} in 139 let jwt = make_jwt payload_json in 140 check (option int) "no expiration" None (Hermes.Jwt.get_expiration jwt) 141 142(** tests *) 143 144let decode_tests = 145 [ ("decode valid JWT", `Quick, test_decode_valid) 146 ; ("decode with extra fields", `Quick, test_decode_with_extra_fields) 147 ; ("decode invalid format", `Quick, test_decode_invalid_format) 148 ; ("decode no dots", `Quick, test_decode_no_dots) 149 ; ("decode invalid base64", `Quick, test_decode_invalid_base64) 150 ; ("decode invalid json", `Quick, test_decode_invalid_json) ] 151 152let expiry_tests = 153 [ ("expired token", `Quick, test_expired_token) 154 ; ("valid token", `Quick, test_valid_token) 155 ; ("expired with buffer", `Quick, test_expired_with_buffer) 156 ; ("invalid JWT is expired", `Quick, test_expired_invalid_jwt) 157 ; ("get_expiration", `Quick, test_get_expiration) 158 ; ("get_expiration missing", `Quick, test_get_expiration_missing) ] 159 160let () = run "Jwt" [("decode", decode_tests); ("expiry", expiry_tests)]