fix: Return the correct error code for expired access tokens

This commit is contained in:
Ginger
2026-05-26 14:27:09 -04:00
parent 3e8403de64
commit 7f36c44763
3 changed files with 41 additions and 11 deletions
+23 -4
View File
@@ -1,6 +1,7 @@
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use conduwuit::{Err, Result, err}; use conduwuit::{Err, Error, Result, err};
use http::StatusCode;
use ruma::{ use ruma::{
DeviceId, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId, DeviceId, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
api::{ api::{
@@ -10,12 +11,15 @@ use ruma::{
AuthScheme, NoAccessToken, NoAuthentication, AuthScheme, NoAccessToken, NoAuthentication,
}, },
client, client,
error::{ErrorKind, UnknownTokenErrorData},
federation::authentication::ServerSignatures, federation::authentication::ServerSignatures,
}, },
assign,
}; };
use service::{ use service::{
Services, Services,
server_keys::{PubKeyMap, PubKeys}, server_keys::{PubKeyMap, PubKeys},
users::AccessTokenStatus,
}; };
use crate::{router::args::AuthQueryParams, service::appservice::RegistrationInfo}; use crate::{router::args::AuthQueryParams, service::appservice::RegistrationInfo};
@@ -153,7 +157,18 @@ impl CheckAuth for AccessToken {
query: AuthQueryParams, query: AuthQueryParams,
route: TypeId, route: TypeId,
) -> Result<Self::Identity> { ) -> Result<Self::Identity> {
if let Some((sender_user, sender_device)) = services.users.find_from_token(&output).await { if let Some((sender_user, sender_device, status)) = services.users.find_from_token(&output).await {
// If the token is expired we return a soft logout
if matches!(status, AccessTokenStatus::Expired) {
return Err(Error::Request(
ErrorKind::UnknownToken(
assign!(UnknownTokenErrorData::new(), { soft_logout: true }),
),
"This token has expired".into(),
StatusCode::UNAUTHORIZED,
));
}
// Locked users can only use /logout and /logout/all // Locked users can only use /logout and /logout/all
if services if services
.users .users
@@ -164,7 +179,7 @@ impl CheckAuth for AccessToken {
if !(route == TypeId::of::<client::session::logout::v3::Request>() if !(route == TypeId::of::<client::session::logout::v3::Request>()
|| route == TypeId::of::<client::session::logout_all::v3::Request>()) || route == TypeId::of::<client::session::logout_all::v3::Request>())
{ {
return Err!(Request(Unauthorized("Your account is locked."))); return Err!(Request(UserLocked("Your account is locked.")));
} }
} }
@@ -215,7 +230,11 @@ impl CheckAuth for AccessToken {
appservice_info: Box::new(appservice_info), appservice_info: Box::new(appservice_info),
}) })
} else { } else {
Err!(Request(Unauthorized("Invalid access token."))) Err(Error::Request(
ErrorKind::UnknownToken(UnknownTokenErrorData::new()),
"Invalid token".into(),
StatusCode::UNAUTHORIZED,
))
} }
} }
} }
+1 -1
View File
@@ -67,7 +67,7 @@ impl crate::Service for Service {
for (id, registration) in appservices { for (id, registration) in appservices {
// During startup, resolve any token collisions in favour of appservices // During startup, resolve any token collisions in favour of appservices
// by logging out conflicting user devices // by logging out conflicting user devices
if let Some((user_id, device_id)) = self if let Some((user_id, device_id, _)) = self
.services .services
.users .users
.find_from_token(&registration.as_token) .find_from_token(&registration.as_token)
+17 -6
View File
@@ -57,6 +57,12 @@ impl HashedPassword {
} }
} }
/// The status of an access token.
pub enum AccessTokenStatus {
Valid,
Expired,
}
pub struct Service { pub struct Service {
services: Services, services: Services,
db: Data, db: Data,
@@ -347,7 +353,10 @@ impl Service {
pub async fn count(&self) -> usize { self.db.userid_password.count().await } pub async fn count(&self) -> usize { self.db.userid_password.count().await }
/// Find out which user an access token belongs to. /// Find out which user an access token belongs to.
pub async fn find_from_token(&self, token: &str) -> Option<(OwnedUserId, OwnedDeviceId)> { pub async fn find_from_token(
&self,
token: &str,
) -> Option<(OwnedUserId, OwnedDeviceId, AccessTokenStatus)> {
let user = self let user = self
.db .db
.token_userdeviceid .token_userdeviceid
@@ -357,11 +366,11 @@ impl Service {
.ok(); .ok();
// Check if the token has expired // Check if the token has expired
if let Some(user) = &user { if let Some((user_id, device_id)) = user {
if let Some(expires) = self if let Some(expires) = self
.db .db
.userdeviceid_tokenexpires .userdeviceid_tokenexpires
.qry(user) .qry(&(&user_id, &device_id))
.await .await
.deserialized::<u64>() .deserialized::<u64>()
.ok() .ok()
@@ -372,12 +381,14 @@ impl Service {
.expect("expiry time should not overflow SystemTime"); .expect("expiry time should not overflow SystemTime");
if SystemTime::now() > expires_at { if SystemTime::now() > expires_at {
return None; return Some((user_id, device_id, AccessTokenStatus::Expired));
} }
} }
}
user Some((user_id, device_id, AccessTokenStatus::Valid))
} else {
None
}
} }
/// Returns an iterator over all users on this homeserver. /// Returns an iterator over all users on this homeserver.