mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-11-13 04:06:54 +00:00
fix: Return the correct error code for expired access tokens
This commit is contained in:
5 files changed
+44
-12
No files matched your search
+24
-4
@@ -1,6 +1,7 @@
|
||||
use std::any::{Any, TypeId};
|
||||
|
||||
use conduwuit::{Err, Result, err};
|
||||
use conduwuit::{Err, Error, Result, err};
|
||||
use http::StatusCode;
|
||||
use ruma::{
|
||||
DeviceId, OwnedDeviceId, OwnedServerName, OwnedUserId, UserId,
|
||||
api::{
|
||||
@@ -10,12 +11,15 @@ use ruma::{
|
||||
AuthScheme, NoAccessToken, NoAuthentication,
|
||||
},
|
||||
client,
|
||||
error::{ErrorKind, UnknownTokenErrorData},
|
||||
federation::authentication::ServerSignatures,
|
||||
},
|
||||
assign,
|
||||
};
|
||||
use service::{
|
||||
Services,
|
||||
server_keys::{PubKeyMap, PubKeys},
|
||||
users::AccessTokenStatus,
|
||||
};
|
||||
|
||||
use crate::{router::args::AuthQueryParams, service::appservice::RegistrationInfo};
|
||||
@@ -165,8 +169,20 @@ impl CheckAuth for AccessToken {
|
||||
if output.is_empty() {
|
||||
return Err!(Request(Unauthorized("Missing access token.")));
|
||||
}
|
||||
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
|
||||
if services
|
||||
.users
|
||||
@@ -177,7 +193,7 @@ impl CheckAuth for AccessToken {
|
||||
if !(route == TypeId::of::<client::session::logout::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.")));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +244,11 @@ impl CheckAuth for AccessToken {
|
||||
appservice_info: Box::new(appservice_info),
|
||||
})
|
||||
} else {
|
||||
Err!(Request(Unauthorized("Invalid access token.")))
|
||||
Err(Error::Request(
|
||||
ErrorKind::UnknownToken(UnknownTokenErrorData::new()),
|
||||
"Invalid token".into(),
|
||||
StatusCode::UNAUTHORIZED,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,7 +67,7 @@ impl crate::Service for Service {
|
||||
for (id, registration) in appservices {
|
||||
// During startup, resolve any token collisions in favour of appservices
|
||||
// by logging out conflicting user devices
|
||||
if let Some((user_id, device_id)) = self
|
||||
if let Some((user_id, device_id, _)) = self
|
||||
.services
|
||||
.users
|
||||
.find_from_token(®istration.as_token)
|
||||
|
||||
@@ -314,8 +314,10 @@ impl Service {
|
||||
.deserialized::<RefreshTokenInfo>()
|
||||
{
|
||||
(refresh_token_info.user_id, refresh_token_info.device_id)
|
||||
} else if let Some(user) = self.services.users.find_from_token(&token).await {
|
||||
user
|
||||
} else if let Some((user_id, device_id, _)) =
|
||||
self.services.users.find_from_token(&token).await
|
||||
{
|
||||
(user_id, device_id)
|
||||
} else {
|
||||
return Err!("Invalid token");
|
||||
};
|
||||
|
||||
@@ -14,6 +14,12 @@ use ruminuwuity::invite_permission_config::{FilterLevel, InvitePermissionConfigE
|
||||
|
||||
use crate::users::{HashedPassword, UserSuspension};
|
||||
|
||||
/// The status of an access token.
|
||||
pub enum AccessTokenStatus {
|
||||
Valid,
|
||||
Expired,
|
||||
}
|
||||
|
||||
impl super::Service {
|
||||
/// Returns true/false based on whether the recipient/receiving user has
|
||||
/// ignored the sender.
|
||||
@@ -242,10 +248,13 @@ 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) -> Option<(OwnedUserId, OwnedDeviceId)> {
|
||||
pub async fn find_from_token(
|
||||
&self,
|
||||
token: &str,
|
||||
) -> Option<(OwnedUserId, OwnedDeviceId, AccessTokenStatus)> {
|
||||
assert!(!token.is_empty(), "Empty access token");
|
||||
|
||||
let user = self
|
||||
let (user_id, device_id) = self
|
||||
.db
|
||||
.token_userdeviceid
|
||||
.get(token)
|
||||
@@ -257,7 +266,7 @@ impl super::Service {
|
||||
if let Some(expires) = self
|
||||
.db
|
||||
.userdeviceid_tokenexpires
|
||||
.qry(&user)
|
||||
.qry(&(&user_id, &device_id))
|
||||
.await
|
||||
.deserialized::<u64>()
|
||||
.ok()
|
||||
@@ -268,11 +277,11 @@ impl super::Service {
|
||||
.expect("expiry time should not overflow SystemTime");
|
||||
|
||||
if SystemTime::now() > expires_at {
|
||||
return None;
|
||||
return Some((user_id, device_id, AccessTokenStatus::Expired));
|
||||
}
|
||||
}
|
||||
|
||||
Some(user)
|
||||
Some((user_id, device_id, AccessTokenStatus::Valid))
|
||||
}
|
||||
|
||||
/// Returns an iterator over all users on this homeserver.
|
||||
|
||||
@@ -7,6 +7,7 @@ pub(super) mod profile;
|
||||
|
||||
use std::{mem, sync::Arc};
|
||||
|
||||
pub use account::AccessTokenStatus;
|
||||
use conduwuit::{
|
||||
Err, Error, Result, Server, err,
|
||||
utils::{self},
|
||||
|
||||
Reference in new issue
Block a user