use crate::{SaasContext, Wechat, WechatResult};
use async_trait::async_trait;
use reqwest::Url;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum OauthScope {
#[serde(rename = "snsapi_base")]
Base = 1,
#[serde(rename = "snsapi_userinfo")]
UserInfo = 2,
}
impl ToString for OauthScope {
fn to_string(&self) -> String {
match self {
OauthScope::Base => "snsapi_base".into(),
OauthScope::UserInfo => "snsapi_userinfo".into(),
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct OauthUserInfo {
pub openid: String,
pub nickname: String,
pub sex: crate::menu::Gender,
pub province: String,
pub city: String,
pub country: String,
pub headimgurl: String,
pub privilege: Vec<String>,
pub unionid: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct OauthAccessToken {
pub access_token: String,
pub expires_in: i64,
pub refresh_token: String,
pub openid: String,
pub scope: OauthScope,
}
#[async_trait]
pub trait Oauth {
async fn get_authorize_url(
&self,
context: &SaasContext,
scope: &OauthScope,
state: &String,
) -> WechatResult<String>;
async fn get_authorize_access_token(
&self,
context: &SaasContext,
code: &String,
) -> WechatResult<OauthAccessToken>;
async fn refresh_authorize_access_token(
&self,
context: &SaasContext,
refresh_token: &String,
) -> WechatResult<OauthAccessToken>;
async fn verify_authorize_access_token(
&self,
context: &SaasContext,
openid: &String,
access_token: &String,
) -> WechatResult<()>;
async fn get_user_info_by_access_token(
&self,
context: &SaasContext,
openid: &String,
access_token: &String,
) -> WechatResult<OauthUserInfo>;
}
#[async_trait]
impl Oauth for Wechat {
async fn get_authorize_url(
&self,
context: &SaasContext,
scope: &OauthScope,
state: &String,
) -> WechatResult<String> {
let config = self.saas_resolver.resolve_config(self, context).await?;
let mut url = Url::parse("https://open.weixin.qq.com/connect/oauth2/authorize").unwrap();
{
let mut query_kv = url.query_pairs_mut();
query_kv.append_pair("appid", &config.app_id);
query_kv.append_pair("redirect_uri", &config.oauth_redirect_url);
query_kv.append_pair("response_type", "code");
query_kv.append_pair("scope", &scope.to_string());
query_kv.append_pair("state", state);
}
let url = format!("{}#wechat_redirect", url.to_string());
Ok(url)
}
async fn get_authorize_access_token(
&self,
context: &SaasContext,
code: &String,
) -> WechatResult<OauthAccessToken> {
use crate::req_utils::ToApiResult;
let config = self.saas_resolver.resolve_config(self, context).await?;
let mut url = Url::parse(
"https://api.weixin.qq.com/sns/oauth2/access_token?grant_type=authorization_code",
)
.unwrap();
{
let mut query_kv = url.query_pairs_mut();
query_kv.append_pair("appid", &config.app_id);
query_kv.append_pair("secret", &config.app_secret);
query_kv.append_pair("code", code);
}
let result: OauthAccessToken = reqwest::get(url).await?.text().await?.to_api_result()?;
Ok(result)
}
async fn refresh_authorize_access_token(
&self,
context: &SaasContext,
refresh_token: &String,
) -> WechatResult<OauthAccessToken> {
use crate::req_utils::ToApiResult;
let config = self.saas_resolver.resolve_config(self, context).await?;
let mut url = Url::parse(
"https://api.weixin.qq.com/sns/oauth2/refresh_token?grant_type=refresh_token",
)
.unwrap();
{
let mut query_kv = url.query_pairs_mut();
query_kv.append_pair("appid", &config.app_id);
query_kv.append_pair("refresh_token", refresh_token);
}
let result: OauthAccessToken = reqwest::get(url).await?.text().await?.to_api_result()?;
Ok(result)
}
async fn verify_authorize_access_token(
&self,
context: &SaasContext,
openid: &String,
access_token: &String,
) -> WechatResult<()> {
use crate::req_utils::{EmptyApiResult, ToApiResult};
let mut url = Url::parse("https://api.weixin.qq.com/sns/auth").unwrap();
{
let mut query_kv = url.query_pairs_mut();
query_kv.append_pair("openid", openid);
query_kv.append_pair("access_token", access_token);
}
let _: EmptyApiResult = reqwest::get(url).await?.text().await?.to_api_result()?;
Ok(())
}
async fn get_user_info_by_access_token(
&self,
context: &SaasContext,
openid: &String,
access_token: &String,
) -> WechatResult<OauthUserInfo> {
use crate::req_utils::{EmptyApiResult, ToApiResult};
let mut url = Url::parse("https://api.weixin.qq.com/sns/userinfo?lang=zh_CN").unwrap();
{
let mut query_kv = url.query_pairs_mut();
query_kv.append_pair("openid", openid);
query_kv.append_pair("access_token", access_token);
}
let user: OauthUserInfo = reqwest::get(url).await?.text().await?.to_api_result()?;
Ok(user)
}
}