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