objective categorical abstract machine language personal data server
65
fork

Configure Feed

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

fix dpop token parsing & check account active

futurGH 38c6ae45 a59fdcaf

+56 -53
+53 -50
pegasus/lib/auth.ml
··· 86 86 module Verifiers = struct 87 87 open struct 88 88 let parse_header req expected_type = 89 - match Dream.header req "authorization" with 89 + match Dream.header req "Authorization" with 90 90 | Some header -> ( 91 91 match String.split_on_char ' ' header with 92 92 | [typ; token] ··· 114 114 Error "invalid basic authorization header" 115 115 116 116 let parse_bearer req = parse_header req "Bearer" 117 + 118 + let parse_dpop req = parse_header req "DPoP" 117 119 end 118 120 119 121 type ctx = {req: Dream.request; db: Data_store.t} ··· 124 126 fun {req; _} -> 125 127 match Dream.header req "authorization" with 126 128 | Some _ -> 127 - Lwt.return_error @@ Errors.auth_required "Invalid authorization header" 129 + Lwt.return_error @@ Errors.auth_required "invalid authorization header" 128 130 | None -> 129 131 Lwt.return_ok Unauthenticated 130 132 ··· 136 138 | "admin", p when p = Env.admin_password -> 137 139 Lwt.return_ok Admin 138 140 | _ -> 139 - Lwt.return_error @@ Errors.auth_required "Invalid credentials" ) 141 + Lwt.return_error @@ Errors.auth_required "invalid credentials" ) 140 142 | Error _ -> 141 - Lwt.return_error @@ Errors.auth_required "Invalid authorization header" 143 + Lwt.return_error @@ Errors.auth_required "invalid authorization header" 142 144 143 145 let bearer : verifier = 144 146 fun {req; db} -> ··· 152 154 | Some {deactivated_at= Some _; _} -> 153 155 Lwt.return_error 154 156 @@ Errors.auth_required ~name:"AccountDeactivated" 155 - "Account is deactivated" 157 + "account is deactivated" 156 158 | None -> 157 - Lwt.return_error @@ Errors.auth_required "Invalid credentials" ) 159 + Lwt.return_error @@ Errors.auth_required "invalid credentials" ) 158 160 | Error _ -> 159 - Lwt.return_error @@ Errors.auth_required "Invalid credentials" ) 161 + Lwt.return_error @@ Errors.auth_required "invalid credentials" ) 160 162 | Error _ -> 161 - Lwt.return_error @@ Errors.auth_required "Invalid authorization header" 163 + Lwt.return_error @@ Errors.auth_required "invalid authorization header" 162 164 163 165 let oauth : verifier = 164 166 fun {req; db} -> 165 - match Dream.header req "Authorization" with 166 - | None -> 167 + match parse_dpop req with 168 + | Error _ -> 167 169 Lwt.return_error @@ Errors.auth_required "missing authorization header" 168 - | Some auth -> 169 - if String.starts_with ~prefix:"DPoP " auth then 170 - let token = String.sub auth 5 (String.length auth - 5) in 171 - let dpop_header = Dream.header req "DPoP" in 172 - let full_url = "https://" ^ Env.hostname ^ Dream.target req in 173 - let%lwt dpop_result = 174 - Oauth.Dpop.verify_dpop_proof ~nonce_state:!dpop_nonce_state 175 - ~mthd:(Dream.method_to_string @@ Dream.method_ req) 176 - ~url:full_url ~dpop_header ~access_token:token () 177 - in 178 - match dpop_result with 170 + | Ok token -> ( 171 + let dpop_header = Dream.header req "DPoP" in 172 + let full_url = "https://" ^ Env.hostname ^ Dream.target req in 173 + let%lwt dpop_result = 174 + Oauth.Dpop.verify_dpop_proof ~nonce_state:!dpop_nonce_state 175 + ~mthd:(Dream.method_to_string @@ Dream.method_ req) 176 + ~url:full_url ~dpop_header ~access_token:token () 177 + in 178 + match dpop_result with 179 + | Error e -> 180 + Lwt.return_error @@ Errors.auth_required ("dpop: " ^ e) 181 + | Ok proof -> ( 182 + match Jwt.verify_jwt token Env.jwt_key with 179 183 | Error e -> 180 - Lwt.return_error @@ Errors.auth_required ("dpop: " ^ e) 181 - | Ok proof -> ( 182 - match Jwt.decode_jwt token with 183 - | Error e -> 184 - Lwt.return_error @@ Errors.auth_required e 185 - | Ok (_header, claims) -> ( 186 - let open Yojson.Safe.Util in 187 - try 188 - let did = claims |> member "sub" |> to_string in 189 - let exp = claims |> member "exp" |> to_int in 190 - let jkt_claim = 191 - claims |> member "cnf" |> member "jkt" |> to_string 192 - in 193 - if jkt_claim <> proof.jkt then 194 - Lwt.return_error @@ Errors.auth_required "dpop key mismatch" 195 - else 196 - let now = int_of_float (Unix.gettimeofday ()) in 197 - if exp < now then 198 - Lwt.return_error @@ Errors.auth_required "token expired" 199 - else 200 - match Jwt.verify_jwt token Env.jwt_key with 201 - | Error e -> 202 - Lwt.return_error @@ Errors.auth_required e 203 - | Ok _ -> 204 - Lwt.return_ok (Access {did}) 205 - with _ -> 206 - Lwt.return_error 207 - @@ Errors.auth_required "malformed JWT claims" ) ) 208 - else bearer {req; db} 184 + Lwt.return_error @@ Errors.auth_required e 185 + | Ok (_header, claims) -> ( 186 + let open Yojson.Safe.Util in 187 + try 188 + let did = claims |> member "sub" |> to_string in 189 + let exp = claims |> member "exp" |> to_int in 190 + let jkt_claim = 191 + claims |> member "cnf" |> member "jkt" |> to_string 192 + in 193 + let now = int_of_float (Unix.gettimeofday ()) in 194 + if jkt_claim <> proof.jkt then 195 + Lwt.return_error @@ Errors.auth_required "dpop key mismatch" 196 + else if exp < now then 197 + Lwt.return_error @@ Errors.auth_required "token expired" 198 + else 199 + match%lwt Data_store.get_actor_by_identifier did db with 200 + | Some {deactivated_at= None; _} -> 201 + Lwt.return_ok (Access {did}) 202 + | Some {deactivated_at= Some _; _} -> 203 + Lwt.return_error 204 + @@ Errors.auth_required ~name:"AccountDeactivated" 205 + "account is deactivated" 206 + | None -> 207 + Lwt.return_error 208 + @@ Errors.auth_required "invalid credentials" 209 + with _ -> 210 + Lwt.return_error @@ Errors.auth_required "malformed JWT claims" 211 + ) ) ) 209 212 210 213 let refresh : verifier = 211 214 fun {req; db} ->
+3 -3
pegasus/lib/oauth/types.ml
··· 42 42 type dpop_proof = {jti: string; jkt: string; htm: string; htu: string} 43 43 [@@deriving yojson {strict= false}] 44 44 45 - type oauth_request_record = 45 + type oauth_request = 46 46 { request_id: string 47 47 ; client_id: string 48 48 ; request_data: string ··· 51 51 ; created_at: int } 52 52 [@@deriving yojson {strict= false}] 53 53 54 - type oauth_code_record = 54 + type oauth_code = 55 55 { code: string 56 56 ; request_id: string 57 57 ; authorized_by: string option ··· 60 60 ; used: bool } 61 61 [@@deriving yojson {strict= false}] 62 62 63 - type oauth_token_record = 63 + type oauth_token = 64 64 { id: int 65 65 ; token_id: string 66 66 ; refresh_token: string