Skip to main content

openauth_plugins/device_authorization/routes/
verify.rs

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