openauth_plugins/device_authorization/routes/
verify.rs1use 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}