phabricator_oauth/
client.rs

1use oauth2::{
2    basic::{
3        BasicClient, BasicErrorResponse, BasicRevocationErrorResponse, BasicTokenIntrospectionResponse,
4        BasicTokenResponse, BasicTokenType
5    },
6    http::{HeaderMap, Method},
7    reqwest::async_http_client,
8    url::Url,
9    AccessToken, AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, HttpRequest, RedirectUrl,
10    StandardRevocableToken, TokenUrl
11};
12use serde_json;
13use std::borrow::Cow;
14use serde::Deserialize;
15
16use crate::error::PhabOAuthError;
17use crate::user::PhabricatorUser;
18
19type Result<T> = std::result::Result<T, PhabOAuthError>;
20
21#[derive(Deserialize, Debug)]
22struct OAuthResponse<T> {
23    result: Option<T>,
24    // error_code: Option<String>,
25    // error_info: Option<String>
26}
27
28pub struct PhabOAuthClient {
29    redirect_url: String,
30    phabricator_url: String,
31    client: oauth2::Client<BasicErrorResponse, BasicTokenResponse, BasicTokenType, BasicTokenIntrospectionResponse, StandardRevocableToken, BasicRevocationErrorResponse>
32}
33
34impl PhabOAuthClient {
35    pub fn new(phid: String, secret: String, redirect_url: String, phabricator_url: String) -> Result<PhabOAuthClient> {
36        let client = BasicClient::new(
37            ClientId::new(phid),
38            Some(ClientSecret::new(secret)),
39            AuthUrl::new(format!("{}/oauthserver/auth/", phabricator_url))?,
40            Some(TokenUrl::new(format!("{}/oauthserver/token/", phabricator_url))?)
41        )
42        .set_redirect_uri(RedirectUrl::new(redirect_url.clone())?);
43
44        Ok(
45            PhabOAuthClient {
46                redirect_url,
47                phabricator_url,
48                client
49            }
50        )
51    }
52
53    pub fn get_auth_url(&self) -> Result<(Url, CsrfToken)> {
54        let url = RedirectUrl::new(self.redirect_url.clone())?;
55
56        let (auth_url, csrf_token) = self.client
57            .authorize_url(CsrfToken::new_random)
58            .set_redirect_uri(Cow::Owned(url))
59            .url();
60
61        Ok((auth_url, csrf_token))
62    }
63
64    pub async fn get_token(&self, code: String) -> Result<BasicTokenResponse> {
65        let token_response = self.client
66            .exchange_code(AuthorizationCode::new(code))
67            .request_async(async_http_client).await?;
68
69        Ok(token_response)
70    }
71
72    pub async fn get_user(&self, token: &AccessToken) -> Result<Option<PhabricatorUser>> {
73        let request_url = format!("{}/api/user.whoami?access_token={}", self.phabricator_url, token.secret());
74        let request = HttpRequest{
75            url: Url::parse(request_url.as_str())?,
76            headers: HeaderMap::new(),
77            method: Method::GET,
78            body: vec![]
79        };
80        let response = async_http_client(request).await?;
81        let json = String::from_utf8(response.body)?;
82        let user_result: OAuthResponse<PhabricatorUser> = serde_json::from_str(json.as_str())?;
83        Ok(user_result.result)
84    }
85}