io_oauth/2.0/authorization-code-grant/
access-token-request.rs1use std::borrow::Cow;
2
3use http::{header::CONTENT_TYPE, request};
4use io_http::v1_1::coroutines::send::{SendHttp, SendHttpError, SendHttpResult};
5use io_stream::io::StreamIo;
6use thiserror::Error;
7use url::form_urlencoded::Serializer;
8
9use crate::v2_0::issue_access_token::{
10 AccessTokenResponse, IssueAccessTokenErrorParams, IssueAccessTokenSuccessParams,
11};
12
13#[cfg(feature = "pkce")]
14use super::pkce::PkceCodeVerifier;
15
16pub struct AccessTokenRequestParams<'a> {
17 pub code: Cow<'a, str>,
18 pub redirect_uri: Option<Cow<'a, str>>,
19 pub client_id: Cow<'a, str>,
20 #[cfg(feature = "pkce")]
21 pub pkce_code_verifier: Option<Cow<'a, PkceCodeVerifier>>,
22}
23
24impl<'a> AccessTokenRequestParams<'a> {
25 pub fn to_form_url_encoded_serializer(&self) -> Serializer<'a, String> {
28 let mut serializer = Serializer::new(String::new());
29
30 serializer.append_pair("grant_type", "authorization_code");
31 serializer.append_pair("code", &self.code);
32
33 if let Some(uri) = &self.redirect_uri {
34 serializer.append_pair("redirect_uri", uri);
35 }
36
37 serializer.append_pair("client_id", &self.client_id);
38
39 #[cfg(feature = "pkce")]
40 if let Some(verifier) = &self.pkce_code_verifier {
41 let verifier = String::from_utf8_lossy(verifier.expose());
42 serializer.append_pair("code_verifier", &verifier);
43 }
44
45 serializer
46 }
47}
48
49impl ToString for AccessTokenRequestParams<'_> {
50 fn to_string(&self) -> String {
51 self.to_form_url_encoded_serializer().finish()
52 }
53}
54
55#[derive(Debug, Error)]
57pub enum RequestOauth2AccessTokenError {
58 #[error(transparent)]
59 SendHttpRequest(#[from] SendHttpError),
60 #[error(transparent)]
61 ParseHttpResponse(#[from] serde_json::Error),
62}
63
64#[derive(Debug)]
66pub enum RequestOauth2AccessTokenResult {
67 Ok(AccessTokenResponse),
69 Io(StreamIo),
71 Err(RequestOauth2AccessTokenError),
73}
74
75#[derive(Debug)]
84pub struct RequestOauth2AccessToken(SendHttp);
85
86impl RequestOauth2AccessToken {
87 pub fn new(
88 request: request::Builder,
89 body: AccessTokenRequestParams<'_>,
90 ) -> http::Result<Self> {
91 let request = request
92 .header(CONTENT_TYPE, "application/x-www-form-urlencoded")
93 .body(body.to_string().into_bytes())?;
94
95 Ok(Self(SendHttp::new(request)))
96 }
97
98 pub fn resume(&mut self, input: Option<StreamIo>) -> RequestOauth2AccessTokenResult {
99 let response = match self.0.resume(input) {
100 SendHttpResult::Ok(result) => result.response,
101 SendHttpResult::Io(io) => return RequestOauth2AccessTokenResult::Io(io),
102 SendHttpResult::Err(err) => return RequestOauth2AccessTokenResult::Err(err.into()),
103 };
104
105 let body = response.body().as_slice();
106
107 if !response.status().is_success() {
108 return match IssueAccessTokenErrorParams::try_from(body) {
109 Ok(res) => RequestOauth2AccessTokenResult::Ok(Err(res)),
110 Err(err) => RequestOauth2AccessTokenResult::Err(err.into()),
111 };
112 }
113
114 match IssueAccessTokenSuccessParams::try_from(body) {
115 Ok(res) => RequestOauth2AccessTokenResult::Ok(Ok(res)),
116 Err(err) => RequestOauth2AccessTokenResult::Err(err.into()),
117 }
118 }
119}