greentic_oauth_core/
provider.rs

1use std::{error::Error, fmt};
2
3use crate::types::{OAuthFlowRequest, OAuthFlowResult, TokenHandleClaims, TokenSet};
4
5/// Convenience alias for provider interactions.
6pub type ProviderResult<T> = Result<T, ProviderError>;
7
8/// High-level trait all OAuth providers must implement for the broker.
9pub trait Provider: Send + Sync {
10    /// Authorization endpoint for the provider.
11    fn auth_url(&self) -> &str;
12    /// Token endpoint for the provider.
13    fn token_url(&self) -> &str;
14    /// Registered redirect URI the provider will callback with.
15    fn redirect_uri(&self) -> &str;
16    /// Build an authorization redirect response for a flow request.
17    fn build_authorize_redirect(
18        &self,
19        request: &OAuthFlowRequest,
20    ) -> ProviderResult<OAuthFlowResult>;
21    /// Exchange an authorization code for tokens.
22    ///
23    /// Providers SHOULD enforce PKCE when supported by the upstream
24    /// integration. If the caller generated a verifier during the authorize
25    /// redirect, it will be supplied via `pkce_verifier`; implementations are
26    /// free to ignore the hint when the upstream does not require it.
27    fn exchange_code(
28        &self,
29        claims: &TokenHandleClaims,
30        code: &str,
31        pkce_verifier: Option<&str>,
32    ) -> ProviderResult<TokenSet>;
33    /// Refresh an existing token set.
34    fn refresh(&self, claims: &TokenHandleClaims, refresh_token: &str) -> ProviderResult<TokenSet>;
35    /// Revoke an access or refresh token.
36    fn revoke(&self, claims: &TokenHandleClaims, token: &str) -> ProviderResult<()>;
37}
38
39/// Lightweight error type for provider implementers.
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct ProviderError {
42    kind: ProviderErrorKind,
43    message: Option<String>,
44}
45
46impl ProviderError {
47    /// Create a new error for the given kind with an optional detail message.
48    pub fn new(kind: ProviderErrorKind, message: impl Into<Option<String>>) -> Self {
49        Self {
50            kind,
51            message: message.into(),
52        }
53    }
54
55    /// Access the classification of this error.
56    pub fn kind(&self) -> ProviderErrorKind {
57        self.kind
58    }
59
60    /// Optional descriptive message supplied when the error was created.
61    pub fn message(&self) -> Option<&str> {
62        self.message.as_deref()
63    }
64}
65
66impl fmt::Display for ProviderError {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match &self.message {
69            Some(message) => write!(f, "{}: {}", self.kind, message),
70            None => write!(f, "{}", self.kind),
71        }
72    }
73}
74
75impl Error for ProviderError {}
76
77/// Classification of errors returned by providers.
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum ProviderErrorKind {
80    /// Misconfiguration or invalid request was issued.
81    Configuration,
82    /// Upstream transport or network error.
83    Transport,
84    /// Provider rejected the request due to lack of permissions.
85    Authorization,
86    /// Provider returned an unexpected payload.
87    InvalidResponse,
88    /// The operation is not supported by the provider implementation.
89    Unsupported,
90    /// Catch-all for miscellaneous failures.
91    Other,
92}
93
94impl fmt::Display for ProviderErrorKind {
95    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96        let label = match self {
97            ProviderErrorKind::Configuration => "configuration error",
98            ProviderErrorKind::Transport => "transport error",
99            ProviderErrorKind::Authorization => "authorization error",
100            ProviderErrorKind::InvalidResponse => "invalid response",
101            ProviderErrorKind::Unsupported => "unsupported operation",
102            ProviderErrorKind::Other => "provider error",
103        };
104        f.write_str(label)
105    }
106}