feat: Implement oauth auth code and refresh token flows

This commit is contained in:
Ginger committed 2026-06-29 09:35:48 -04:00
1 parent fb6e5d2838
commit df3f646c92
39 files changed
+1052 -154

No files matched your search

+32 -5
View File
@@ -1,3 +1,5 @@
use std::time::{Duration, SystemTime};
use conduwuit::{
Err, debug_error, debug_warn, err, trace, utils,
utils::{ReadyExt, stream::TryIgnore},
@@ -240,12 +242,37 @@ impl super::Service {
/// Find out which user an access token belongs to. Will panic if the access
/// token is empty.
pub async fn find_from_token(
&self,
token: &str,
) -> conduwuit::Result<(OwnedUserId, OwnedDeviceId)> {
pub async fn find_from_token(&self, token: &str) -> Option<(OwnedUserId, OwnedDeviceId)> {
assert!(!token.is_empty(), "Empty access token");
self.db.token_userdeviceid.get(token).await.deserialized()
let user = self
.db
.token_userdeviceid
.get(token)
.await
.deserialized()
.ok()?;
// Check if the token has expired
if let Some(expires) = self
.db
.userdeviceid_tokenexpires
.qry(&user)
.await
.deserialized::<u64>()
.ok()
.map(Duration::from_secs)
{
let expires_at = SystemTime::UNIX_EPOCH
.checked_add(expires)
.expect("expiry time should not overflow SystemTime");
if SystemTime::now() > expires_at {
return None;
}
}
Some(user)
}
/// Returns an iterator over all users on this homeserver.
+1
View File
@@ -45,6 +45,7 @@ impl super::Service {
user_id,
&request.device_id,
"",
None,
request.initial_device_display_name.clone(),
None,
)
+25 -2
View File
@@ -1,4 +1,7 @@
use std::net::IpAddr;
use std::{
net::IpAddr,
time::{Duration, SystemTime},
};
use conduwuit::{
Err, utils,
@@ -22,6 +25,7 @@ impl super::Service {
user_id: &UserId,
device_id: &DeviceId,
token: &str,
token_max_age: Option<Duration>,
initial_device_display_name: Option<String>,
client_ip: Option<String>,
) -> conduwuit::Result<()> {
@@ -39,7 +43,8 @@ impl super::Service {
increment(&self.db.userid_devicelistversion, user_id.as_bytes());
self.db.userdeviceid_metadata.put(key, Json(device));
self.set_token(user_id, device_id, token).await
self.set_token(user_id, device_id, token, token_max_age)
.await
}
/// Removes a device from a user.
@@ -55,6 +60,7 @@ impl super::Service {
if let Ok(old_token) = self.db.userdeviceid_token.qry(&userdeviceid).await {
self.db.userdeviceid_token.del(userdeviceid);
self.db.token_userdeviceid.remove(&old_token);
self.db.userdeviceid_tokenexpires.del(userdeviceid);
}
// Remove todevice events
@@ -68,6 +74,9 @@ impl super::Service {
// TODO: Remove onetimekeys
// Remove OAuth session information
self.services.oauth.remove_session(user_id, device_id).await;
increment(&self.db.userid_devicelistversion, user_id.as_bytes());
self.db.userdeviceid_metadata.del(userdeviceid);
@@ -121,6 +130,7 @@ impl super::Service {
user_id: &UserId,
device_id: &DeviceId,
token: &str,
token_max_age: Option<Duration>,
) -> conduwuit::Result<()> {
let key = (user_id, device_id);
if self.db.userdeviceid_metadata.qry(&key).await.is_err() {
@@ -147,6 +157,7 @@ impl super::Service {
// Remove old token
if let Ok(old_token) = self.db.userdeviceid_token.qry(&key).await {
self.db.token_userdeviceid.remove(&old_token);
self.db.userdeviceid_tokenexpires.remove(&old_token);
// It will be removed from userdeviceid_token by the insert later
}
@@ -154,6 +165,18 @@ impl super::Service {
self.db.userdeviceid_token.put_raw(key, token);
self.db.token_userdeviceid.raw_put(token, key);
if let Some(max_age) = token_max_age {
let expires = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("system time should not be before the epoch")
.saturating_add(max_age)
.as_secs();
self.db.userdeviceid_tokenexpires.put(key, expires);
} else {
self.db.userdeviceid_tokenexpires.del(key);
}
Ok(())
}
+5 -1
View File
@@ -15,7 +15,7 @@ use database::Map;
use ruma::{UserId, api::error::ErrorKind, encryption::CrossSigningKey, serde::Raw};
use serde::{Deserialize, Serialize};
use crate::{Dep, account_data, admin, appservice, globals, rooms};
use crate::{Dep, account_data, admin, appservice, globals, oauth, rooms};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserSuspension {
@@ -51,6 +51,7 @@ struct Services {
admin: Dep<admin::Service>,
appservice: Dep<appservice::Service>,
globals: Dep<globals::Service>,
oauth: Dep<oauth::Service>,
state_accessor: Dep<rooms::state_accessor::Service>,
state_cache: Dep<rooms::state_cache::Service>,
}
@@ -64,6 +65,7 @@ struct Data {
logintoken_expiresatuserid: Arc<Map>,
todeviceid_events: Arc<Map>,
token_userdeviceid: Arc<Map>,
userdeviceid_tokenexpires: Arc<Map>,
userdeviceid_metadata: Arc<Map>,
userdeviceid_token: Arc<Map>,
userfilterid_filter: Arc<Map>,
@@ -91,6 +93,7 @@ impl crate::Service for Service {
admin: args.depend::<admin::Service>("admin"),
appservice: args.depend::<appservice::Service>("appservice"),
globals: args.depend::<globals::Service>("globals"),
oauth: args.depend::<oauth::Service>("oauth"),
state_accessor: args
.depend::<rooms::state_accessor::Service>("rooms::state_accessor"),
state_cache: args.depend::<rooms::state_cache::Service>("rooms::state_cache"),
@@ -120,6 +123,7 @@ impl crate::Service for Service {
userid_selfsigningkeyid: args.db["userid_selfsigningkeyid"].clone(),
userid_usersigningkeyid: args.db["userid_usersigningkeyid"].clone(),
useridprofilekey_value: args.db["useridprofilekey_value"].clone(),
userdeviceid_tokenexpires: args.db["userdeviceid_tokenexpires"].clone(),
},
}))
}