OAuth 2.0 authorization and token exchange
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

oauth: update test runner for per-module split

The concurrent commit 180d7ab11 landed the new per-module test files
(test_client_auth, test_par, test_provider, test_redirect_uri,
test_authorization_url, test_parse_token_response, test_helpers) but
not the runner or the original kitchen sink. Finish the split:

- Remove test_regressions.ml (tests are now in the per-module files).
- Update test.ml to call the per-module suites.

+10 -587
+10 -1
test/test.ml
··· 1 1 let () = 2 2 Crypto_rng_unix.use_default (); 3 3 Alcotest.run "oauth" 4 - [ Test_github_oauth.suite; Test_regressions.suite; Test_token.suite ] 4 + [ 5 + Test_github_oauth.suite; 6 + Test_authorization_url.suite; 7 + Test_provider.suite; 8 + Test_redirect_uri.suite; 9 + Test_client_auth.suite; 10 + Test_par.suite; 11 + Test_parse_token_response.suite; 12 + Test_token.suite; 13 + ]
-586
test/test_regressions.ml
··· 1 - let redir s = Oauth.redirect_uri s |> Result.get_ok 2 - 3 - let contains str ~substring = 4 - let len = String.length substring in 5 - let rec go i = 6 - if i + len > String.length str then false 7 - else if String.sub str i len = substring then true 8 - else go (i + 1) 9 - in 10 - go 0 11 - 12 - let first_existing paths = 13 - match List.find_opt Sys.file_exists paths with 14 - | Some path -> path 15 - | None -> 16 - Alcotest.fail 17 - (Fmt.str "missing test fixture, looked for one of: %s" 18 - (String.concat ", " paths)) 19 - 20 - let read_file paths = 21 - In_channel.with_open_bin (first_existing paths) In_channel.input_all 22 - 23 - let query_param name query = List.assoc_opt name query 24 - 25 - let test_authorization_url_includes_response_type_code () = 26 - let url = 27 - Oauth.authorization_url Oauth.Github ~client_id:"test_client" 28 - ~redirect_uri:(redir "https://example.com/callback") 29 - ~state:"test_state" ~scope:[ "repo" ] () 30 - in 31 - let uri = Uri.of_string url in 32 - Alcotest.(check (option string)) 33 - "response_type=code" (Some "code") 34 - (Uri.get_query_param uri "response_type") 35 - 36 - (* exchange_form_body and refresh_form_body are no longer public — 37 - form encoding correctness is tested indirectly via exchange_code 38 - and refresh_token which use them internally. *) 39 - 40 - let parse_token_error = Alcotest.testable Oauth.pp_parse_token_error ( = ) 41 - 42 - let test_parse_token_response_ok () = 43 - let body = 44 - {|{"access_token":"gho_abc","expires_in":3600,"refresh_token":"ghr_xyz"}|} 45 - in 46 - match Oauth.parse_token_response body with 47 - | Ok t -> 48 - Alcotest.(check string) "access_token" "gho_abc" t.access_token; 49 - Alcotest.(check (option int)) "expires_in" (Some 3600) t.expires_in; 50 - Alcotest.(check (option string)) 51 - "refresh_token" (Some "ghr_xyz") t.refresh_token 52 - | Error e -> 53 - Alcotest.failf "expected Ok, got Error %a" Oauth.pp_parse_token_error e 54 - 55 - let test_parse_token_response_invalid_json () = 56 - let body = "not json at all" in 57 - Alcotest.(check (result reject parse_token_error)) 58 - "invalid json" (Error Oauth.Invalid_json) 59 - (Oauth.parse_token_response body) 60 - 61 - let test_parse_token_response_missing_access_token () = 62 - let body = {|{"expires_in":3600}|} in 63 - Alcotest.(check (result reject parse_token_error)) 64 - "missing access_token" (Error Oauth.Missing_access_token) 65 - (Oauth.parse_token_response body) 66 - 67 - let test_parse_token_response_empty_access_token () = 68 - let body = {|{"access_token":""}|} in 69 - Alcotest.(check (result reject parse_token_error)) 70 - "empty access_token" (Error Oauth.Missing_access_token) 71 - (Oauth.parse_token_response body) 72 - 73 - let test_parse_token_response_invalid_format () = 74 - let body = {|{"access_token":12345}|} in 75 - Alcotest.(check (result reject parse_token_error)) 76 - "access_token wrong type" (Error Oauth.Invalid_token_format) 77 - (Oauth.parse_token_response body) 78 - 79 - let test_parse_token_response_rejects_mac_token () = 80 - let body = 81 - {|{"access_token":"tok_abc","token_type":"mac","expires_in":3600}|} 82 - in 83 - match Oauth.parse_token_response body with 84 - | Error (Oauth.Unsupported_token_type "mac") -> () 85 - | Error e -> Alcotest.failf "wrong error: %a" Oauth.pp_parse_token_error e 86 - | Ok _ -> Alcotest.fail "expected rejection of mac token_type" 87 - 88 - let test_parse_token_response_accepts_bearer () = 89 - let body = 90 - {|{"access_token":"tok_abc","token_type":"Bearer","expires_in":3600}|} 91 - in 92 - match Oauth.parse_token_response body with 93 - | Ok t -> Alcotest.(check string) "access_token" "tok_abc" t.access_token 94 - | Error e -> 95 - Alcotest.failf "unexpected error: %a" Oauth.pp_parse_token_error e 96 - 97 - let test_parse_token_response_accepts_bearer_case_insensitive () = 98 - let body = {|{"access_token":"tok_abc","token_type":"BEARER"}|} in 99 - match Oauth.parse_token_response body with 100 - | Ok t -> Alcotest.(check string) "access_token" "tok_abc" t.access_token 101 - | Error e -> 102 - Alcotest.failf "unexpected error: %a" Oauth.pp_parse_token_error e 103 - 104 - let test_parse_token_response_accepts_missing_token_type () = 105 - (* GitHub omits token_type *) 106 - let body = {|{"access_token":"gho_abc"}|} in 107 - match Oauth.parse_token_response body with 108 - | Ok t -> Alcotest.(check string) "access_token" "gho_abc" t.access_token 109 - | Error e -> 110 - Alcotest.failf "unexpected error: %a" Oauth.pp_parse_token_error e 111 - 112 - let custom name = 113 - match 114 - Oauth.custom_provider ~name ~authorize_url:"https://example.com/auth" 115 - ~token_url:"https://example.com/token" 116 - ~userinfo_url:"https://example.com/user" ~uid_field:"id" () 117 - with 118 - | Ok p -> Oauth.Custom p 119 - | Error (`Msg msg) -> failwith msg 120 - 121 - let test_provider_name_is_raw () = 122 - (* provider_name returns the raw name for DB identity *) 123 - Alcotest.(check string) "builtin" "github" (Oauth.provider_name Oauth.Github); 124 - Alcotest.(check string) 125 - "raw name" "corp/sso" 126 - (Oauth.provider_name (custom "corp/sso")); 127 - Alcotest.(check string) 128 - "preserves case" "Acme SSO" 129 - (Oauth.provider_name (custom "Acme SSO")); 130 - Alcotest.(check string) 131 - "unicode preserved" "\xe4\xbc\x81\xe6\xa5\xad" 132 - (Oauth.provider_name (custom "\xe4\xbc\x81\xe6\xa5\xad")) 133 - 134 - let test_provider_slug_is_path_safe () = 135 - (* provider_slug returns a URL-safe slug for routes *) 136 - Alcotest.(check string) "builtin" "github" (Oauth.provider_slug Oauth.Github); 137 - Alcotest.(check string) 138 - "slash" "corp-sso" 139 - (Oauth.provider_slug (custom "corp/sso")); 140 - Alcotest.(check string) 141 - "spaces" "acme-sso" 142 - (Oauth.provider_slug (custom "Acme SSO")); 143 - Alcotest.(check string) 144 - "already clean" "myidp" 145 - (Oauth.provider_slug (custom "myidp")); 146 - Alcotest.(check string) 147 - "special chars" "my-cool-provider" 148 - (Oauth.provider_slug (custom "My Cool_Provider!")); 149 - Alcotest.(check string) 150 - "non-ascii fallback" "custom" 151 - (Oauth.provider_slug (custom "\xe4\xbc\x81\xe6\xa5\xad")) 152 - 153 - let test_readme_uses_current_api_names () = 154 - let readme = read_file [ "README.md"; "ocaml-oauth/README.md" ] in 155 - Alcotest.(check bool) 156 - "README should not reference removed Github_oauth module" false 157 - (contains readme ~substring:"Github_oauth"); 158 - Alcotest.(check bool) 159 - "README should not reference callback_url" false 160 - (contains readme ~substring:"callback_url"); 161 - Alcotest.(check bool) 162 - "README should not reference access_token_url" false 163 - (contains readme ~substring:"access_token_url"); 164 - Alcotest.(check bool) 165 - "README should use current package name" true 166 - (contains readme ~substring:"opam install oauth") 167 - 168 - let test_opam_declares_test_runtime_dependencies () = 169 - let opam = read_file [ "oauth.opam"; "ocaml-oauth/oauth.opam" ] in 170 - let test_dune = read_file [ "test/dune"; "ocaml-oauth/test/dune" ] in 171 - let fuzz_dune = read_file [ "fuzz/dune"; "ocaml-oauth/fuzz/dune" ] in 172 - let needs_crypto_rng_unix = 173 - contains test_dune ~substring:"crypto-rng.unix" 174 - || contains fuzz_dune ~substring:"crypto-rng.unix" 175 - in 176 - if needs_crypto_rng_unix then 177 - Alcotest.(check bool) 178 - "opam should declare crypto-rng.unix when tests require it" true 179 - (contains opam ~substring:"\"crypto-rng.unix\"") 180 - 181 - (* ── Client authentication (RFC 6749 §2.3.1) ─────────────────────── *) 182 - 183 - let test_client_auth_none_apply () = 184 - let a = Oauth.Client_auth.none ~client_id:"cid" in 185 - let fields, headers = Oauth.Client_auth.apply a in 186 - Alcotest.(check (list (pair string string))) 187 - "fields" 188 - [ ("client_id", "cid") ] 189 - fields; 190 - Alcotest.(check (list (pair string string))) "no headers" [] headers 191 - 192 - let test_client_auth_post_apply () = 193 - let a = 194 - Oauth.Client_auth.post ~client_id:"cid" ~client_secret:"supersecret" 195 - in 196 - let fields, headers = Oauth.Client_auth.apply a in 197 - Alcotest.(check (list (pair string string))) 198 - "fields" 199 - [ ("client_id", "cid"); ("client_secret", "supersecret") ] 200 - fields; 201 - Alcotest.(check (list (pair string string))) "no headers" [] headers 202 - 203 - let test_client_auth_basic_apply () = 204 - let a = Oauth.Client_auth.basic ~client_id:"cid" ~client_secret:"csec" in 205 - let fields, headers = Oauth.Client_auth.apply a in 206 - Alcotest.(check (list (pair string string))) 207 - "fields" 208 - [ ("client_id", "cid") ] 209 - fields; 210 - (* Basic base64("cid:csec") = Basic Y2lkOmNzZWM= *) 211 - Alcotest.(check (list (pair string string))) 212 - "Authorization header" 213 - [ ("Authorization", "Basic Y2lkOmNzZWM=") ] 214 - headers 215 - 216 - let test_client_auth_basic_percent_encodes_special_chars () = 217 - (* RFC 6749 §2.3.1: credentials are form-urlencoded before joining with ":" 218 - so a secret containing ':' or other special chars does not produce an 219 - ambiguous token. We percent-encode both halves uniformly. *) 220 - let a = 221 - Oauth.Client_auth.basic ~client_id:"id:with:colons" 222 - ~client_secret:"p@ss:wor d" 223 - in 224 - let _, headers = Oauth.Client_auth.apply a in 225 - let auth = List.assoc "Authorization" headers in 226 - (* Decode the base64 part and check it's pct(id):pct(secret). *) 227 - let b64 = 228 - match String.split_on_char ' ' auth with 229 - | [ "Basic"; b64 ] -> b64 230 - | _ -> Alcotest.failf "malformed Authorization header: %s" auth 231 - in 232 - let decoded = Base64.decode_exn b64 in 233 - (* client_id and client_secret percent-encoded separately, joined by raw ':' *) 234 - Alcotest.(check string) 235 - "percent-encoded halves joined by ':'" "id%3Awith%3Acolons:p%40ss%3Awor%20d" 236 - decoded 237 - 238 - let test_client_auth_client_id_accessor () = 239 - Alcotest.(check string) 240 - "none" "a" 241 - (Oauth.Client_auth.client_id (Oauth.Client_auth.none ~client_id:"a")); 242 - Alcotest.(check string) 243 - "post" "b" 244 - (Oauth.Client_auth.client_id 245 - (Oauth.Client_auth.post ~client_id:"b" ~client_secret:"x")); 246 - Alcotest.(check string) 247 - "basic" "c" 248 - (Oauth.Client_auth.client_id 249 - (Oauth.Client_auth.basic ~client_id:"c" ~client_secret:"x")) 250 - 251 - (* ── PAR (RFC 9126) ──────────────────────────────────────────────── *) 252 - 253 - let par_error = Alcotest.testable Oauth.Par.pp_error ( = ) 254 - 255 - let test_par_parse_response_ok () = 256 - let body = 257 - {|{"request_uri":"urn:ietf:params:oauth:request_uri:x","expires_in":60}|} 258 - in 259 - match Oauth.Par.parse_response body with 260 - | Ok r -> 261 - Alcotest.(check string) 262 - "request_uri" "urn:ietf:params:oauth:request_uri:x" r.request_uri; 263 - Alcotest.(check int) "expires_in" 60 r.expires_in 264 - | Error e -> Alcotest.failf "unexpected: %a" Oauth.Par.pp_error e 265 - 266 - let test_par_parse_response_missing_request_uri () = 267 - let body = {|{"expires_in":60}|} in 268 - Alcotest.(check (result reject par_error)) 269 - "missing request_uri" (Error Oauth.Par.Missing_request_uri) 270 - (Oauth.Par.parse_response body) 271 - 272 - let test_par_parse_response_missing_expires_in () = 273 - let body = {|{"request_uri":"urn:x"}|} in 274 - Alcotest.(check (result reject par_error)) 275 - "missing expires_in" (Error Oauth.Par.Invalid_expires_in) 276 - (Oauth.Par.parse_response body) 277 - 278 - let test_par_parse_response_invalid_json () = 279 - Alcotest.(check (result reject par_error)) 280 - "invalid json" (Error Oauth.Par.Invalid_json) 281 - (Oauth.Par.parse_response "not json") 282 - 283 - let test_par_push_requires_par_endpoint () = 284 - (* Built-in providers have no PAR endpoint; push should refuse cleanly. *) 285 - Eio_main.run @@ fun env -> 286 - Eio.Switch.run @@ fun sw -> 287 - let http = Requests.v ~sw env in 288 - Alcotest.(check (result reject par_error)) 289 - "no par_endpoint" (Error Oauth.Par.No_par_endpoint) 290 - (Oauth.Par.push http Oauth.Github 291 - ~client_auth:(Oauth.Client_auth.post ~client_id:"x" ~client_secret:"y") 292 - ~redirect_uri:(redir "https://example.com/cb") 293 - ~state:"s" ~scope:[ "r" ] ()) 294 - 295 - let test_par_authorization_url_only_carries_client_id_and_request_uri () = 296 - match 297 - Oauth.custom_provider ~name:"atp" ~authorize_url:"https://as.example/auth" 298 - ~token_url:"https://as.example/token" 299 - ~userinfo_url:"https://as.example/user" ~uid_field:"sub" 300 - ~par_endpoint:"https://as.example/par" () 301 - with 302 - | Error (`Msg msg) -> Alcotest.failf "custom_provider: %s" msg 303 - | Ok c -> 304 - let url = 305 - Oauth.Par.authorization_url (Oauth.Custom c) ~client_id:"cid" 306 - ~request_uri:"urn:example:req:1" 307 - in 308 - let uri = Uri.of_string url in 309 - Alcotest.(check (option string)) 310 - "client_id" (Some "cid") 311 - (Uri.get_query_param uri "client_id"); 312 - Alcotest.(check (option string)) 313 - "request_uri" (Some "urn:example:req:1") 314 - (Uri.get_query_param uri "request_uri"); 315 - Alcotest.(check (option string)) 316 - "no response_type" None 317 - (Uri.get_query_param uri "response_type"); 318 - Alcotest.(check (option string)) 319 - "no scope" None 320 - (Uri.get_query_param uri "scope"); 321 - Alcotest.(check (option string)) 322 - "no state" None 323 - (Uri.get_query_param uri "state") 324 - 325 - let test_custom_provider_accepts_par_endpoint () = 326 - match 327 - Oauth.custom_provider ~name:"atp" ~authorize_url:"https://as.example/auth" 328 - ~token_url:"https://as.example/token" 329 - ~userinfo_url:"https://as.example/user" ~uid_field:"sub" 330 - ~par_endpoint:"https://as.example/par" () 331 - with 332 - | Ok c -> 333 - Alcotest.(check (option string)) 334 - "par_endpoint" (Some "https://as.example/par") c.par_endpoint 335 - | Error (`Msg msg) -> Alcotest.failf "unexpected error: %s" msg 336 - 337 - let test_custom_provider_rejects_http_par_endpoint () = 338 - match 339 - Oauth.custom_provider ~name:"atp" ~authorize_url:"https://as.example/auth" 340 - ~token_url:"https://as.example/token" 341 - ~userinfo_url:"https://as.example/user" ~uid_field:"sub" 342 - ~par_endpoint:"http://as.example/par" () 343 - with 344 - | Error (`Msg msg) -> 345 - Alcotest.(check bool) 346 - "mentions par_endpoint" true 347 - (contains msg ~substring:"par_endpoint") 348 - | Ok _ -> Alcotest.fail "expected Error for http:// par_endpoint" 349 - 350 - (* ── Transport security ──────────────────────────────────────────── *) 351 - 352 - let test_custom_provider_rejects_http_token_url () = 353 - match 354 - Oauth.custom_provider ~name:"bad" ~authorize_url:"https://example.com/auth" 355 - ~token_url:"http://example.com/token" 356 - ~userinfo_url:"https://example.com/user" ~uid_field:"id" () 357 - with 358 - | Error (`Msg msg) -> 359 - Alcotest.(check bool) 360 - "mentions token_url" true 361 - (contains msg ~substring:"token_url") 362 - | Ok _ -> Alcotest.fail "expected Error for http:// token_url" 363 - 364 - let test_custom_provider_rejects_http_authorize_url () = 365 - match 366 - Oauth.custom_provider ~name:"bad" ~authorize_url:"http://example.com/auth" 367 - ~token_url:"https://example.com/token" 368 - ~userinfo_url:"https://example.com/user" ~uid_field:"id" () 369 - with 370 - | Error (`Msg msg) -> 371 - Alcotest.(check bool) 372 - "mentions authorize_url" true 373 - (contains msg ~substring:"authorize_url") 374 - | Ok _ -> Alcotest.fail "expected Error for http:// authorize_url" 375 - 376 - let test_custom_provider_rejects_http_userinfo_url () = 377 - match 378 - Oauth.custom_provider ~name:"bad" ~authorize_url:"https://example.com/auth" 379 - ~token_url:"https://example.com/token" 380 - ~userinfo_url:"http://example.com/user" ~uid_field:"id" () 381 - with 382 - | Error (`Msg msg) -> 383 - Alcotest.(check bool) 384 - "mentions userinfo_url" true 385 - (contains msg ~substring:"userinfo_url") 386 - | Ok _ -> Alcotest.fail "expected Error for http:// userinfo_url" 387 - 388 - let test_custom_provider_accepts_https () = 389 - match 390 - Oauth.custom_provider ~name:"good" ~authorize_url:"https://example.com/auth" 391 - ~token_url:"https://example.com/token" 392 - ~userinfo_url:"https://example.com/user" ~uid_field:"id" () 393 - with 394 - | Ok p -> Alcotest.(check string) "name" "good" p.name 395 - | Error (`Msg msg) -> Alcotest.failf "unexpected error: %s" msg 396 - 397 - let test_custom_provider_rejects_builtin_slug_collision () = 398 - (* "Git Hub" slugifies to "git-hub", not "github", so it should be OK *) 399 - (* But "GitHub" slugifies to "github" — collision with built-in *) 400 - match 401 - Oauth.custom_provider ~name:"GitHub" ~authorize_url:"https://evil.com/auth" 402 - ~token_url:"https://evil.com/token" ~userinfo_url:"https://evil.com/user" 403 - ~uid_field:"id" () 404 - with 405 - | Error (`Msg msg) -> 406 - Alcotest.(check bool) 407 - "mentions collision" true 408 - (contains msg ~substring:"collides") 409 - | Ok _ -> Alcotest.fail "expected Error for slug colliding with built-in" 410 - 411 - let test_custom_provider_rejects_google_slug_collision () = 412 - match 413 - Oauth.custom_provider ~name:"Google" ~authorize_url:"https://evil.com/auth" 414 - ~token_url:"https://evil.com/token" ~userinfo_url:"https://evil.com/user" 415 - ~uid_field:"id" () 416 - with 417 - | Error (`Msg msg) -> 418 - Alcotest.(check bool) 419 - "mentions collision" true 420 - (contains msg ~substring:"collides") 421 - | Ok _ -> Alcotest.fail "expected Error for slug colliding with built-in" 422 - 423 - let test_custom_provider_allows_non_colliding_slug () = 424 - match 425 - Oauth.custom_provider ~name:"My Corp SSO" 426 - ~authorize_url:"https://sso.corp.com/auth" 427 - ~token_url:"https://sso.corp.com/token" 428 - ~userinfo_url:"https://sso.corp.com/user" ~uid_field:"sub" () 429 - with 430 - | Ok p -> Alcotest.(check string) "name" "My Corp SSO" p.name 431 - | Error (`Msg msg) -> Alcotest.failf "unexpected error: %s" msg 432 - 433 - (* ── Redirect URI validation ─────────────────────────────────────── *) 434 - 435 - let test_redirect_uri_rejects_http () = 436 - match Oauth.redirect_uri "http://example.com/callback" with 437 - | Error (`Msg msg) -> 438 - Alcotest.(check bool) 439 - "mentions HTTPS" true 440 - (contains msg ~substring:"HTTPS") 441 - | Ok _ -> Alcotest.fail "expected Error for http:// redirect_uri" 442 - 443 - let test_redirect_uri_accepts_https () = 444 - match Oauth.redirect_uri "https://example.com/callback" with 445 - | Ok _ -> () 446 - | Error (`Msg msg) -> Alcotest.failf "unexpected error: %s" msg 447 - 448 - let test_redirect_uri_allows_localhost_http () = 449 - match Oauth.redirect_uri "http://localhost:8080/callback" with 450 - | Ok _ -> () 451 - | Error (`Msg msg) -> Alcotest.failf "unexpected error: %s" msg 452 - 453 - let test_redirect_uri_allows_127_http () = 454 - match Oauth.redirect_uri "http://127.0.0.1:3000/callback" with 455 - | Ok _ -> () 456 - | Error (`Msg msg) -> Alcotest.failf "unexpected error: %s" msg 457 - 458 - let test_redirect_uri_allows_ipv6_loopback () = 459 - match Oauth.redirect_uri "http://[::1]:8080/callback" with 460 - | Ok _ -> () 461 - | Error (`Msg msg) -> Alcotest.failf "unexpected error: %s" msg 462 - 463 - let test_redirect_uri_rejects_fragment () = 464 - match Oauth.redirect_uri "https://example.com/callback#frag" with 465 - | Error (`Msg msg) -> 466 - Alcotest.(check bool) 467 - "mentions fragment" true 468 - (contains msg ~substring:"fragment") 469 - | Ok _ -> Alcotest.fail "expected Error for URI with fragment" 470 - 471 - let test_redirect_uri_rejects_no_scheme () = 472 - match Oauth.redirect_uri "/callback" with 473 - | Error _ -> () 474 - | Ok _ -> Alcotest.fail "expected Error for relative URI" 475 - 476 - (* ── TLS verification enforcement ────────────────────────────────── *) 477 - 478 - let test_exchange_code_rejects_verify_tls_false () = 479 - Eio_main.run @@ fun env -> 480 - Eio.Switch.run @@ fun sw -> 481 - let http = Requests.v ~sw ~verify_tls:false env in 482 - let raised = ref false in 483 - (try 484 - ignore 485 - (Oauth.exchange_code http Oauth.Github 486 - ~client_auth: 487 - (Oauth.Client_auth.post ~client_id:"x" ~client_secret:"y") 488 - ~code:"z" 489 - ~redirect_uri:(redir "https://example.com/cb") 490 - ()) 491 - with Invalid_argument _ -> raised := true); 492 - Alcotest.(check bool) "raises Invalid_argument" true !raised 493 - 494 - let test_verify_tls_getter () = 495 - Eio_main.run @@ fun env -> 496 - Eio.Switch.run @@ fun sw -> 497 - let secure = Requests.v ~sw env in 498 - let insecure = Requests.v ~sw ~verify_tls:false env in 499 - Alcotest.(check bool) "default is true" true (Requests.verify_tls secure); 500 - Alcotest.(check bool) "false when set" false (Requests.verify_tls insecure) 501 - 502 - let suite = 503 - ( "regressions", 504 - [ 505 - Alcotest.test_case "authorization_url includes response_type" `Quick 506 - test_authorization_url_includes_response_type_code; 507 - Alcotest.test_case "parse_token_response ok" `Quick 508 - test_parse_token_response_ok; 509 - Alcotest.test_case "parse_token_response invalid json" `Quick 510 - test_parse_token_response_invalid_json; 511 - Alcotest.test_case "parse_token_response missing access_token" `Quick 512 - test_parse_token_response_missing_access_token; 513 - Alcotest.test_case "parse_token_response empty access_token" `Quick 514 - test_parse_token_response_empty_access_token; 515 - Alcotest.test_case "parse_token_response invalid format" `Quick 516 - test_parse_token_response_invalid_format; 517 - Alcotest.test_case "parse_token_response rejects mac" `Quick 518 - test_parse_token_response_rejects_mac_token; 519 - Alcotest.test_case "parse_token_response accepts bearer" `Quick 520 - test_parse_token_response_accepts_bearer; 521 - Alcotest.test_case "parse_token_response bearer case-insensitive" `Quick 522 - test_parse_token_response_accepts_bearer_case_insensitive; 523 - Alcotest.test_case "parse_token_response accepts missing token_type" 524 - `Quick test_parse_token_response_accepts_missing_token_type; 525 - Alcotest.test_case "provider_name is raw" `Quick test_provider_name_is_raw; 526 - Alcotest.test_case "provider_slug is path-safe" `Quick 527 - test_provider_slug_is_path_safe; 528 - Alcotest.test_case "custom_provider rejects http:// token_url" `Quick 529 - test_custom_provider_rejects_http_token_url; 530 - Alcotest.test_case "custom_provider rejects http:// authorize_url" `Quick 531 - test_custom_provider_rejects_http_authorize_url; 532 - Alcotest.test_case "custom_provider rejects http:// userinfo_url" `Quick 533 - test_custom_provider_rejects_http_userinfo_url; 534 - Alcotest.test_case "custom_provider accepts https://" `Quick 535 - test_custom_provider_accepts_https; 536 - Alcotest.test_case "custom_provider rejects github slug collision" `Quick 537 - test_custom_provider_rejects_builtin_slug_collision; 538 - Alcotest.test_case "custom_provider rejects google slug collision" `Quick 539 - test_custom_provider_rejects_google_slug_collision; 540 - Alcotest.test_case "custom_provider allows non-colliding slug" `Quick 541 - test_custom_provider_allows_non_colliding_slug; 542 - Alcotest.test_case "redirect_uri rejects http://" `Quick 543 - test_redirect_uri_rejects_http; 544 - Alcotest.test_case "redirect_uri accepts https://" `Quick 545 - test_redirect_uri_accepts_https; 546 - Alcotest.test_case "redirect_uri allows http://localhost" `Quick 547 - test_redirect_uri_allows_localhost_http; 548 - Alcotest.test_case "redirect_uri allows http://127.0.0.1" `Quick 549 - test_redirect_uri_allows_127_http; 550 - Alcotest.test_case "redirect_uri allows http://[::1]" `Quick 551 - test_redirect_uri_allows_ipv6_loopback; 552 - Alcotest.test_case "redirect_uri rejects fragment" `Quick 553 - test_redirect_uri_rejects_fragment; 554 - Alcotest.test_case "redirect_uri rejects no scheme" `Quick 555 - test_redirect_uri_rejects_no_scheme; 556 - Alcotest.test_case "Client_auth.none apply" `Quick 557 - test_client_auth_none_apply; 558 - Alcotest.test_case "Client_auth.post apply" `Quick 559 - test_client_auth_post_apply; 560 - Alcotest.test_case "Client_auth.basic apply" `Quick 561 - test_client_auth_basic_apply; 562 - Alcotest.test_case "Client_auth.basic percent-encodes special chars" 563 - `Quick test_client_auth_basic_percent_encodes_special_chars; 564 - Alcotest.test_case "Client_auth.client_id accessor" `Quick 565 - test_client_auth_client_id_accessor; 566 - Alcotest.test_case "PAR parse response ok" `Quick 567 - test_par_parse_response_ok; 568 - Alcotest.test_case "PAR parse response missing request_uri" `Quick 569 - test_par_parse_response_missing_request_uri; 570 - Alcotest.test_case "PAR parse response missing expires_in" `Quick 571 - test_par_parse_response_missing_expires_in; 572 - Alcotest.test_case "PAR parse response invalid json" `Quick 573 - test_par_parse_response_invalid_json; 574 - Alcotest.test_case "PAR push refuses when provider lacks endpoint" `Quick 575 - test_par_push_requires_par_endpoint; 576 - Alcotest.test_case 577 - "PAR authorization_url carries only client_id+request_uri" `Quick 578 - test_par_authorization_url_only_carries_client_id_and_request_uri; 579 - Alcotest.test_case "custom_provider accepts par_endpoint" `Quick 580 - test_custom_provider_accepts_par_endpoint; 581 - Alcotest.test_case "custom_provider rejects http:// par_endpoint" `Quick 582 - test_custom_provider_rejects_http_par_endpoint; 583 - Alcotest.test_case "exchange_code rejects verify_tls:false" `Quick 584 - test_exchange_code_rejects_verify_tls_false; 585 - Alcotest.test_case "verify_tls getter" `Quick test_verify_tls_getter; 586 - ] )