objective categorical abstract machine language personal data server
65
fork

Configure Feed

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

Capture user IP/UA on /oauth/authorize rather than /token

futurGH a2cf2523 d579423d

+41 -16
+2 -2
frontend/src/templates/AccountPermissionsPage.mlx
··· 173 173 </div> 174 174 <span className="text-sm text-mist-80"> 175 175 (string 176 - ( device.last_refreshed_at 177 - ^ " from " ^ device.last_ip ) ) 176 + ( "Last active " ^ device.last_refreshed_at 177 + ^ {js| · |js} ^ device.last_ip ) ) 178 178 </span> 179 179 </li> ) 180 180 devices
+8 -1
pegasus/lib/api/oauth_/authorize.ml
··· 52 52 ; request_id 53 53 ; authorized_by= None 54 54 ; authorized_at= None 55 + ; authorized_ip= None 56 + ; authorized_user_agent= None 55 57 ; expires_at 56 58 ; used= false } 57 59 in ··· 157 159 else if code_rec.request_id <> request_id then 158 160 Errors.invalid_request "code not for this request" 159 161 else 162 + let ip = Util.request_ip ctx.req in 163 + let user_agent = 164 + Dream.header ctx.req "User-Agent" 165 + in 160 166 let%lwt () = 161 - Queries.activate_auth_code ctx.db code did 167 + Queries.activate_auth_code ctx.db code did ~ip 168 + ~user_agent 162 169 in 163 170 let params = 164 171 [ ("code", code)
+12 -3
pegasus/lib/api/oauth_/token.ml
··· 101 101 Jwt.sign_jwt claims ~typ:"at+jwt" 102 102 ~signing_key:Env.jwt_key 103 103 in 104 + let auth_ip = 105 + Option.value code_rec.authorized_ip ~default:ip 106 + in 107 + let auth_user_agent = 108 + match code_rec.authorized_user_agent with 109 + | Some ua -> 110 + Some ua 111 + | None -> 112 + user_agent 113 + in 104 114 let%lwt () = 105 115 Queries.insert_oauth_token ctx.db 106 116 { refresh_token ··· 111 121 ; created_at= now_ms 112 122 ; last_refreshed_at= now_ms 113 123 ; expires_at 114 - ; last_ip= ip 115 - ; last_user_agent= user_agent } 124 + ; last_ip= auth_ip 125 + ; last_user_agent= auth_user_agent } 116 126 in 117 127 let nonce = Dpop.next_nonce () in 118 128 Dream.json ··· 176 186 Queries.update_oauth_token ctx.db 177 187 ~old_refresh_token:refresh_token 178 188 ~new_refresh_token:new_refresh ~expires_at:new_expires_at 179 - ~ip ~user_agent 180 189 in 181 190 Dream.json ~headers:[("Cache-Control", "no-store")] 182 191 @@ Yojson.Safe.to_string
+3
pegasus/lib/migrations/data_store/002_track_oauth_sessions.sql
··· 4 4 ALTER TABLE oauth_tokens ADD COLUMN last_ip TEXT NOT NULL DEFAULT ''; 5 5 ALTER TABLE oauth_tokens ADD COLUMN last_user_agent TEXT; 6 6 7 + ALTER TABLE oauth_codes ADD COLUMN authorized_ip TEXT; 8 + ALTER TABLE oauth_codes ADD COLUMN authorized_user_agent TEXT; 9 + 7 10 CREATE INDEX IF NOT EXISTS oauth_tokens_did_idx ON oauth_tokens(did);
+14 -10
pegasus/lib/oauth/queries.ml
··· 44 44 get_opt 45 45 {sql| 46 46 SELECT @string{code}, @string{request_id}, @string?{authorized_by}, 47 - @int?{authorized_at}, @int{expires_at}, @bool{used} 47 + @int?{authorized_at}, @string?{authorized_ip}, 48 + @string?{authorized_user_agent}, @int{expires_at}, @bool{used} 48 49 FROM oauth_codes 49 50 WHERE code = %string{code} 50 51 |sql} 51 52 record_out] 52 53 ~code 53 54 54 - let activate_auth_code conn code did = 55 + let activate_auth_code conn code did ~ip ~user_agent = 55 56 let authorized_at = Util.now_ms () in 56 57 Util.use_pool conn 57 58 @@ [%rapper ··· 59 60 {sql| 60 61 UPDATE oauth_codes 61 62 SET authorized_by = %string{did}, 62 - authorized_at = %int{authorized_at} 63 + authorized_at = %int{authorized_at}, 64 + authorized_ip = %string{ip}, 65 + authorized_user_agent = %string?{user_agent} 63 66 WHERE code = %string{code} 64 67 |sql}] 65 - ~did ~authorized_at ~code 68 + ~did ~authorized_at ~ip ~user_agent ~code 66 69 67 70 let consume_auth_code conn code = 68 71 Util.use_pool conn ··· 73 76 SET used = 1 74 77 WHERE code = %string{code} AND used = 0 75 78 RETURNING @string{code}, @string{request_id}, @string?{authorized_by}, 76 - @int?{authorized_at}, @int{expires_at}, @bool{used} 79 + @int?{authorized_at}, @string?{authorized_ip}, 80 + @string?{authorized_user_agent}, @int{expires_at}, @bool{used} 77 81 |sql} 78 82 record_out] 79 83 ~code ··· 103 107 record_out] 104 108 ~refresh_token 105 109 106 - let update_oauth_token conn ~old_refresh_token ~new_refresh_token ~expires_at 107 - ~ip ~user_agent = 110 + let update_oauth_token conn ~old_refresh_token ~new_refresh_token ~expires_at = 111 + let now_ms = Util.now_ms () in 108 112 Util.use_pool conn 109 113 @@ [%rapper 110 114 execute 111 115 {sql| 112 116 UPDATE oauth_tokens 113 117 SET refresh_token = %string{new_refresh_token}, 114 - expires_at = %int{expires_at}, last_ip = %string{ip}, 115 - last_user_agent = %string?{user_agent} 118 + expires_at = %int{expires_at}, 119 + last_refreshed_at = %int{now_ms} 116 120 WHERE refresh_token = %string{old_refresh_token} 117 121 |sql}] 118 - ~new_refresh_token ~expires_at ~old_refresh_token ~ip ~user_agent 122 + ~new_refresh_token ~expires_at ~now_ms ~old_refresh_token 119 123 120 124 let delete_oauth_token_by_refresh conn refresh_token = 121 125 Util.use_pool conn
+2
pegasus/lib/oauth/types.ml
··· 69 69 ; request_id: string 70 70 ; authorized_by: string option [@default None] 71 71 ; authorized_at: int option [@default None] 72 + ; authorized_ip: string option [@default None] 73 + ; authorized_user_agent: string option [@default None] 72 74 ; expires_at: int 73 75 ; used: bool } 74 76 [@@deriving yojson {strict= false}]