objective categorical abstract machine language personal data server

Use more accurate auth error names

futur.blue c43ec295 7e1604c6

verified
+77 -59
+77 -59
pegasus/lib/auth.ml
··· 34 else if jwt.sub = "" then Lwt.return_error "missing sub" 35 else if now_s < jwt.iat then 36 Lwt.return_error "token issued in the future" 37 - else if now_s > jwt.exp then Lwt.return_error "expired token" 38 else if jwt.scope <> expected_scope then 39 Lwt.return_error "invalid scope" 40 else if jwt.jti = "" then Lwt.return_error "missing jti" ··· 42 let%lwt revoked_at = 43 Data_store.is_token_revoked t ~did:jwt.sub ~jti:jwt.jti 44 in 45 - if revoked_at <> None then Lwt.return_error "token revoked" 46 else Lwt.return_ok jwt 47 with _ -> Lwt.return_error "invalid token format" ) 48 ··· 219 | "admin", p when p = Env.admin_password -> 220 Lwt.return_ok Admin 221 | _ -> 222 - Lwt.return_error @@ Errors.auth_required "invalid credentials" ) 223 | Error _ -> 224 Lwt.return_error @@ Errors.auth_required "invalid authorization header" 225 ··· 234 Lwt.return_ok (Access {did}) 235 | Some {deactivated_at= Some _; _} -> 236 Lwt.return_error 237 - @@ Errors.auth_required ~name:"AccountDeactivated" 238 "account is deactivated" 239 | None -> 240 - Lwt.return_error @@ Errors.auth_required "invalid credentials" ) 241 | Error _ -> 242 - Lwt.return_error @@ Errors.auth_required "invalid credentials" ) 243 | Error _ -> 244 Lwt.return_error @@ Errors.auth_required "invalid authorization header" 245 ··· 297 let scopes = Oauth.Scopes.of_string scope_str in 298 let now = int_of_float (Unix.gettimeofday ()) in 299 if jkt_claim <> proof.jkt then 300 - Lwt.return_error @@ Errors.auth_required "dpop key mismatch" 301 else if exp < now then 302 - Lwt.return_error @@ Errors.auth_required "token expired" 303 else if not (Oauth.Scopes.has_atproto scopes) then 304 Lwt.return_error 305 - @@ Errors.auth_required ~name:"InvalidToken" 306 "oauth token missing 'atproto' scope" 307 else 308 let%lwt session = ··· 311 Lwt.return_ok sess 312 with _ -> 313 Lwt.return_error 314 - @@ Errors.auth_required "invalid credentials" 315 in 316 match session with 317 | Ok {active= Some true; _} -> 318 Lwt.return_ok (OAuth {did; proof; scopes}) 319 | Ok _ -> 320 Lwt.return_error 321 - @@ Errors.auth_required ~name:"AccountDeactivated" 322 "account is deactivated" 323 | Error _ -> 324 Lwt.return_error 325 - @@ Errors.auth_required "invalid credentials" 326 with _ -> 327 Lwt.return_error @@ Errors.auth_required "malformed JWT claims" ) 328 ) ) ··· 341 raise (Errors.Redirect "/account") 342 | Some {deactivated_at= Some _; _} -> 343 Lwt.return_error 344 - @@ Errors.auth_required ~name:"AccountDeactivated" 345 "account is deactivated" 346 | None -> 347 let%lwt () = Session.Raw.clear_session req in 348 - Lwt.return_error @@ Errors.auth_required "no active session" ) 349 | None -> 350 Lwt.return_error @@ Errors.auth_required "no active session" 351 ··· 360 Lwt.return_ok (Refresh {did; jti}) 361 | Some {deactivated_at= Some _; _} -> 362 Lwt.return_error 363 - @@ Errors.auth_required ~name:"AccountDeactivated" 364 "account is deactivated" 365 | None -> 366 - Lwt.return_error @@ Errors.auth_required "invalid credentials" ) 367 - | Error "" | Error _ -> 368 - Lwt.return_error @@ Errors.auth_required "invalid credentials" ) 369 | Error _ -> 370 Lwt.return_error @@ Errors.auth_required "invalid authorization header" 371 ··· 376 Lwt.return_ok (Access {did}) 377 | Some {deactivated_at= Some _; _} -> 378 Lwt.return_error 379 - @@ Errors.auth_required ~name:"AccountDeactivated" 380 "account is deactivated" 381 | None -> 382 - Lwt.return_error @@ Errors.auth_required "invalid credentials" 383 in 384 let verify_with_key token pubkey_multibase did db = 385 - let pubkey = 386 - Kleidos.parse_multikey_str pubkey_multibase 387 - in 388 match Jwt.verify_jwt token ~pubkey with 389 | Ok _ -> 390 check_actor_status did db 391 | Error e -> 392 Dream.debug (fun log -> log "service jwt verification failed: %s" e) ; 393 Lwt.return_error 394 - @@ Errors.auth_required "jwt signature does not match jwt issuer" 395 in 396 fun {req; db} -> 397 match parse_bearer req with ··· 410 let lxm = payload |> member "lxm" |> to_string_option in 411 let now = int_of_float (Unix.gettimeofday ()) in 412 if exp < now then 413 - Lwt.return_error @@ Errors.auth_required "jwt expired" 414 else if aud <> Env.did then 415 Lwt.return_error 416 - @@ Errors.auth_required "jwt audience does not match service did" 417 else 418 let nsid = 419 (Dream.path [@warning "-3"]) req |> List.rev |> List.hd 420 in 421 - ( match lxm with 422 | Some l when l <> nsid && l <> "*" -> 423 Lwt.return_error 424 - @@ Errors.auth_required 425 ("jwt lxm " ^ l ^ " does not match " ^ nsid) 426 - | _ -> 427 let did = 428 match String.split_on_char '#' iss with 429 | did :: _ -> ··· 431 | [] -> 432 iss 433 in 434 - ( match%lwt Id_resolver.Did.resolve did with 435 | Error e -> 436 Dream.debug (fun log -> 437 log "failed to resolve did %s: %s" did e ) ; 438 Lwt.return_error 439 - @@ Errors.auth_required "could not resolve issuer did" 440 | Ok did_doc -> ( 441 match 442 Id_resolver.Did.Document.get_verification_key did_doc ··· 444 with 445 | None -> 446 Lwt.return_error 447 - @@ Errors.auth_required 448 - "missing or bad key in issuer did doc" 449 | Some pubkey_multibase -> ( 450 - match%lwt verify_with_key token pubkey_multibase did db with 451 | Ok creds -> 452 Lwt.return_ok creds 453 - | Error _ -> 454 - (* try again, skipping cache in case of key rotation *) 455 - ( match%lwt 456 - Id_resolver.Did.resolve ~skip_cache:true did 457 - with 458 - | Error _ -> 459 Lwt.return_error 460 - @@ Errors.auth_required 461 "jwt signature does not match jwt issuer" 462 - | Ok fresh_doc -> ( 463 - match 464 - Id_resolver.Did.Document.get_verification_key 465 - fresh_doc "#atproto" 466 - with 467 - | None -> 468 - Lwt.return_error 469 - @@ Errors.auth_required 470 - "jwt signature does not match jwt issuer" 471 - | Some fresh_pubkey_multibase 472 - when fresh_pubkey_multibase = pubkey_multibase -> 473 - Lwt.return_error 474 - @@ Errors.auth_required 475 - "jwt signature does not match jwt issuer" 476 - | Some fresh_pubkey_multibase -> 477 - verify_with_key token fresh_pubkey_multibase did 478 - db ) ) ) ) ) ) 479 with _ -> 480 - Lwt.return_error @@ Errors.auth_required "malformed service jwt" ) ) 481 482 let authorization : verifier = 483 fun ctx ->
··· 34 else if jwt.sub = "" then Lwt.return_error "missing sub" 35 else if now_s < jwt.iat then 36 Lwt.return_error "token issued in the future" 37 + else if now_s > jwt.exp then Lwt.return_error "ExpiredToken" 38 else if jwt.scope <> expected_scope then 39 Lwt.return_error "invalid scope" 40 else if jwt.jti = "" then Lwt.return_error "missing jti" ··· 42 let%lwt revoked_at = 43 Data_store.is_token_revoked t ~did:jwt.sub ~jti:jwt.jti 44 in 45 + if revoked_at <> None then Lwt.return_error "ExpiredToken" 46 else Lwt.return_ok jwt 47 with _ -> Lwt.return_error "invalid token format" ) 48 ··· 219 | "admin", p when p = Env.admin_password -> 220 Lwt.return_ok Admin 221 | _ -> 222 + Lwt.return_error @@ Errors.invalid_request "invalid credentials" ) 223 | Error _ -> 224 Lwt.return_error @@ Errors.auth_required "invalid authorization header" 225 ··· 234 Lwt.return_ok (Access {did}) 235 | Some {deactivated_at= Some _; _} -> 236 Lwt.return_error 237 + @@ Errors.invalid_request ~name:"AccountDeactivated" 238 "account is deactivated" 239 | None -> 240 + Lwt.return_error 241 + @@ Errors.internal_error ~msg:"invalid credentials" () ) 242 + | Error "ExpiredToken" -> 243 + Lwt.return_error 244 + @@ Errors.invalid_request ~name:"ExpiredToken" "token expired" 245 | Error _ -> 246 + Lwt.return_error 247 + @@ Errors.invalid_request ~name:"InvalidToken" "invalid credentials" ) 248 | Error _ -> 249 Lwt.return_error @@ Errors.auth_required "invalid authorization header" 250 ··· 302 let scopes = Oauth.Scopes.of_string scope_str in 303 let now = int_of_float (Unix.gettimeofday ()) in 304 if jkt_claim <> proof.jkt then 305 + Lwt.return_error @@ Errors.invalid_request "dpop key mismatch" 306 else if exp < now then 307 + Lwt.return_error 308 + @@ Errors.invalid_request ~name:"ExpiredToken" "token expired" 309 else if not (Oauth.Scopes.has_atproto scopes) then 310 Lwt.return_error 311 + @@ Errors.invalid_request ~name:"InvalidToken" 312 "oauth token missing 'atproto' scope" 313 else 314 let%lwt session = ··· 317 Lwt.return_ok sess 318 with _ -> 319 Lwt.return_error 320 + @@ Errors.internal_error ~msg:"invalid credentials" () 321 in 322 match session with 323 | Ok {active= Some true; _} -> 324 Lwt.return_ok (OAuth {did; proof; scopes}) 325 | Ok _ -> 326 Lwt.return_error 327 + @@ Errors.invalid_request ~name:"AccountDeactivated" 328 "account is deactivated" 329 | Error _ -> 330 Lwt.return_error 331 + @@ Errors.internal_error ~msg:"invalid credentials" () 332 with _ -> 333 Lwt.return_error @@ Errors.auth_required "malformed JWT claims" ) 334 ) ) ··· 347 raise (Errors.Redirect "/account") 348 | Some {deactivated_at= Some _; _} -> 349 Lwt.return_error 350 + @@ Errors.invalid_request ~name:"AccountDeactivated" 351 "account is deactivated" 352 | None -> 353 let%lwt () = Session.Raw.clear_session req in 354 + Lwt.return_error @@ Errors.internal_error ~msg:"no active session" () 355 + ) 356 | None -> 357 Lwt.return_error @@ Errors.auth_required "no active session" 358 ··· 367 Lwt.return_ok (Refresh {did; jti}) 368 | Some {deactivated_at= Some _; _} -> 369 Lwt.return_error 370 + @@ Errors.invalid_request ~name:"AccountDeactivated" 371 "account is deactivated" 372 | None -> 373 + Lwt.return_error 374 + @@ Errors.internal_error ~msg:"invalid credentials" () ) 375 + | Error "ExpiredToken" -> 376 + Lwt.return_error 377 + @@ Errors.invalid_request ~name:"ExpiredToken" "token expired" 378 + | Error _ -> 379 + Lwt.return_error 380 + @@ Errors.invalid_request ~name:"InvalidToken" "invalid credentials" ) 381 | Error _ -> 382 Lwt.return_error @@ Errors.auth_required "invalid authorization header" 383 ··· 388 Lwt.return_ok (Access {did}) 389 | Some {deactivated_at= Some _; _} -> 390 Lwt.return_error 391 + @@ Errors.invalid_request ~name:"AccountDeactivated" 392 "account is deactivated" 393 | None -> 394 + Lwt.return_error 395 + @@ Errors.internal_error ~msg:"invalid credentials" () 396 in 397 let verify_with_key token pubkey_multibase did db = 398 + let pubkey = Kleidos.parse_multikey_str pubkey_multibase in 399 match Jwt.verify_jwt token ~pubkey with 400 | Ok _ -> 401 check_actor_status did db 402 | Error e -> 403 Dream.debug (fun log -> log "service jwt verification failed: %s" e) ; 404 Lwt.return_error 405 + @@ Errors.invalid_request ~name:"InvalidToken" 406 + "jwt signature does not match jwt issuer" 407 in 408 fun {req; db} -> 409 match parse_bearer req with ··· 422 let lxm = payload |> member "lxm" |> to_string_option in 423 let now = int_of_float (Unix.gettimeofday ()) in 424 if exp < now then 425 + Lwt.return_error 426 + @@ Errors.invalid_request ~name:"ExpiredToken" "token expired" 427 else if aud <> Env.did then 428 Lwt.return_error 429 + @@ Errors.invalid_request ~name:"InvalidToken" 430 + "jwt audience does not match service did" 431 else 432 let nsid = 433 (Dream.path [@warning "-3"]) req |> List.rev |> List.hd 434 in 435 + match lxm with 436 | Some l when l <> nsid && l <> "*" -> 437 Lwt.return_error 438 + @@ Errors.invalid_request ~name:"InvalidToken" 439 ("jwt lxm " ^ l ^ " does not match " ^ nsid) 440 + | _ -> ( 441 let did = 442 match String.split_on_char '#' iss with 443 | did :: _ -> ··· 445 | [] -> 446 iss 447 in 448 + match%lwt Id_resolver.Did.resolve did with 449 | Error e -> 450 Dream.debug (fun log -> 451 log "failed to resolve did %s: %s" did e ) ; 452 Lwt.return_error 453 + @@ Errors.internal_error 454 + ~msg:"could not resolve jwt issuer did" () 455 | Ok did_doc -> ( 456 match 457 Id_resolver.Did.Document.get_verification_key did_doc ··· 459 with 460 | None -> 461 Lwt.return_error 462 + @@ Errors.internal_error 463 + ~msg:"missing or bad key in issuer did doc" () 464 | Some pubkey_multibase -> ( 465 + match%lwt 466 + verify_with_key token pubkey_multibase did db 467 + with 468 | Ok creds -> 469 Lwt.return_ok creds 470 + | Error _ -> ( 471 + (* try again, skipping cache in case of key rotation *) 472 + match%lwt 473 + Id_resolver.Did.resolve ~skip_cache:true did 474 + with 475 + | Error _ -> 476 + Lwt.return_error 477 + @@ Errors.invalid_request ~name:"InvalidToken" 478 + "jwt signature does not match jwt issuer" 479 + | Ok fresh_doc -> ( 480 + match 481 + Id_resolver.Did.Document.get_verification_key 482 + fresh_doc "#atproto" 483 + with 484 + | None -> 485 Lwt.return_error 486 + @@ Errors.invalid_request ~name:"InvalidToken" 487 "jwt signature does not match jwt issuer" 488 + | Some fresh_pubkey_multibase 489 + when fresh_pubkey_multibase = pubkey_multibase -> 490 + Lwt.return_error 491 + @@ Errors.invalid_request ~name:"InvalidToken" 492 + "jwt signature does not match jwt issuer" 493 + | Some fresh_pubkey_multibase -> 494 + verify_with_key token fresh_pubkey_multibase did 495 + db ) ) ) ) ) 496 with _ -> 497 + Lwt.return_error @@ Errors.invalid_request "malformed service jwt" ) 498 + ) 499 500 let authorization : verifier = 501 fun ctx ->