use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AuthInfo {
pub scheme: AuthScheme,
#[serde(skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub oauth: Option<OAuthInfo>,
#[serde(flatten)]
pub params: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub enum AuthScheme {
#[default]
None,
Bearer,
OAuth2,
Custom(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OAuthInfo {
pub auth_url: String,
pub token_url: String,
pub client_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub scopes: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub redirect_uri: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pkce_method: Option<PkceMethod>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PkceMethod {
#[serde(rename = "plain")]
Plain,
#[serde(rename = "S256")]
S256,
}
impl AuthInfo {
pub fn none() -> Self {
Self {
scheme: AuthScheme::None,
token: None,
oauth: None,
params: HashMap::new(),
}
}
pub fn bearer(token: impl Into<String>) -> Self {
Self {
scheme: AuthScheme::Bearer,
token: Some(token.into()),
oauth: None,
params: HashMap::new(),
}
}
pub fn oauth2(oauth: OAuthInfo) -> Self {
Self {
scheme: AuthScheme::OAuth2,
token: None,
oauth: Some(oauth),
params: HashMap::new(),
}
}
pub fn is_required(&self) -> bool {
!matches!(self.scheme, AuthScheme::None)
}
pub fn authorization_header(&self) -> Option<String> {
match (&self.scheme, &self.token) {
(AuthScheme::Bearer, Some(token)) => Some(format!("Bearer {}", token)),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn auth_none() {
let auth = AuthInfo::none();
assert!(!auth.is_required());
assert_eq!(auth.authorization_header(), None);
}
#[test]
fn auth_bearer() {
let auth = AuthInfo::bearer("test-token");
assert!(auth.is_required());
assert_eq!(
auth.authorization_header(),
Some("Bearer test-token".to_string())
);
}
#[test]
fn oauth_info() {
let oauth = OAuthInfo {
auth_url: "https://auth.example.com/authorize".to_string(),
token_url: "https://auth.example.com/token".to_string(),
client_id: "test-client".to_string(),
scopes: Some(vec!["read".to_string(), "write".to_string()]),
redirect_uri: Some("http://localhost:8080/callback".to_string()),
pkce_method: Some(PkceMethod::S256),
};
let auth = AuthInfo::oauth2(oauth);
assert!(auth.is_required());
assert_eq!(auth.scheme, AuthScheme::OAuth2);
}
}