use axum::{
extract::{Query, State},
response::IntoResponse,
};
use axum_extra::response::Html;
use mas_axum_utils::{cookies::CookieJar, FancyError};
use mas_router::UrlBuilder;
use mas_storage::{BoxClock, BoxRepository};
use mas_templates::{
DeviceLinkContext, DeviceLinkFormField, FieldError, FormState, TemplateContext, Templates,
};
use serde::{Deserialize, Serialize};
use crate::PreferredLanguage;
#[derive(Serialize, Deserialize)]
pub struct Params {
code: String,
}
#[tracing::instrument(name = "handlers.oauth2.device.link.get", skip_all, err)]
pub(crate) async fn get(
clock: BoxClock,
mut repo: BoxRepository,
PreferredLanguage(locale): PreferredLanguage,
State(templates): State<Templates>,
State(url_builder): State<UrlBuilder>,
cookie_jar: CookieJar,
query: Option<Query<Params>>,
) -> Result<impl IntoResponse, FancyError> {
let mut form_state = FormState::default();
if let Some(Query(params)) = query {
form_state = FormState::from_form(¶ms);
let code = params.code.to_uppercase();
let grant = repo
.oauth2_device_code_grant()
.find_by_user_code(&code)
.await?
.filter(|grant| grant.is_pending())
.filter(|grant| grant.expires_at > clock.now());
if let Some(grant) = grant {
let destination = url_builder.redirect(&mas_router::DeviceCodeConsent::new(grant.id));
return Ok((cookie_jar, destination).into_response());
}
form_state = form_state.with_error_on_field(DeviceLinkFormField::Code, FieldError::Invalid);
};
let ctx = DeviceLinkContext::new()
.with_form_state(form_state)
.with_language(locale);
let content = templates.render_device_link(&ctx)?;
Ok((cookie_jar, Html(content)).into_response())
}