Skip to main content

modkit_auth/oauth2/
types.rs

1use serde::Deserialize;
2
3pub use modkit_utils::SecretString;
4
5/// `OAuth2` client authentication method.
6#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
7pub enum ClientAuthMethod {
8    /// HTTP Basic authentication (RFC 6749 §2.3.1).
9    /// `Authorization: Basic base64(client_id:client_secret)`
10    #[default]
11    Basic,
12    /// Credentials in the request body (RFC 6749 §2.3.1 alternative).
13    /// `client_id` and `client_secret` as form fields.
14    Form,
15}
16
17/// Deserialized `OAuth2` token endpoint response.
18///
19/// Only the fields required by the client credentials flow are included.
20/// Unknown fields are silently ignored during deserialization.
21///
22/// **Intentionally `Deserialize`-only** — `Serialize` is not derived to
23/// prevent accidental serialization of access tokens into logs or
24/// error messages.
25#[derive(Deserialize)]
26pub(crate) struct TokenResponse {
27    /// The access token issued by the authorization server.
28    pub access_token: String,
29    /// The lifetime in seconds of the access token (optional per RFC 6749).
30    #[serde(default)]
31    pub expires_in: Option<u64>,
32    /// The type of the token issued (optional; must be "Bearer" if present).
33    #[serde(default)]
34    pub token_type: Option<String>,
35}
36
37#[cfg(test)]
38#[cfg_attr(coverage_nightly, coverage(off))]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn default_auth_method_is_basic() {
44        assert_eq!(ClientAuthMethod::default(), ClientAuthMethod::Basic);
45    }
46
47    #[test]
48    fn deserialize_full_response() {
49        let json = r#"{"access_token":"tok","expires_in":3600,"token_type":"Bearer"}"#;
50        let r: TokenResponse = serde_json::from_str(json).unwrap();
51        assert_eq!(r.access_token, "tok");
52        assert_eq!(r.expires_in, Some(3600));
53        assert_eq!(r.token_type.as_deref(), Some("Bearer"));
54    }
55
56    #[test]
57    fn deserialize_minimal_response() {
58        let json = r#"{"access_token":"tok"}"#;
59        let r: TokenResponse = serde_json::from_str(json).unwrap();
60        assert_eq!(r.access_token, "tok");
61        assert!(r.expires_in.is_none());
62        assert!(r.token_type.is_none());
63    }
64
65    #[test]
66    fn deserialize_ignores_unknown_fields() {
67        let json = r#"{"access_token":"tok","scope":"read","refresh_token":"rt"}"#;
68        let r: TokenResponse = serde_json::from_str(json).unwrap();
69        assert_eq!(r.access_token, "tok");
70    }
71}