Skip to main content

sockudo_http/
auth.rs

1use crate::{Token, util};
2use sonic_rs::Value;
3
4/// Authentication data for socket connections
5#[derive(Debug, serde::Serialize)]
6pub struct SocketAuth {
7    pub auth: String,
8    #[serde(skip_serializing_if = "Option::is_none")]
9    pub channel_data: Option<String>,
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub shared_secret: Option<String>,
12}
13
14/// User authentication data
15#[derive(Debug, serde::Serialize)]
16pub struct UserAuth {
17    pub auth: String,
18    pub user_data: String,
19}
20
21/// Gets socket signature for channel authorization
22pub fn get_socket_signature(
23    sockudo: &crate::Sockudo,
24    token: &Token,
25    channel: &str,
26    socket_id: &str,
27    data: Option<&Value>,
28) -> crate::Result<SocketAuth> {
29    #[cfg(not(feature = "encryption"))]
30    let _ = sockudo;
31    let mut signature_data = vec![socket_id.to_string(), channel.to_string()];
32    let mut channel_data = None;
33
34    if let Some(data) = data {
35        let serialized = sonic_rs::to_string(data)?;
36        signature_data.push(serialized.clone());
37        channel_data = Some(serialized);
38    }
39
40    let auth_string = signature_data.join(":");
41    let signature = token.sign(&auth_string);
42    let auth = format!("{}:{}", token.key, signature);
43
44    #[cfg_attr(not(feature = "encryption"), allow(unused_mut))]
45    let mut result = SocketAuth {
46        auth,
47        channel_data,
48        shared_secret: None,
49    };
50
51    // Handle encrypted channels
52    if util::is_encrypted_channel(channel) {
53        #[cfg(feature = "encryption")]
54        {
55            if sockudo.config().encryption_master_key().is_none() {
56                return Err(crate::SockudoError::Encryption {
57                    message: "Cannot generate shared_secret because encryptionMasterKey is not set"
58                        .to_string(),
59                });
60            }
61
62            let shared_secret = sockudo.channel_shared_secret(channel)?;
63            result.shared_secret = Some(base64::Engine::encode(
64                &base64::engine::general_purpose::STANDARD,
65                shared_secret,
66            ));
67        }
68
69        #[cfg(not(feature = "encryption"))]
70        {
71            return Err(crate::SockudoError::Encryption {
72                message: "Encryption support is not enabled. Enable the 'encryption' feature to use encrypted channels.".to_string(),
73            });
74        }
75    }
76
77    Ok(result)
78}
79
80/// Gets socket signature for user authentication
81pub fn get_socket_signature_for_user(
82    token: &Token,
83    socket_id: &str,
84    user_data: &Value,
85) -> crate::Result<UserAuth> {
86    let serialized_user_data = sonic_rs::to_string(user_data)?;
87    let signature_string = format!("{}::user::{}", socket_id, serialized_user_data);
88    let signature = token.sign(&signature_string);
89
90    Ok(UserAuth {
91        auth: format!("{}:{}", token.key, signature),
92        user_data: serialized_user_data,
93    })
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use sonic_rs::json;
100
101    #[test]
102    fn test_get_socket_signature_for_user() {
103        let token = Token::new("test_key", "test_secret");
104        let user_data = json!({"id": "123", "name": "Test User"});
105
106        let result = get_socket_signature_for_user(&token, "123.456", &user_data).unwrap();
107
108        assert!(result.auth.starts_with("test_key:"));
109        assert!(result.user_data.contains("123"));
110    }
111
112    #[cfg(feature = "encryption")]
113    #[test]
114    fn test_encrypted_channel_auth_with_encryption() {
115        use crate::{Config, Sockudo};
116
117        // This test only runs when encryption is enabled
118        let config = Config::builder()
119            .app_id("test")
120            .key("test_key")
121            .secret("test_secret")
122            .encryption_master_key_base64("MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=")
123            .unwrap()
124            .build()
125            .unwrap();
126
127        let sockudo = Sockudo::new(config).unwrap();
128        let token = Token::new("test_key", "test_secret");
129
130        let result =
131            get_socket_signature(&sockudo, &token, "private-encrypted-test", "123.456", None)
132                .unwrap();
133
134        assert!(result.shared_secret.is_some());
135    }
136
137    #[cfg(not(feature = "encryption"))]
138    #[test]
139    fn test_encrypted_channel_auth_without_encryption() {
140        use crate::{Config, Sockudo};
141
142        // This test only runs when encryption is disabled
143        let config = Config::builder()
144            .app_id("test")
145            .key("test_key")
146            .secret("test_secret")
147            .build()
148            .unwrap();
149
150        let sockudo = Sockudo::new(config).unwrap();
151        let token = Token::new("test_key", "test_secret");
152
153        let result =
154            get_socket_signature(&sockudo, &token, "private-encrypted-test", "123.456", None);
155
156        // Should fail with appropriate error message
157        assert!(result.is_err());
158        if let Err(crate::SockudoError::Encryption { message }) = result {
159            assert!(message.contains("Encryption support is not enabled"));
160        } else {
161            panic!("Expected encryption error");
162        }
163    }
164}