1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
use async_trait::async_trait;
use std::collections::HashMap;
use tracing::{event, instrument, Level};

use super::{Client, Response};
use crate::credentials::{AccessToken, Credentials};
use crate::error::Error::{self, Internal};

static AUTH_URI: &str = "https://api.weixin.qq.com/sns/jscode2session";
static ACCESS_TOKEN_URI: &str = "https://api.weixin.qq.com/cgi-bin/token";

#[async_trait]
pub trait Authenticate {
    async fn login(&self, code: &str) -> Result<Credentials, Error>;
}

#[async_trait]
pub trait GetAccessToken {
    async fn get_access_token(&self) -> Result<AccessToken, Error>;
}

#[async_trait]
impl Authenticate for Client {
    /// 登录凭证校验
    /// https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
    #[instrument(skip(self, code))]
    async fn login(&self, code: &str) -> Result<Credentials, Error> {
        event!(Level::DEBUG, "code: {}", code);

        let mut hash_map: HashMap<&str, &str> = HashMap::new();

        hash_map.insert("appid", &self.app_id);
        hash_map.insert("secret", &self.secret);
        hash_map.insert("js_code", code);
        hash_map.insert("grant_type", "authorization_code");

        let res = self.client.get(AUTH_URI).query(&hash_map).send().await?;

        event!(Level::DEBUG, "response: {:#?}", res);

        if res.status().is_success() {
            event!(Level::DEBUG, "get credentials");

            let res = res.json::<Response<Credentials>>().await?;

            let credential = res.get()?;

            event!(Level::DEBUG, "credentials: {:#?}", credential);

            Ok(credential)
        } else {
            Err(Internal(res.text().await?))
        }
    }
}

#[async_trait]
impl GetAccessToken for Client {
    /// 获取小程序全局唯一后台接口调用凭据(access_token)
    /// https://developers.weixin.qq.com/miniprogram/dev/api-backend/open-api/access-token/auth.getAccessToken.html
    #[instrument(skip(self))]
    async fn get_access_token(&self) -> Result<AccessToken, Error> {
        let mut hash_map: HashMap<&str, &str> = HashMap::new();

        hash_map.insert("appid", &self.app_id);
        hash_map.insert("secret", &self.secret);
        hash_map.insert("grant_type", "client_credential");

        let res = self
            .client
            .get(ACCESS_TOKEN_URI)
            .query(&hash_map)
            .send()
            .await?;

        event!(Level::DEBUG, "response: {:#?}", res);

        if res.status().is_success() {
            event!(Level::DEBUG, "get access_token");

            let res = res.json::<Response<AccessToken>>().await?;

            let access_token = res.get()?;

            event!(Level::DEBUG, "access_token: {:#?}", access_token);

            Ok(access_token)
        } else {
            Err(Internal(res.text().await?))
        }
    }
}