1// Copyright 2024 New Vector Ltd.
2// Copyright 2023, 2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
67use axum::{
8 extract::{Query, State},
9 response::{Html, IntoResponse},
10};
11use mas_axum_utils::{InternalError, cookies::CookieJar};
12use mas_router::UrlBuilder;
13use mas_storage::{BoxClock, BoxRepository};
14use mas_templates::{
15 DeviceLinkContext, DeviceLinkFormField, FieldError, FormState, TemplateContext, Templates,
16};
17use serde::{Deserialize, Serialize};
1819use crate::PreferredLanguage;
2021#[derive(Serialize, Deserialize)]
22pub struct Params {
23#[serde(default)]
24code: Option<String>,
25}
2627#[tracing::instrument(name = "handlers.oauth2.device.link.get", skip_all)]
28pub(crate) async fn get(
29 clock: BoxClock,
30mut repo: BoxRepository,
31 PreferredLanguage(locale): PreferredLanguage,
32 State(templates): State<Templates>,
33 State(url_builder): State<UrlBuilder>,
34 cookie_jar: CookieJar,
35 Query(query): Query<Params>,
36) -> Result<impl IntoResponse, InternalError> {
37let mut form_state = FormState::from_form(&query);
3839// If we have a code in query, find it in the database
40if let Some(code) = &query.code {
41// Find the code in the database
42let code = code.to_uppercase();
43let grant = repo
44 .oauth2_device_code_grant()
45 .find_by_user_code(&code)
46 .await?
47// XXX: We should have different error messages for already exchanged and expired
48.filter(|grant| grant.is_pending())
49 .filter(|grant| grant.expires_at > clock.now());
5051if let Some(grant) = grant {
52// This is a valid code, redirect to the consent page
53 // This will in turn redirect to the login page if the user is not logged in
54let destination = url_builder.redirect(&mas_router::DeviceCodeConsent::new(grant.id));
5556return Ok((cookie_jar, destination).into_response());
57 }
5859// The code isn't valid, set an error on the form
60form_state = form_state.with_error_on_field(DeviceLinkFormField::Code, FieldError::Invalid);
61 }
6263// Rendre the form
64let ctx = DeviceLinkContext::new()
65 .with_form_state(form_state)
66 .with_language(locale);
6768let content = templates.render_device_link(&ctx)?;
6970Ok((cookie_jar, Html(content)).into_response())
71}