wechat_minapp/user/
credential.rs1use super::User;
2use super::user_info::{UserBuilder, UserInfo};
3#[allow(deprecated)]
4use aes::{
5 Aes128,
6 cipher::{BlockDecryptMut, KeyIvInit, block_padding::Pkcs7, generic_array::GenericArray},
7};
8use base64::{Engine, engine::general_purpose::STANDARD};
9use cbc::Decryptor;
10use hex::encode;
11use hmac::{Hmac, Mac};
12use http::{HeaderValue, Method, Request};
13use serde::{Deserialize, Serialize};
14use serde_json::from_slice;
15use sha2::Sha256;
16use std::collections::HashMap;
17use tracing::{debug, instrument};
18
19use crate::{Result, constants, error::Error::InternalServer, response::Response};
20
21type Aes128CbcDec = Decryptor<Aes128>;
22
23#[derive(Serialize, Deserialize, Clone)]
24pub struct Credential {
25 open_id: String,
26 session_key: String,
27 #[serde(skip_serializing_if = "Option::is_none")]
28 union_id: Option<String>,
29}
30
31impl Credential {
32 pub fn open_id(&self) -> &str {
33 &self.open_id
34 }
35
36 pub fn session_key(&self) -> &str {
37 &self.session_key
38 }
39
40 pub fn union_id(&self) -> Option<&str> {
41 self.union_id.as_deref()
42 }
43
44 #[instrument(skip(self, encrypted_data, iv))]
68 pub fn decrypt(&self, encrypted_data: &str, iv: &str) -> Result<UserInfo> {
69 debug!("encrypted_data: {}", encrypted_data);
70 debug!("iv: {}", iv);
71
72 let key = STANDARD.decode(self.session_key.as_bytes())?;
73 let iv = STANDARD.decode(iv.as_bytes())?;
74 #[allow(deprecated)]
75 let decryptor = Aes128CbcDec::new(
76 &GenericArray::clone_from_slice(&key),
77 &GenericArray::clone_from_slice(&iv),
78 );
79
80 let encrypted_data = STANDARD.decode(encrypted_data.as_bytes())?;
81
82 let buffer = decryptor.decrypt_padded_vec_mut::<Pkcs7>(&encrypted_data)?;
83
84 let builder = from_slice::<UserBuilder>(&buffer)?;
85
86 debug!("user builder: {:#?}", builder);
87
88 Ok(builder.build())
89 }
90}
91
92impl std::fmt::Debug for Credential {
93 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 f.debug_struct("Credential")
96 .field("open_id", &self.open_id)
97 .field("session_key", &"********")
98 .field("union_id", &self.union_id)
99 .finish()
100 }
101}
102
103#[derive(Deserialize)]
104pub(crate) struct CredentialBuilder {
105 #[serde(rename = "openid")]
106 open_id: String,
107 session_key: String,
108 #[serde(rename = "unionid")]
109 union_id: Option<String>,
110}
111
112impl CredentialBuilder {
113 pub(crate) fn build(self) -> Credential {
114 Credential {
115 open_id: self.open_id,
116 session_key: self.session_key,
117 union_id: self.union_id,
118 }
119 }
120}
121
122impl std::fmt::Debug for CredentialBuilder {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 f.debug_struct("CredentialBuilder")
125 .field("open_id", &self.open_id)
126 .field("session_key", &"********")
127 .field("union_id", &self.union_id)
128 .finish()
129 }
130}
131
132type HmacSha256 = Hmac<Sha256>;
133
134impl User {
135 #[instrument(skip(self, session_key, open_id))]
138 pub async fn check_session_key(&self, session_key: &str, open_id: &str) -> Result<()> {
139 let mut mac = HmacSha256::new_from_slice(session_key.as_bytes())?;
140 mac.update(b"");
141 let hasher = mac.finalize();
142 let signature = encode(hasher.into_bytes());
143
144 let mut map = HashMap::new();
145
146 map.insert("openid", open_id.to_string());
147 map.insert("signature", signature);
148 map.insert("sig_method", "hmac_sha256".into());
149 let mut url = url::Url::parse(constants::CHECK_SESSION_KEY_END_POINT)?;
150 url.query_pairs_mut().extend_pairs(&map);
151 let client = &self.client.client;
152 let query = serde_json::to_vec(&map)?;
153 let request = Request::builder()
154 .uri(url.as_str())
155 .method(Method::GET)
156 .header(
157 "User-Agent",
158 HeaderValue::from_static(constants::HTTP_CLIENT_USER_AGENT),
159 )
160 .body(query)?;
161
162 let response = client.execute(request).await?;
163
164 debug!("response: {:#?}", response);
165
166 if response.status().is_success() {
167 Ok(())
168 } else {
169 let (_parts, body) = response.into_parts();
170 let message = String::from_utf8_lossy(&body.to_vec()).to_string();
171 Err(crate::error::Error::InternalServer(message))
172 }
173 }
174
175 #[instrument(skip(self, open_id))]
178 pub async fn reset_session_key(&self, session_key: &str, open_id: &str) -> Result<Credential> {
179 let mut mac = HmacSha256::new_from_slice(session_key.as_bytes())?;
180 mac.update(b"");
181 let hasher = mac.finalize();
182 let signature = encode(hasher.into_bytes());
183
184 let mut map = HashMap::new();
185 map.insert("access_token", self.client.token().await?);
186 map.insert("openid", open_id.to_string());
187 map.insert("signature", signature);
188 map.insert("sig_method", "hmac_sha256".into());
189
190 let mut url = url::Url::parse(constants::RESET_SESSION_KEY_END_POINT)?;
191 url.query_pairs_mut().extend_pairs(&map);
192 let client = &self.client.client;
193 let query = serde_json::to_vec(&map)?;
194 let request = Request::builder()
195 .uri(url.as_str())
196 .method(Method::GET)
197 .header(
198 "User-Agent",
199 HeaderValue::from_static(constants::HTTP_CLIENT_USER_AGENT),
200 )
201 .body(query)?;
202
203 let response = client.execute(request).await?;
204 debug!("response: {:#?}", &response);
205
206 if response.status().is_success() {
207 let (_parts, body) = response.into_parts();
208 let json = serde_json::from_slice::<Response<CredentialBuilder>>(&body.to_vec())?;
209
210 let credential = json.extract()?.build();
211
212 debug!("credential: {:#?}", credential);
213
214 Ok(credential)
215 } else {
216 let (_parts, body) = response.into_parts();
217 let message = String::from_utf8_lossy(&body.to_vec()).to_string();
218 Err(InternalServer(message))
219 }
220 }
221}