···140140 | Error _ ->
141141 Lwt.return_error @@ Errors.auth_required "Invalid authorization header"
142142143143- let access : verifier =
143143+ let bearer : verifier =
144144 fun {req; db} ->
145145 match parse_bearer req with
146146 | Ok jwt -> (
···160160 | Error _ ->
161161 Lwt.return_error @@ Errors.auth_required "Invalid authorization header"
162162163163+ let oauth : verifier =
164164+ fun {req; db} ->
165165+ match Dream.header req "Authorization" with
166166+ | None ->
167167+ Lwt.return_error @@ Errors.auth_required "missing authorization header"
168168+ | Some auth ->
169169+ if String.starts_with ~prefix:"DPoP " auth then
170170+ let token = String.sub auth 5 (String.length auth - 5) in
171171+ let dpop_header = Dream.header req "DPoP" in
172172+ let full_url = "https://" ^ Env.hostname ^ Dream.target req in
173173+ let%lwt dpop_result =
174174+ Oauth.Dpop.verify_dpop_proof ~nonce_state:!dpop_nonce_state
175175+ ~mthd:(Dream.method_to_string @@ Dream.method_ req)
176176+ ~url:full_url ~dpop_header ~access_token:token ()
177177+ in
178178+ match dpop_result with
179179+ | Error e ->
180180+ Lwt.return_error @@ Errors.auth_required ("dpop: " ^ e)
181181+ | Ok proof -> (
182182+ match Jwt.decode_jwt token with
183183+ | Error e ->
184184+ Lwt.return_error @@ Errors.auth_required e
185185+ | Ok (_header, claims) -> (
186186+ let open Yojson.Safe.Util in
187187+ try
188188+ let did = claims |> member "sub" |> to_string in
189189+ let exp = claims |> member "exp" |> to_int in
190190+ let jkt_claim =
191191+ claims |> member "cnf" |> member "jkt" |> to_string
192192+ in
193193+ if jkt_claim <> proof.jkt then
194194+ Lwt.return_error @@ Errors.auth_required "dpop key mismatch"
195195+ else
196196+ let now = int_of_float (Unix.gettimeofday ()) in
197197+ if exp < now then
198198+ Lwt.return_error @@ Errors.auth_required "token expired"
199199+ else
200200+ match Jwt.verify_jwt token Env.jwt_key with
201201+ | Error e ->
202202+ Lwt.return_error @@ Errors.auth_required e
203203+ | Ok _ ->
204204+ Lwt.return_ok (Access {did})
205205+ with _ ->
206206+ Lwt.return_error
207207+ @@ Errors.auth_required "malformed JWT claims" ) )
208208+ else bearer {req; db}
209209+163210 let refresh : verifier =
164211 fun {req; db} ->
165212 match parse_bearer req with
···189236 | Some ("Basic" :: _) ->
190237 admin ctx
191238 | Some ("Bearer" :: _) ->
192192- access ctx
239239+ bearer ctx
240240+ | Some ("DPoP" :: _) ->
241241+ oauth ctx
193242 | _ ->
194243 Lwt.return_error
195244 @@ Errors.auth_required ~name:"InvalidToken"
···198247 let any : verifier =
199248 fun ctx -> try authorization ctx with _ -> unauthenticated ctx
200249201201- type t = Unauthenticated | Admin | Access | Refresh | Authorization | Any
250250+ type t =
251251+ | Unauthenticated
252252+ | Admin
253253+ | Bearer
254254+ | Oauth
255255+ | Refresh
256256+ | Authorization
257257+ | Any
202258203259 let of_t = function
204260 | Unauthenticated ->
205261 unauthenticated
206262 | Admin ->
207263 admin
208208- | Access ->
209209- access
264264+ | Bearer ->
265265+ bearer
266266+ | Oauth ->
267267+ oauth
210268 | Refresh ->
211269 refresh
212270 | Authorization ->
-14
pegasus/lib/jwt.ml
···118118 let exp = now_s + Defaults.service_token_exp in
119119 let payload = service_jwt_to_yojson {iss= did; aud; lxm; exp} in
120120 sign_jwt payload signing_key
121121-122122-let extract_claim claims key =
123123- try
124124- let open Yojson.Safe.Util in
125125- let rec find_nested json keys =
126126- match keys with
127127- | [] ->
128128- Some json
129129- | k :: rest ->
130130- find_nested (json |> member k) rest
131131- in
132132- let keys = String.split_on_char '.' key in
133133- find_nested claims keys
134134- with _ -> None