tangled
alpha
login
or
join now
futur.blue
/
pegasus
57
fork
atom
objective categorical abstract machine language personal data server
57
fork
atom
overview
issues
2
pulls
pipelines
Multi account session support
futur.blue
3 months ago
9b7df25f
c48efaf4
verified
This commit was signed with the committer's
known signature
.
futur.blue
SSH Key Fingerprint:
SHA256:QHGqHWNpqYyw9bt8KmPuJIyeZX9SZewBZ0PR1COtKQ0=
+144
-48
5 changed files
expand all
collapse all
unified
split
pegasus
lib
api
account_
login.ml
oauth_
authorize.ml
token.ml
oauth
queries.ml
session.ml
+1
-1
pegasus/lib/api/account_/login.ml
···
33
~props:{redirect_url; csrf_token; error= Some error}
34
| Some {did; _} ->
35
let%lwt () = Dream.invalidate_session ctx.req in
36
-
let%lwt () = Dream.set_session_field ctx.req "did" did in
37
Dream.redirect ctx.req redirect_url )
38
| _ ->
39
let redirect_url = "/account" in
···
33
~props:{redirect_url; csrf_token; error= Some error}
34
| Some {did; _} ->
35
let%lwt () = Dream.invalidate_session ctx.req in
36
+
let%lwt () = Session.log_in_did ctx.req did in
37
Dream.redirect ctx.req redirect_url )
38
| _ ->
39
let redirect_url = "/account" in
+53
-44
pegasus/lib/api/oauth_/authorize.ml
···
1
open Oauth
2
open Oauth.Types
3
4
-
let get_session_user (ctx : Xrpc.context) =
5
-
match Dream.session_field ctx.req "did" with
6
-
| Some did ->
7
-
Lwt.return_some did
8
-
| None ->
9
-
Lwt.return_none
10
-
11
let get_handler =
12
Xrpc.handler (fun ctx ->
13
let login_redirect =
···
62
; expires_at
63
; used= false }
64
in
65
-
match%lwt get_session_user ctx with
66
| None ->
67
login_redirect
68
-
| Some did -> (
69
-
match req.login_hint with
70
-
| Some hint when hint <> did ->
71
-
login_redirect
72
-
| _ ->
73
-
let%lwt handle =
74
-
match%lwt
75
-
Data_store.get_actor_by_identifier did ctx.db
76
-
with
77
-
| Some {handle; _} ->
78
-
Lwt.return handle
79
-
| None ->
80
-
Errors.internal_error
81
-
~msg:"failed to resolve user" ()
82
-
in
83
-
let scopes = String.split_on_char ' ' req.scope in
84
-
let csrf_token = Dream.csrf_token ctx.req in
85
-
let client_id_uri = Uri.of_string metadata.client_id in
86
-
let host, path =
87
-
( Uri.host_with_default client_id_uri
88
-
~default:"unknown"
89
-
, Uri.path client_id_uri )
90
-
in
91
-
let client_url = (host, path) in
92
-
let client_name = metadata.client_name in
93
-
Util.render_html ~title:("Authorizing " ^ host)
94
-
(module Frontend.OauthAuthorizePage)
95
-
~props:
96
-
{ client_url
97
-
; client_name
98
-
; handle
99
-
; scopes
100
-
; code
101
-
; request_uri
102
-
; csrf_token } ) ) ) )
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
103
104
let post_handler =
105
Xrpc.handler (fun ctx ->
106
-
match%lwt get_session_user ctx with
107
| None ->
108
Errors.auth_required "missing authentication"
109
| Some user_did -> (
···
1
open Oauth
2
open Oauth.Types
3
0
0
0
0
0
0
0
4
let get_handler =
5
Xrpc.handler (fun ctx ->
6
let login_redirect =
···
55
; expires_at
56
; used= false }
57
in
58
+
match%lwt Session.Raw.get_session ctx.req with
59
| None ->
60
login_redirect
61
+
| Some session when session.logged_in_dids = [] ->
62
+
login_redirect
63
+
| Some {current_did; logged_in_dids} -> (
64
+
let%lwt did =
65
+
match req.login_hint with
66
+
| Some hint when List.mem hint logged_in_dids ->
67
+
let%lwt () =
68
+
if Some hint <> current_did then
69
+
Session.Raw.set_current_did ctx.req hint
70
+
else Lwt.return_unit
71
+
in
72
+
Lwt.return_some hint
73
+
| _ ->
74
+
Lwt.return current_did
75
+
in
76
+
match did with
77
+
| None ->
78
+
login_redirect
79
+
| Some did ->
80
+
let%lwt handle =
81
+
match%lwt
82
+
Data_store.get_actor_by_identifier did ctx.db
83
+
with
84
+
| Some {handle; _} ->
85
+
Lwt.return handle
86
+
| None ->
87
+
Errors.internal_error
88
+
~msg:"failed to resolve user" ()
89
+
in
90
+
let scopes = String.split_on_char ' ' req.scope in
91
+
let csrf_token = Dream.csrf_token ctx.req in
92
+
let client_id_uri =
93
+
Uri.of_string metadata.client_id
94
+
in
95
+
let host, path =
96
+
( Uri.host_with_default client_id_uri
97
+
~default:"unknown"
98
+
, Uri.path client_id_uri )
99
+
in
100
+
let client_url = (host, path) in
101
+
let client_name = metadata.client_name in
102
+
Util.render_html ~title:("Authorizing " ^ host)
103
+
(module Frontend.OauthAuthorizePage)
104
+
~props:
105
+
{ client_url
106
+
; client_name
107
+
; handle
108
+
; scopes
109
+
; code
110
+
; request_uri
111
+
; csrf_token } ) ) ) )
112
113
let post_handler =
114
Xrpc.handler (fun ctx ->
115
+
match%lwt Session.Raw.get_current_did ctx.req with
116
| None ->
117
Errors.auth_required "missing authentication"
118
| Some user_did -> (
+1
pegasus/lib/api/oauth_/token.ml
···
170
Queries.update_oauth_token ctx.db
171
~old_refresh_token:refresh_token
172
~new_refresh_token:new_refresh ~expires_at:new_expires_at
0
173
in
174
Dream.json ~headers:[("Cache-Control", "no-store")]
175
@@ Yojson.Safe.to_string
···
170
Queries.update_oauth_token ctx.db
171
~old_refresh_token:refresh_token
172
~new_refresh_token:new_refresh ~expires_at:new_expires_at
173
+
~ip ~user_agent
174
in
175
Dream.json ~headers:[("Cache-Control", "no-store")]
176
@@ Yojson.Safe.to_string
+5
-3
pegasus/lib/oauth/queries.ml
···
103
record_out]
104
~refresh_token
105
106
-
let update_oauth_token conn ~old_refresh_token ~new_refresh_token ~expires_at =
0
107
Util.use_pool conn
108
@@ [%rapper
109
execute
110
{sql|
111
UPDATE oauth_tokens
112
SET refresh_token = %string{new_refresh_token},
113
-
expires_at = %int{expires_at}
0
114
WHERE refresh_token = %string{old_refresh_token}
115
|sql}]
116
-
~new_refresh_token ~expires_at ~old_refresh_token
117
118
let delete_oauth_token_by_refresh conn refresh_token =
119
Util.use_pool conn
···
103
record_out]
104
~refresh_token
105
106
+
let update_oauth_token conn ~old_refresh_token ~new_refresh_token ~expires_at
107
+
~ip ~user_agent =
108
Util.use_pool conn
109
@@ [%rapper
110
execute
111
{sql|
112
UPDATE oauth_tokens
113
SET refresh_token = %string{new_refresh_token},
114
+
expires_at = %int{expires_at}, last_ip = %string{ip},
115
+
last_user_agent = %string?{user_agent}
116
WHERE refresh_token = %string{old_refresh_token}
117
|sql}]
118
+
~new_refresh_token ~expires_at ~old_refresh_token ~ip ~user_agent
119
120
let delete_oauth_token_by_refresh conn refresh_token =
121
Util.use_pool conn
+84
pegasus/lib/session.ml
···
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
···
1
+
type data =
2
+
{ current_did: string option [@default None]
3
+
; logged_in_dids: string list [@default []] }
4
+
[@@deriving yojson {strict= false}]
5
+
6
+
let default = {current_did= None; logged_in_dids= []}
7
+
8
+
module Raw = struct
9
+
let set_session req data =
10
+
Dream.set_session_field req "pegasus.session"
11
+
(data_to_yojson data |> Yojson.Safe.to_string)
12
+
13
+
let get_session req =
14
+
match Dream.session_field req "pegasus.session" with
15
+
| Some data -> (
16
+
match
17
+
data_of_yojson (try Yojson.Safe.from_string data with _ -> `Null)
18
+
with
19
+
| Ok data ->
20
+
Lwt.return_some data
21
+
| Error _ ->
22
+
let%lwt () = set_session req default in
23
+
Lwt.return_some default )
24
+
| None ->
25
+
Lwt.return_none
26
+
27
+
let clear_session req = Dream.set_session_field req "pegasus.session" ""
28
+
29
+
let get_current_did req =
30
+
match%lwt get_session req with
31
+
| Some {current_did; _} when current_did <> None ->
32
+
Lwt.return current_did
33
+
| _ ->
34
+
Lwt.return_none
35
+
36
+
let set_current_did req did =
37
+
match%lwt get_session req with
38
+
| Some {logged_in_dids; _} ->
39
+
let%lwt () = set_session req {current_did= Some did; logged_in_dids} in
40
+
Lwt.return_unit
41
+
| None ->
42
+
Lwt.return_unit
43
+
44
+
let get_logged_in_dids req =
45
+
match%lwt get_session req with
46
+
| Some {logged_in_dids; _} ->
47
+
Lwt.return logged_in_dids
48
+
| None ->
49
+
Lwt.return []
50
+
51
+
let set_logged_in_dids req dids =
52
+
match%lwt get_session req with
53
+
| Some {current_did; _} ->
54
+
let%lwt () = set_session req {current_did; logged_in_dids= dids} in
55
+
Lwt.return_unit
56
+
| None ->
57
+
Lwt.return_unit
58
+
end
59
+
60
+
open Raw
61
+
62
+
let log_in_did ?(set_current = true) req did =
63
+
match%lwt get_session req with
64
+
| Some {current_did; logged_in_dids} ->
65
+
let%lwt () =
66
+
set_session req
67
+
{ current_did= (if set_current then Some did else current_did)
68
+
; logged_in_dids= did :: logged_in_dids }
69
+
in
70
+
Lwt.return_unit
71
+
| None ->
72
+
Lwt.return_unit
73
+
74
+
let log_out_did req did =
75
+
match%lwt get_session req with
76
+
| Some {current_did; logged_in_dids} ->
77
+
let%lwt () =
78
+
set_session req
79
+
{ current_did
80
+
; logged_in_dids= List.filter (fun d -> d <> did) logged_in_dids }
81
+
in
82
+
Lwt.return_unit
83
+
| None ->
84
+
Lwt.return_unit