···115 let%lwt _ =
116 Sequencer.sequence_sync ctx.db ~did ~rev:commit.rev ~blocks ()
117 in
118- let access_jwt, refresh_jwt = Auth.generate_jwt did in
119 Dream.json @@ Yojson.Safe.to_string
120 @@ response_to_yojson {access_jwt; refresh_jwt; did; handle= input.handle} )
···115 let%lwt _ =
116 Sequencer.sequence_sync ctx.db ~did ~rev:commit.rev ~blocks ()
117 in
118+ let access_jwt, refresh_jwt = Jwt.generate_jwt did in
119 Dream.json @@ Yojson.Safe.to_string
120 @@ response_to_yojson {access_jwt; refresh_jwt; did; handle= input.handle} )
+1-1
pegasus/lib/api/server/createSession.ml
···26 Lwt_result.catch @@ fun () -> Data_store.try_login ~id ~password db
27 with
28 | Ok (Some actor) when Auth.verify_auth auth actor.did ->
29- let access_jwt, refresh_jwt = Auth.generate_jwt actor.did in
30 let active, status =
31 match actor.deactivated_at with
32 | None ->
···26 Lwt_result.catch @@ fun () -> Data_store.try_login ~id ~password db
27 with
28 | Ok (Some actor) when Auth.verify_auth auth actor.did ->
29+ let access_jwt, refresh_jwt = Jwt.generate_jwt actor.did in
30 let active, status =
31 match actor.deactivated_at with
32 | None ->
+3-2
pegasus/lib/api/server/getServiceAuth.ml
···10 | _ ->
11 Errors.invalid_request "missing aud or lxm"
12 in
13- let%lwt signing_key =
14 match%lwt Data_store.get_actor_by_identifier did db with
15 | Some {signing_key; _} ->
16 Lwt.return signing_key
17 | None ->
18 Errors.internal_error ~msg:"actor not found" ()
19 in
20- let token = Auth.generate_service_jwt ~did ~aud ~lxm ~signing_key in
021 Dream.json @@ Yojson.Safe.to_string @@ response_to_yojson {token} )
···10 | _ ->
11 Errors.invalid_request "missing aud or lxm"
12 in
13+ let%lwt signing_multikey =
14 match%lwt Data_store.get_actor_by_identifier did db with
15 | Some {signing_key; _} ->
16 Lwt.return signing_key
17 | None ->
18 Errors.internal_error ~msg:"actor not found" ()
19 in
20+ let signing_key = Kleidos.parse_multikey_str signing_multikey in
21+ let token = Jwt.generate_service_jwt ~did ~aud ~lxm ~signing_key in
22 Dream.json @@ Yojson.Safe.to_string @@ response_to_yojson {token} )
+1-1
pegasus/lib/api/server/refreshSession.ml
···18 in
19 let%lwt () = Data_store.revoke_token ~did ~jti db in
20 let%lwt {handle; did; active; status; _} = Auth.get_session_info did db in
21- let access_jwt, refresh_jwt = Auth.generate_jwt did in
22 Dream.json @@ Yojson.Safe.to_string
23 @@ response_to_yojson
24 {access_jwt; refresh_jwt; handle; did; active; status} )
···18 in
19 let%lwt () = Data_store.revoke_token ~did ~jti db in
20 let%lwt {handle; did; active; status; _} = Auth.get_session_info did db in
21+ let access_jwt, refresh_jwt = Jwt.generate_jwt did in
22 Dream.json @@ Yojson.Safe.to_string
23 @@ response_to_yojson
24 {access_jwt; refresh_jwt; handle; did; active; status} )
+15-76
pegasus/lib/auth.ml
···1type t = (module Rapper_helper.CONNECTION)
23-type symmetric_jwt =
4- {scope: string; aud: string; sub: string; iat: int; exp: int; jti: string}
5-6type session_info =
7 { handle: string
8 ; did: string
···19 | Access of {did: string}
20 | Refresh of {did: string; jti: string}
2122-let generate_jwt did =
23- let now_s = int_of_float (Unix.gettimeofday ()) in
24- let access_exp = now_s + (60 * 60 * 3) in
25- let refresh_exp = now_s + (60 * 60 * 24 * 7) in
26- let jti = Uuidm.v4_gen (Random.get_state ()) () |> Uuidm.to_string in
27- let access =
28- match
29- Jwto.encode Jwto.HS256 Env.jwt_secret
30- [ ("scope", "com.atproto.access")
31- ; ("aud", Env.did)
32- ; ("sub", did)
33- ; ("iat", Int.to_string now_s)
34- ; ("exp", Int.to_string access_exp)
35- ; ("jti", jti) ]
36- with
37- | Ok token ->
38- token
39- | Error err ->
40- failwith err
41- in
42- let refresh =
43- match
44- Jwto.encode Jwto.HS256 Env.jwt_secret
45- [ ("scope", "com.atproto.refresh")
46- ; ("aud", Env.did)
47- ; ("sub", did)
48- ; ("iat", Int.to_string now_s)
49- ; ("exp", Int.to_string refresh_exp)
50- ; ("jti", jti) ]
51- with
52- | Ok token ->
53- token
54- | Error err ->
55- failwith err
56- in
57- (access, refresh)
58-59-let generate_service_jwt ~did ~aud ~lxm ~signing_key =
60- let now_s = int_of_float (Unix.gettimeofday ()) in
61- let exp = now_s + (60 * 5) in
62- match
63- Jwto.encode Jwto.HS256 signing_key
64- [("iss", did); ("aud", aud); ("lxm", lxm); ("exp", Int.to_string exp)]
65- with
66- | Ok token ->
67- token
68- | Error err ->
69- failwith err
70-71let verify_bearer_jwt t token expected_scope =
72- match Jwto.decode_and_verify Env.jwt_secret token with
73 | Error err ->
74 Lwt.return_error err
75- | Ok jwt ->
76- let payload = Jwto.get_payload jwt in
77 let now_s = int_of_float (Unix.gettimeofday ()) in
78- let scope = List.assoc_opt "scope" payload |> Option.value ~default:"" in
79- let aud = List.assoc_opt "aud" payload |> Option.value ~default:"" in
80- let sub = List.assoc_opt "sub" payload |> Option.value ~default:"" in
81- let iat =
82- List.assoc_opt "iat" payload
83- |> Option.map int_of_string
84- |> Option.value ~default:max_int
85- in
86- let exp =
87- List.assoc_opt "exp" payload
88- |> Option.map int_of_string |> Option.value ~default:0
89- in
90- let jti = List.assoc_opt "jti" payload |> Option.value ~default:"" in
91- if aud <> Env.did then Lwt.return_error "invalid aud"
92- else if sub = "" then Lwt.return_error "missing sub"
93- else if now_s < iat then Lwt.return_error "token issued in the future"
94- else if now_s > exp then Lwt.return_error "expired token"
95- else if scope <> expected_scope then Lwt.return_error "invalid scope"
96- else if jti = "" then Lwt.return_error "missing jti"
97 else
98- let%lwt revoked_at = Data_store.is_token_revoked t ~did:sub ~jti in
0099 if revoked_at <> None then Lwt.return_error "token revoked"
100- else Lwt.return_ok {scope; aud; sub; iat; exp; jti}
0101102let verify_auth ?(refresh = false) credentials did =
103 match credentials with
···1type t = (module Rapper_helper.CONNECTION)
20003type session_info =
4 { handle: string
5 ; did: string
···16 | Access of {did: string}
17 | Refresh of {did: string; jti: string}
18000000000000000000000000000000000000000000000000019let verify_bearer_jwt t token expected_scope =
20+ match Jwt.verify_jwt token Env.jwt_key with
21 | Error err ->
22 Lwt.return_error err
23+ | Ok (_, payload) -> (
24+ try
25 let now_s = int_of_float (Unix.gettimeofday ()) in
26+ let jwt = Jwt.symmetric_jwt_of_yojson payload |> Result.get_ok in
27+ if jwt.aud <> Env.did then Lwt.return_error "invalid aud"
28+ else if jwt.sub = "" then Lwt.return_error "missing sub"
29+ else if now_s < jwt.iat then Lwt.return_error "token issued in the future"
30+ else if now_s > jwt.exp then Lwt.return_error "expired token"
31+ else if jwt.scope <> expected_scope then Lwt.return_error "invalid scope"
32+ else if jwt.jti = "" then Lwt.return_error "missing jti"
00000000000033 else
34+ let%lwt revoked_at =
35+ Data_store.is_token_revoked t ~did:jwt.sub ~jti:jwt.jti
36+ in
37 if revoked_at <> None then Lwt.return_error "token revoked"
38+ else Lwt.return_ok jwt
39+ with _ -> Lwt.return_error "invalid token format" )
4041let verify_auth ?(refresh = false) credentials did =
42 match credentials with