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