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;
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 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#[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}