objective categorical abstract machine language personal data server
65
fork

Configure Feed

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

/oauth/authorize

futurGH df46ef20 176fcb07

+167 -4
+2
bin/main.ml
··· 78 78 let%lwt () = Data_store.init db in 79 79 Dream.serve ~interface:"0.0.0.0" ~port:8008 80 80 @@ Dream.logger 81 + @@ Dream.set_secret (Env.jwt_key |> Kleidos.privkey_to_multikey) 82 + @@ Dream.cookie_sessions 81 83 @@ Xrpc.service_proxy_middleware db 82 84 @@ Xrpc.dpop_middleware @@ Dream.router 83 85 @@ List.map
+4
kleidos/kleidos.ml
··· 220 220 let pubkey_to_did_key pubkey : string = 221 221 let pubkey, (module Curve : CURVE) = pubkey in 222 222 Curve.pubkey_to_did_key pubkey 223 + 224 + let privkey_to_multikey privkey : string = 225 + let privkey, (module Curve : CURVE) = privkey in 226 + Curve.privkey_to_multikey privkey
+153
pegasus/lib/api/oauth_/authorize.ml
··· 1 + open Oauth.Types 2 + 3 + let get_session_user (ctx : Xrpc.context) = 4 + match Dream.session_field ctx.req "did" with 5 + | Some did -> 6 + Lwt.return_some did 7 + | None -> 8 + Lwt.return_none 9 + 10 + let get_handler = 11 + Xrpc.handler (fun ctx -> 12 + let return_url = Uri.pct_encode (Dream.target ctx.req) in 13 + let client_id = Dream.query ctx.req "client_id" in 14 + let request_uri = Dream.query ctx.req "request_uri" in 15 + match (client_id, request_uri) with 16 + | None, _ | _, None -> 17 + (* TODO: actually implement the page for this redirect *) 18 + Dream.redirect ctx.req ("/login?return_to=" ^ return_url) 19 + | Some client_id, Some request_uri -> ( 20 + let prefix = Oauth.Constants.request_uri_prefix in 21 + if not (String.starts_with ~prefix request_uri) then 22 + Dream.redirect ctx.req ("/login?return_to=" ^ return_url) 23 + else 24 + let request_id = 25 + String.sub request_uri (String.length prefix) 26 + (String.length request_uri - String.length prefix) 27 + in 28 + match%lwt Oauth.Queries.get_par_request ctx.db request_id with 29 + | None -> 30 + Dream.redirect ctx.req ("/login?return_to=" ^ return_url) 31 + | Some req_record -> ( 32 + if req_record.client_id <> client_id then 33 + Dream.redirect ctx.req ("/login?return_to=" ^ return_url) 34 + else 35 + let req = 36 + Yojson.Safe.from_string req_record.request_data 37 + |> par_request_of_yojson 38 + |> Result.map_error (fun _ -> 39 + Errors.internal_error 40 + ~msg:"failed to parse par request" () ) 41 + |> Result.get_ok 42 + in 43 + let%lwt client = 44 + try%lwt Oauth.Client.fetch_client_metadata client_id 45 + with _ -> 46 + Errors.internal_error 47 + ~msg:"failed to fetch client metadata" () 48 + in 49 + match%lwt get_session_user ctx with 50 + | None -> 51 + Dream.redirect ctx.req ("/login?return_to=" ^ return_url) 52 + | Some did -> ( 53 + match req.login_hint with 54 + | Some hint when hint <> did -> 55 + Dream.redirect ctx.req ("/login?return_to=" ^ return_url) 56 + | _ -> 57 + let%lwt handle = 58 + match%lwt 59 + Data_store.get_actor_by_identifier did ctx.db 60 + with 61 + | Some {handle; _} -> 62 + Lwt.return handle 63 + | None -> 64 + Errors.internal_error 65 + ~msg:"failed to resolve user" () 66 + in 67 + let scopes = String.split_on_char ' ' req.scope in 68 + let client_name = 69 + match client.client_name with 70 + | Some name -> 71 + name 72 + | None -> 73 + client_id 74 + in 75 + (* TODO: render authz page with client_name, handle, scopes, request_uri *) 76 + Dream.html "" ) ) ) ) 77 + 78 + let post_handler pool = 79 + Xrpc.handler (fun ctx -> 80 + match%lwt get_session_user ctx with 81 + | None -> 82 + Errors.auth_required "missing authentication" 83 + | Some user_did -> ( 84 + match%lwt Dream.form ctx.req with 85 + | `Ok fields -> ( 86 + let action = List.assoc_opt "action" fields in 87 + let request_uri = List.assoc_opt "request_uri" fields in 88 + if action <> Some "allow" || request_uri = None then 89 + Errors.invalid_request "invalid request" ; 90 + let request_uri = Option.get request_uri in 91 + let prefix = Oauth.Constants.request_uri_prefix in 92 + let request_id = 93 + String.sub request_uri (String.length prefix) 94 + (String.length request_uri - String.length prefix) 95 + in 96 + let%lwt stored_request = 97 + Oauth.Queries.get_par_request pool request_id 98 + in 99 + match stored_request with 100 + | None -> 101 + Errors.invalid_request "request expired" 102 + | Some req_record -> 103 + let req = 104 + Yojson.Safe.from_string req_record.request_data 105 + |> par_request_of_yojson 106 + |> Result.map_error (fun _ -> 107 + Errors.internal_error 108 + ~msg:"failed to parse stored request" () ) 109 + |> Result.get_ok 110 + in 111 + if Util.now_ms () > req_record.expires_at then 112 + Errors.invalid_request "request expired" 113 + else 114 + let code = 115 + "cod-" 116 + ^ Uuidm.to_string (Uuidm.v4_gen (Random.get_state ()) ()) 117 + in 118 + let expires_at = 119 + Util.now_ms () + Oauth.Constants.code_expiry_ms 120 + in 121 + let%lwt () = 122 + Oauth.Queries.insert_auth_code pool 123 + { code 124 + ; request_id 125 + ; authorized_by= Some user_did 126 + ; authorized_at= Some (Util.now_ms ()) 127 + ; expires_at 128 + ; used= false } 129 + in 130 + let params = 131 + [ ("code", code) 132 + ; ("state", req.state) 133 + ; ("iss", "https://" ^ Env.hostname) ] 134 + in 135 + let query = 136 + String.concat "&" 137 + (List.map 138 + (fun (k, v) -> k ^ "=" ^ Uri.pct_encode v) 139 + params ) 140 + in 141 + let separator = 142 + match req.response_mode with 143 + | Some "query" -> 144 + "?" 145 + | Some "fragment" -> 146 + "#" 147 + | _ -> 148 + "?" 149 + in 150 + let redirect_uri = req.redirect_uri ^ separator ^ query in 151 + Dream.redirect ctx.req redirect_uri ) 152 + | _ -> 153 + Errors.invalid_request "invalid request" ) )
+1 -3
pegasus/lib/api/oauth_/par.ml
··· 43 43 let request_id = 44 44 "req-" ^ (Uuidm.v4_gen (Random.get_state ()) () |> Uuidm.to_string) 45 45 in 46 - let request_uri = 47 - "urn:ietf:params:oauth:request_uri:" ^ request_id 48 - in 46 + let request_uri = Oauth.Constants.request_uri_prefix ^ request_id in 49 47 let expires_at = 50 48 Util.now_ms () + Oauth.Constants.par_request_ttl_ms 51 49 in
+4
pegasus/lib/oauth/constants.ml
··· 7 7 let jti_cache_size = 10_000 8 8 9 9 let par_request_ttl_ms = 300_000 10 + 11 + let code_expiry_ms = 300_000 12 + 13 + let request_uri_prefix = "urn:ietf:params:oauth:request_uri:"
+2 -1
pegasus/lib/oauth/queries.ml
··· 22 22 @string?{dpop_jkt}, @int{expires_at}, @int{created_at} 23 23 FROM oauth_requests 24 24 WHERE request_id = %string{request_id} 25 + AND expires_at > %int{now} 25 26 |sql} 26 27 record_out] 27 - ~request_id 28 + ~request_id ~now:(Util.now_ms ()) 28 29 29 30 let insert_auth_code conn code = 30 31 Util.use_pool conn
+1
pegasus/lib/oauth/types.ml
··· 1 1 type par_request = 2 2 { client_id: string 3 3 ; response_type: string 4 + ; response_mode: string option 4 5 ; redirect_uri: string 5 6 ; scope: string 6 7 ; state: string