use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OAuthRequirements {
pub provider: String,
pub provider_name: String,
pub scopes: Vec<String>,
#[serde(default)]
pub authorization_endpoint: Option<String>,
#[serde(default)]
pub token_endpoint: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum OAuthProvider {
Google,
GitHub,
Slack,
Discord,
Microsoft,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AuthState {
Unauthenticated,
Authenticating,
Authenticated,
Expired,
Revoked,
}
impl AuthState {
pub fn is_authenticated(&self) -> bool {
matches!(self, Self::Authenticated)
}
pub fn requires_reauth(&self) -> bool {
matches!(self, Self::Expired | Self::Revoked | Self::Unauthenticated)
}
pub fn is_in_progress(&self) -> bool {
matches!(self, Self::Authenticating)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AuthAvailability {
pub state: AuthState,
pub auth_required: bool,
pub can_authenticate: bool,
pub message: String,
pub action_hint: AuthActionHint,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AuthActionHint {
None,
StartOAuth,
RefreshToken,
Reauthenticate,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CredentialBinding {
pub plugin_id: String,
pub provider: String,
pub access_token: String,
#[serde(default)]
pub refresh_token: Option<String>,
#[serde(default)]
pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
pub scopes: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AuthInteractionRequest {
pub request_id: String,
pub plugin_id: String,
pub provider: String,
pub scopes: Vec<String>,
pub authorization_url: String,
pub redirect_uri: String,
#[serde(default)]
pub code_verifier: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AuthPrompt {
pub auth_id: String,
pub state: AuthState,
pub title: String,
pub description: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AuthStatusTransition {
pub auth_id: String,
pub previous_state: AuthState,
pub new_state: AuthState,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AuthInteractionResponse {
pub request_id: String,
pub result: AuthInteractionResult,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(tag = "status", rename_all = "snake_case")]
pub enum AuthInteractionResult {
Success {
code: String,
#[serde(default)]
state: Option<String>,
},
Denied {
reason: String,
},
Failed {
error: String,
},
Cancelled,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auth_state_is_authenticated() {
assert!(AuthState::Authenticated.is_authenticated());
assert!(!AuthState::Unauthenticated.is_authenticated());
assert!(!AuthState::Expired.is_authenticated());
}
#[test]
fn test_auth_state_requires_reauth() {
assert!(AuthState::Unauthenticated.requires_reauth());
assert!(AuthState::Expired.requires_reauth());
assert!(AuthState::Revoked.requires_reauth());
assert!(!AuthState::Authenticated.requires_reauth());
assert!(!AuthState::Authenticating.requires_reauth());
}
}