···11+type request_error = AlreadyConfirmed
22+33+let request_email_confirmation (actor : Data_store.Types.actor) db =
44+ match actor.email_confirmed_at with
55+ | Some _ ->
66+ Lwt.return_error AlreadyConfirmed
77+ | None ->
88+ let code =
99+ "eml-"
1010+ ^ String.sub
1111+ Digestif.SHA256.(
1212+ digest_string (actor.did ^ Int.to_string @@ Util.now_ms ())
1313+ |> to_hex )
1414+ 0 8
1515+ in
1616+ let expires_at = Util.now_ms () + (10 * 60 * 1000) in
1717+ let%lwt () =
1818+ Data_store.set_auth_code ~did:actor.did ~code ~expires_at db
1919+ in
2020+ let%lwt () =
2121+ Util.send_email_or_log ~recipients:[To actor.email]
2222+ ~subject:(Printf.sprintf "Confirm email for %s" actor.handle)
2323+ ~body:
2424+ (Plain
2525+ (Printf.sprintf
2626+ "Confirm your email address using the following token: %s"
2727+ code ) )
2828+ in
2929+ Lwt.return_ok ()
3030+131let calc_key_did ctx = Some (Auth.get_authed_did_exn ctx.Xrpc.auth)
232333let handler =
···2151 | None ->
2252 Errors.internal_error ~msg:"actor not found" ()
2353 | Some actor -> (
2424- match actor.email_confirmed_at with
2525- | Some _ ->
5454+ match%lwt request_email_confirmation actor db with
5555+ | Error AlreadyConfirmed ->
2656 Errors.invalid_request ~name:"InvalidRequest"
2757 "email already confirmed"
2828- | None ->
2929- let code =
3030- "eml-"
3131- ^ String.sub
3232- Digestif.SHA256.(
3333- digest_string (did ^ Int.to_string @@ Util.now_ms ())
3434- |> to_hex )
3535- 0 8
3636- in
3737- let expires_at = Util.now_ms () + (10 * 60 * 1000) in
3838- let%lwt () = Data_store.set_auth_code ~did ~code ~expires_at db in
3939- let%lwt () =
4040- Util.send_email_or_log ~recipients:[To actor.email]
4141- ~subject:(Printf.sprintf "Confirm email for %s" actor.handle)
4242- ~body:
4343- (Plain
4444- (Printf.sprintf
4545- "Confirm your email address using the following token: \
4646- %s"
4747- code ) )
4848- in
5858+ | _ ->
4959 Dream.empty `OK ) )
+5-3
pegasus/lib/api/server/requestEmailUpdate.ml
···3434 actor.email
3535 in
3636 Util.send_email_or_log ~recipients:[To to_email]
3737- ~subject:(Printf.sprintf "Email confirmation for %s" actor.handle)
3737+ ~subject:(Printf.sprintf "Confirm email change for %s" actor.handle)
3838 ~body:
3939 (Plain
4040 (Printf.sprintf
4141- "Confirm your email address using the following token: %s" code )
4242- )
4141+ "Confirm that you would like to change your email address%s \
4242+ using the following token: %s"
4343+ (match pending_email with Some e -> " to " ^ e | None -> "")
4444+ code ) )
4345 else Lwt.return_unit
4446 in
4547 Lwt.return token_required
+37-28
pegasus/lib/session.ml
···55 ; admin_authenticated: bool [@default false] }
66[@@deriving yojson {strict= false}]
7788+type actor = Frontend.AccountSwitcher.actor =
99+ {did: string; handle: string; avatar_data_uri: string option}
1010+[@@deriving yojson {strict= false}]
1111+812let default =
913 { current_did= None
1014 ; logged_in_dids= []
···150154let list_logged_in_actors req db =
151155 match%lwt get_logged_in_dids req with
152156 | [] ->
153153- Lwt.return []
157157+ Lwt.return (None, [])
154158 | dids ->
155155- Lwt_list.filter_map_s
156156- (fun did ->
157157- match%lwt Data_store.get_actor_by_identifier did db with
158158- | Some {handle; _} -> (
159159- let actor : Frontend.OauthAuthorizePage.actor =
160160- {did; handle; avatar_data_uri= None}
161161- in
162162- let%lwt us = User_store.connect did in
163163- match%lwt
164164- User_store.get_record us "app.bsky.actor.profile/self"
165165- with
166166- | Some {value= profile; _} -> (
167167- match Mist.Lex.String_map.find_opt "avatar" profile with
168168- | Some (`BlobRef {ref; _}) -> (
169169- match%lwt User_store.get_blob us ref with
170170- | Some {data; mimetype; _}
171171- when String.starts_with ~prefix:"image/" mimetype ->
172172- Lwt.return_some
173173- { actor with
174174- avatar_data_uri=
175175- Some (Util.make_data_uri ~mimetype ~data) }
159159+ let%lwt current_did = Raw.get_current_did req in
160160+ let%lwt actors =
161161+ Lwt_list.filter_map_s
162162+ (fun did ->
163163+ match%lwt Data_store.get_actor_by_identifier did db with
164164+ | Some {handle; _} -> (
165165+ let actor = {did; handle; avatar_data_uri= None} in
166166+ let%lwt us = User_store.connect did in
167167+ match%lwt
168168+ User_store.get_record us "app.bsky.actor.profile/self"
169169+ with
170170+ | Some {value= profile; _} -> (
171171+ match Mist.Lex.String_map.find_opt "avatar" profile with
172172+ | Some (`BlobRef {ref; _}) -> (
173173+ match%lwt User_store.get_blob us ref with
174174+ | Some {data; mimetype; _}
175175+ when String.starts_with ~prefix:"image/" mimetype ->
176176+ Lwt.return_some
177177+ { actor with
178178+ avatar_data_uri=
179179+ Some (Util.make_data_uri ~mimetype ~data) }
180180+ | _ ->
181181+ Lwt.return_some actor )
176182 | _ ->
177183 Lwt.return_some actor )
178178- | _ ->
184184+ | None ->
179185 Lwt.return_some actor )
180180- | None ->
181181- Lwt.return_some actor )
182182- | _ ->
183183- Lwt.return_none )
184184- dids
186186+ | _ ->
187187+ Lwt.return_none )
188188+ dids
189189+ in
190190+ let current_actor =
191191+ List.find_opt (fun (a : actor) -> Some a.did = current_did) actors
192192+ in
193193+ Lwt.return (current_actor, actors)
185194186195let set_admin_authenticated req authenticated =
187196 match%lwt get_session req with