oauth 0.0.2

Universal OAuth 2.0 adapter for Rust web frameworks
Documentation
use std::convert::Infallible;
use std::sync::Arc;

use http::StatusCode;
use serde::Serialize;
use warp::Filter;

use crate::OAuthConfig;
use crate::error::OAuthError;
use crate::grants::{self, TokenRequest};

/// Create a Warp filter that serves the token endpoint.
pub fn oauth_filter(
    config: Arc<OAuthConfig>,
) -> impl Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone {
    let config_filter = warp::any().map(move || Arc::clone(&config));

    warp::path!("oauth" / "token")
        .and(warp::post())
        .and(warp::body::json())
        .and(config_filter)
        .and_then(handle_request)
}

async fn handle_request(
    request: TokenRequest,
    config: Arc<OAuthConfig>,
) -> Result<impl warp::Reply, Infallible> {
    let response = grants::issue_token(&config, request);
    let reply = match response {
        Ok(token) => {
            let body = warp::reply::json(&token);
            warp::reply::with_status(body, StatusCode::OK)
        }
        Err(err) => {
            let body = warp::reply::json(&ErrorBody::from(&err));
            warp::reply::with_status(body, err.status_code())
        }
    };

    Ok(reply)
}

#[derive(Serialize)]
struct ErrorBody {
    error: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    error_description: Option<String>,
}

impl From<&OAuthError> for ErrorBody {
    fn from(err: &OAuthError) -> Self {
        let error = match err {
            OAuthError::InvalidClient => "invalid_client",
            OAuthError::UnsupportedGrant(_) | OAuthError::NotImplemented(_) => {
                "unsupported_grant_type"
            }
            OAuthError::InvalidGrant(_) => "invalid_grant",
            OAuthError::InvalidScope(_) => "invalid_scope",
            OAuthError::Config(_) | OAuthError::TokenStore(_) | OAuthError::Internal(_) => {
                "server_error"
            }
        }
        .to_string();

        let error_description = match err {
            OAuthError::UnsupportedGrant(message)
            | OAuthError::InvalidGrant(message)
            | OAuthError::InvalidScope(message)
            | OAuthError::Internal(message) => Some(message.clone()),
            OAuthError::NotImplemented(message) => Some((*message).into()),
            _ => None,
        };

        Self {
            error,
            error_description,
        }
    }
}