openauth-plugins 0.0.4

Official OpenAuth plugin modules.
Documentation
use http::{Method, StatusCode};
use openauth_core::api::{create_auth_endpoint, AuthEndpointOptions};
use openauth_core::plugin::PluginEndpoint;
use serde::{Deserialize, Serialize};
use url::form_urlencoded;

use crate::device_authorization::errors::{oauth_error_response, OAuthDeviceError};
use crate::device_authorization::routes::token::required_adapter;
use crate::device_authorization::store::DeviceCodeStore;

#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct DeviceVerificationResponse {
    pub user_code: String,
    pub status: String,
}

pub fn device_verify() -> PluginEndpoint {
    create_auth_endpoint(
        "/device",
        Method::GET,
        AuthEndpointOptions::new()
            .operation_id("deviceVerify")
            .openapi(super::openapi::device_verify_operation()),
        |_context, request| {
            Box::pin(async move {
                let Some(user_code) = query_param(request.uri().query(), "user_code") else {
                    return oauth_error_response(
                        StatusCode::BAD_REQUEST,
                        OAuthDeviceError::InvalidRequest,
                        "Invalid user code",
                    );
                };
                let adapter = required_adapter(_context)?;
                let store = DeviceCodeStore::new(adapter.as_ref());
                let clean = super::clean_user_code(&user_code);
                let Some(record) = store.find_by_user_code(&clean).await? else {
                    return oauth_error_response(
                        StatusCode::BAD_REQUEST,
                        OAuthDeviceError::InvalidRequest,
                        "Invalid user code",
                    );
                };
                if record.expires_at < time::OffsetDateTime::now_utc() {
                    return oauth_error_response(
                        StatusCode::BAD_REQUEST,
                        OAuthDeviceError::ExpiredToken,
                        "User code has expired",
                    );
                }
                super::json_response(
                    StatusCode::OK,
                    &DeviceVerificationResponse {
                        user_code,
                        status: record.status.as_str().to_owned(),
                    },
                )
            })
        },
    )
}

fn query_param(query: Option<&str>, name: &str) -> Option<String> {
    form_urlencoded::parse(query?.as_bytes())
        .find(|(key, _)| key == name)
        .map(|(_, value)| value.into_owned())
}