mas_handlers/compat/
logout.rs
1use axum::{Json, response::IntoResponse};
8use axum_extra::typed_header::TypedHeader;
9use headers::{Authorization, authorization::Bearer};
10use hyper::StatusCode;
11use mas_axum_utils::sentry::SentryEventID;
12use mas_data_model::TokenType;
13use mas_storage::{
14 BoxClock, BoxRepository, BoxRng, Clock, RepositoryAccess,
15 compat::{CompatAccessTokenRepository, CompatSessionRepository},
16 queue::{QueueJobRepositoryExt as _, SyncDevicesJob},
17};
18use thiserror::Error;
19
20use super::MatrixError;
21use crate::{BoundActivityTracker, impl_from_error_for_route};
22
23#[derive(Error, Debug)]
24pub enum RouteError {
25 #[error(transparent)]
26 Internal(Box<dyn std::error::Error + Send + Sync + 'static>),
27
28 #[error("Missing access token")]
29 MissingAuthorization,
30
31 #[error("Invalid token format")]
32 TokenFormat(#[from] mas_data_model::TokenFormatError),
33
34 #[error("Invalid access token")]
35 InvalidAuthorization,
36}
37
38impl_from_error_for_route!(mas_storage::RepositoryError);
39
40impl IntoResponse for RouteError {
41 fn into_response(self) -> axum::response::Response {
42 let event_id = sentry::capture_error(&self);
43 let response = match self {
44 Self::Internal(_) => MatrixError {
45 errcode: "M_UNKNOWN",
46 error: "Internal error",
47 status: StatusCode::INTERNAL_SERVER_ERROR,
48 },
49 Self::MissingAuthorization => MatrixError {
50 errcode: "M_MISSING_TOKEN",
51 error: "Missing access token",
52 status: StatusCode::UNAUTHORIZED,
53 },
54 Self::InvalidAuthorization | Self::TokenFormat(_) => MatrixError {
55 errcode: "M_UNKNOWN_TOKEN",
56 error: "Invalid access token",
57 status: StatusCode::UNAUTHORIZED,
58 },
59 };
60
61 (SentryEventID::from(event_id), response).into_response()
62 }
63}
64
65#[tracing::instrument(name = "handlers.compat.logout.post", skip_all, err)]
66pub(crate) async fn post(
67 clock: BoxClock,
68 mut rng: BoxRng,
69 mut repo: BoxRepository,
70 activity_tracker: BoundActivityTracker,
71 maybe_authorization: Option<TypedHeader<Authorization<Bearer>>>,
72) -> Result<impl IntoResponse, RouteError> {
73 let TypedHeader(authorization) = maybe_authorization.ok_or(RouteError::MissingAuthorization)?;
74
75 let token = authorization.token();
76 let token_type = TokenType::check(token)?;
77
78 if token_type != TokenType::CompatAccessToken {
79 return Err(RouteError::InvalidAuthorization);
80 }
81
82 let token = repo
83 .compat_access_token()
84 .find_by_token(token)
85 .await?
86 .filter(|t| t.is_valid(clock.now()))
87 .ok_or(RouteError::InvalidAuthorization)?;
88
89 let session = repo
90 .compat_session()
91 .lookup(token.session_id)
92 .await?
93 .filter(|s| s.is_valid())
94 .ok_or(RouteError::InvalidAuthorization)?;
95
96 activity_tracker
97 .record_compat_session(&clock, &session)
98 .await;
99
100 let user = repo
101 .user()
102 .lookup(session.user_id)
103 .await?
104 .ok_or(RouteError::InvalidAuthorization)?;
106
107 repo.queue_job()
109 .schedule_job(&mut rng, &clock, SyncDevicesJob::new(&user))
110 .await?;
111
112 repo.compat_session().finish(&clock, session).await?;
113
114 repo.save().await?;
115
116 Ok(Json(serde_json::json!({})))
117}