io_oauth/2.0/authorization-code-grant/
access-token-request.rs

1use std::borrow::Cow;
2
3use http::{header::CONTENT_TYPE, request};
4use io_http::v1_1::coroutines::Send;
5use io_stream::Io;
6use url::form_urlencoded::Serializer;
7
8use crate::v2_0::issue_access_token::{
9    AccessTokenResponse, IssueAccessTokenErrorParams, IssueAccessTokenSuccessParams,
10};
11
12#[cfg(feature = "pkce")]
13use super::pkce::PkceCodeVerifier;
14
15pub struct AccessTokenRequestParams<'a> {
16    pub code: Cow<'a, str>,
17    pub redirect_uri: Option<Cow<'a, str>>,
18    pub client_id: Cow<'a, str>,
19    #[cfg(feature = "pkce")]
20    pub pkce_code_verifier: Option<Cow<'a, PkceCodeVerifier>>,
21}
22
23impl<'a> AccessTokenRequestParams<'a> {
24    // SAFETY: this function exposes the code and the PKCE code
25    // verifier
26    pub fn to_form_url_encoded_serializer(&self) -> Serializer<'a, String> {
27        let mut serializer = Serializer::new(String::new());
28
29        serializer.append_pair("grant_type", "authorization_code");
30        serializer.append_pair("code", &self.code);
31
32        if let Some(uri) = &self.redirect_uri {
33            serializer.append_pair("redirect_uri", uri);
34        }
35
36        serializer.append_pair("client_id", &self.client_id);
37
38        #[cfg(feature = "pkce")]
39        if let Some(verifier) = &self.pkce_code_verifier {
40            let verifier = String::from_utf8_lossy(verifier.expose());
41            serializer.append_pair("code_verifier", &verifier);
42        }
43
44        serializer
45    }
46}
47
48impl ToString for AccessTokenRequestParams<'_> {
49    fn to_string(&self) -> String {
50        self.to_form_url_encoded_serializer().finish()
51    }
52}
53
54/// The authorization code grant type is used to obtain both access
55/// tokens and refresh tokens and is optimized for confidential
56/// clients. Since this is a redirection-based flow, the client must
57/// be capable of interacting with the resource owner's user-agent
58/// (typically a web browser) and capable of receiving incoming
59/// requests (via redirection) from the authorization server.
60///
61/// Refs: https://datatracker.ietf.org/doc/html/rfc6749#section-4.1
62///
63#[derive(Debug)]
64pub struct SendAccessTokenRequest(Send);
65
66impl SendAccessTokenRequest {
67    pub fn new(
68        request: request::Builder,
69        body: AccessTokenRequestParams<'_>,
70    ) -> http::Result<Self> {
71        let request = request
72            .header(CONTENT_TYPE, "application/x-www-form-urlencoded")
73            .body(body.to_string().into_bytes())?;
74
75        Ok(Self(Send::new(request)))
76    }
77
78    pub fn resume(
79        &mut self,
80        input: Option<Io>,
81    ) -> Result<serde_json::Result<AccessTokenResponse>, Io> {
82        let response = self.0.resume(input)?;
83        let body = response.body().as_slice();
84
85        if response.status().is_success() {
86            match IssueAccessTokenSuccessParams::try_from(body) {
87                Ok(res) => Ok(Ok(Ok(res))),
88                Err(err) => Ok(Err(err)),
89            }
90        } else {
91            match IssueAccessTokenErrorParams::try_from(body) {
92                Ok(res) => Ok(Ok(Err(res))),
93                Err(err) => Ok(Err(err)),
94            }
95        }
96    }
97}