rustauth_plugins/device_authorization/routes/
verify.rs1use http::{Method, StatusCode};
2use rustauth_core::api::{create_auth_endpoint, AuthEndpointOptions};
3use rustauth_core::plugin::PluginEndpoint;
4use serde::{Deserialize, Serialize};
5use url::form_urlencoded;
6
7use crate::device_authorization::errors::{oauth_error_response, OAuthDeviceError};
8use crate::device_authorization::store::DeviceCodeStore;
9
10#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
11pub struct DeviceVerificationResponse {
12 pub user_code: String,
13 pub status: String,
14}
15
16pub fn device_verify() -> PluginEndpoint {
17 create_auth_endpoint(
18 "/device",
19 Method::GET,
20 AuthEndpointOptions::new()
21 .operation_id("deviceVerify")
22 .openapi(super::openapi::device_verify_operation()),
23 |context, request| async move {
24 let Some(user_code) = query_param(request.uri().query(), "user_code") else {
25 return oauth_error_response(
26 StatusCode::BAD_REQUEST,
27 OAuthDeviceError::InvalidRequest,
28 "Invalid user code",
29 );
30 };
31 let adapter = context.require_adapter()?;
32 let store = DeviceCodeStore::new(adapter.as_ref());
33 let clean = super::clean_user_code(&user_code);
34 let Some(record) = store.find_by_user_code(&clean).await? else {
35 return oauth_error_response(
36 StatusCode::BAD_REQUEST,
37 OAuthDeviceError::InvalidRequest,
38 "Invalid user code",
39 );
40 };
41 if record.expires_at < time::OffsetDateTime::now_utc() {
42 return oauth_error_response(
43 StatusCode::BAD_REQUEST,
44 OAuthDeviceError::ExpiredToken,
45 "User code has expired",
46 );
47 }
48 super::json_response(
49 StatusCode::OK,
50 &DeviceVerificationResponse {
51 user_code,
52 status: record.status.as_str().to_owned(),
53 },
54 )
55 },
56 )
57}
58
59fn query_param(query: Option<&str>, name: &str) -> Option<String> {
60 form_urlencoded::parse(query?.as_bytes())
61 .find(|(key, _)| key == name)
62 .map(|(_, value)| value.into_owned())
63}