Skip to main content

tuwunel_api_client/session/
refresh.rs

1use axum::extract::State;
2use ruma::api::client::session::refresh_token::v3::{Request, Response};
3use tuwunel_core::{Err, Result, debug_info, err};
4
5use crate::{ClientIp, Ruma};
6
7/// # `POST /_matrix/client/v3/refresh`
8///
9/// Refresh an access token.
10///
11/// <https://spec.matrix.org/v1.15/client-server-api/#post_matrixclientv3refresh>
12#[tracing::instrument(skip_all, fields(%client), name = "refresh_token")]
13pub(crate) async fn refresh_token_route(
14	State(services): State<crate::State>,
15	ClientIp(client): ClientIp,
16	body: Ruma<Request>,
17) -> Result<Response> {
18	let refresh_token_claim = body.body.refresh_token;
19
20	if !refresh_token_claim.starts_with("refresh_") {
21		return Err!(Request(Forbidden("Refresh token is malformed.")));
22	}
23
24	let token = services
25		.users
26		.classify_refresh_token(&refresh_token_claim)
27		.await;
28
29	let (user_id, device_id) = token
30		.owner()
31		.ok_or_else(|| err!(Request(Forbidden("Refresh token is unrecognized."))))?;
32
33	let (access_token, expires_in_ms) = services.users.generate_access_token(true);
34	let refresh_token = services
35		.users
36		.rotate_refresh_token(
37			&token,
38			&refresh_token_claim,
39			(&access_token, expires_in_ms),
40			services.users.locked_check(user_id),
41		)
42		.await?;
43
44	debug_info!(?user_id, ?device_id, ?expires_in_ms, "refreshed their access_token");
45
46	Ok(Response {
47		access_token,
48		refresh_token: Some(refresh_token),
49		expires_in_ms,
50	})
51}