Skip to main content

fraiseql_core/security/auth_middleware/
config.rs

1//! Authentication configuration.
2
3use serde::{Deserialize, Serialize};
4
5use super::signing_key::SigningKey;
6
7// ============================================================================
8// Authentication Configuration
9// ============================================================================
10
11/// Authentication configuration
12///
13/// Defines what authentication requirements must be met for a request.
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct AuthConfig {
16    /// If true, authentication is required for all requests
17    pub required: bool,
18
19    /// Token lifetime in seconds (for validation purposes)
20    pub token_expiry_secs: u64,
21
22    /// Signing key for JWT signature verification.
23    ///
24    /// If `None`, signature verification is disabled (NOT RECOMMENDED for production).
25    /// Use `SigningKey::hs256()` or `SigningKey::rs256_pem()` to enable verification.
26    #[serde(skip)]
27    pub signing_key: Option<SigningKey>,
28
29    /// Expected issuer (iss claim).
30    ///
31    /// If set, tokens must have this value in their `iss` claim.
32    #[serde(default)]
33    pub issuer: Option<String>,
34
35    /// Expected audience (aud claim).
36    ///
37    /// If set, tokens must have this value in their `aud` claim.
38    #[serde(default)]
39    pub audience: Option<String>,
40
41    /// Clock skew tolerance in seconds.
42    ///
43    /// Allow this many seconds of clock difference when validating exp/nbf claims.
44    /// Default: 60 seconds
45    #[serde(default = "default_clock_skew")]
46    pub clock_skew_secs: u64,
47}
48
49pub(super) const fn default_clock_skew() -> u64 {
50    60
51}
52
53impl AuthConfig {
54    /// Create a permissive authentication configuration (auth optional)
55    ///
56    /// - Authentication optional
57    /// - Token expiry: 3600 seconds (1 hour)
58    /// - No signature verification (for testing only)
59    #[must_use]
60    pub const fn permissive() -> Self {
61        Self {
62            required:          false,
63            token_expiry_secs: 3600,
64            signing_key:       None,
65            issuer:            None,
66            audience:          None,
67            clock_skew_secs:   default_clock_skew(),
68        }
69    }
70
71    /// Create a standard authentication configuration (auth required)
72    ///
73    /// - Authentication required
74    /// - Token expiry: 3600 seconds (1 hour)
75    /// - No signature verification (configure `signing_key` for production)
76    #[must_use]
77    pub const fn standard() -> Self {
78        Self {
79            required:          true,
80            token_expiry_secs: 3600,
81            signing_key:       None,
82            issuer:            None,
83            audience:          None,
84            clock_skew_secs:   default_clock_skew(),
85        }
86    }
87
88    /// Create a strict authentication configuration (auth required, short expiry)
89    ///
90    /// - Authentication required
91    /// - Token expiry: 1800 seconds (30 minutes)
92    /// - No signature verification (configure `signing_key` for production)
93    #[must_use]
94    pub const fn strict() -> Self {
95        Self {
96            required:          true,
97            token_expiry_secs: 1800,
98            signing_key:       None,
99            issuer:            None,
100            audience:          None,
101            clock_skew_secs:   default_clock_skew(),
102        }
103    }
104
105    /// Create a configuration with HS256 signing key.
106    ///
107    /// This is the recommended configuration for production when using
108    /// symmetric key signing (internal services).
109    #[must_use]
110    pub fn with_hs256(secret: &str) -> Self {
111        Self {
112            required:          true,
113            token_expiry_secs: 3600,
114            signing_key:       Some(SigningKey::hs256(secret)),
115            issuer:            None,
116            audience:          None,
117            clock_skew_secs:   default_clock_skew(),
118        }
119    }
120
121    /// Create a configuration with RS256 signing key from PEM.
122    ///
123    /// This is the recommended configuration for production when using
124    /// asymmetric key signing (external identity providers).
125    #[must_use]
126    pub fn with_rs256_pem(pem: &str) -> Self {
127        Self {
128            required:          true,
129            token_expiry_secs: 3600,
130            signing_key:       Some(SigningKey::rs256_pem(pem)),
131            issuer:            None,
132            audience:          None,
133            clock_skew_secs:   default_clock_skew(),
134        }
135    }
136
137    /// Set the expected issuer.
138    #[must_use]
139    pub fn with_issuer(mut self, issuer: &str) -> Self {
140        self.issuer = Some(issuer.to_string());
141        self
142    }
143
144    /// Set the expected audience.
145    #[must_use]
146    pub fn with_audience(mut self, audience: &str) -> Self {
147        self.audience = Some(audience.to_string());
148        self
149    }
150
151    /// Check if signature verification is enabled.
152    #[must_use]
153    pub const fn has_signing_key(&self) -> bool {
154        self.signing_key.is_some()
155    }
156}