oauth/v2_0/
authorization_code_grant.rs1#[cfg(feature = "async-std")]
5use async_std::{
6 io::{BufReadExt, BufReader, WriteExt},
7 net::TcpListener,
8};
9use oauth2::{
10 url::Url, AuthorizationCode, CsrfToken, PkceCodeChallenge, PkceCodeVerifier, RequestTokenError,
11 Scope, TokenResponse,
12};
13#[cfg(feature = "tokio")]
14use tokio::{
15 io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
16 net::TcpListener,
17};
18
19use super::{Client, Error, Result};
20
21#[derive(Debug, Default)]
34pub struct AuthorizationCodeGrant {
35 pub scopes: Vec<Scope>,
36 pub pkce: Option<(PkceCodeChallenge, PkceCodeVerifier)>,
37}
38
39impl AuthorizationCodeGrant {
40 pub fn new() -> Self {
41 Self::default()
42 }
43
44 pub fn with_scope<T>(mut self, scope: T) -> Self
45 where
46 T: ToString,
47 {
48 self.scopes.push(Scope::new(scope.to_string()));
49 self
50 }
51
52 pub fn with_pkce(mut self) -> Self {
53 self.pkce = Some(PkceCodeChallenge::new_random_sha256());
54 self
55 }
56
57 pub fn get_redirect_url(&self, client: &Client) -> (Url, CsrfToken) {
60 let mut redirect = client
61 .authorize_url(CsrfToken::new_random)
62 .add_scopes(self.scopes.clone());
63
64 if let Some((pkce_challenge, _)) = &self.pkce {
65 redirect = redirect.set_pkce_challenge(pkce_challenge.clone());
66 }
67
68 redirect.url()
69 }
70
71 pub async fn wait_for_redirection(
76 self,
77 client: &Client,
78 csrf_state: CsrfToken,
79 ) -> Result<(String, Option<String>)> {
80 let (mut stream, _) =
82 TcpListener::bind((client.redirect_host.as_str(), client.redirect_port))
83 .await
84 .map_err(|err| {
85 Error::BindRedirectServerError(
86 client.redirect_host.clone(),
87 client.redirect_port,
88 err,
89 )
90 })?
91 .accept()
92 .await
93 .map_err(Error::AcceptRedirectServerError)?;
94
95 let code = {
97 let mut reader = BufReader::new(&mut stream);
98
99 let mut request_line = String::new();
100 reader.read_line(&mut request_line).await?;
101
102 let redirect_url = request_line
103 .split_whitespace()
104 .nth(1)
105 .ok_or_else(|| Error::MissingRedirectUrlError(request_line.clone()))?;
106 let redirect_url = format!("http://localhost{redirect_url}");
107 let redirect_url = Url::parse(&redirect_url)
108 .map_err(|err| Error::ParseRedirectUrlError(err, redirect_url.clone()))?;
109
110 let (_, state) = redirect_url
111 .query_pairs()
112 .find(|(key, _)| key == "state")
113 .ok_or_else(|| Error::FindStateInRedirectUrlError(redirect_url.clone()))?;
114 let state = CsrfToken::new(state.into_owned());
115
116 if state.secret() != csrf_state.secret() {
117 return Err(Error::InvalidStateError(
118 state.secret().to_owned(),
119 csrf_state.secret().to_owned(),
120 ));
121 }
122
123 let (_, code) = redirect_url
124 .query_pairs()
125 .find(|(key, _)| key == "code")
126 .ok_or_else(|| Error::FindCodeInRedirectUrlError(redirect_url.clone()))?;
127
128 AuthorizationCode::new(code.into_owned())
129 };
130
131 let res = "Authentication successful!";
133 let res = format!(
134 "HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n{}",
135 res.len(),
136 res
137 );
138 stream.write_all(res.as_bytes()).await?;
139
140 let mut res = client.exchange_code(code);
142
143 if let Some((_, pkce_verifier)) = self.pkce {
144 res = res.set_pkce_verifier(pkce_verifier);
145 }
146
147 let res = res
148 .request_async(&Client::send_oauth2_request)
149 .await
150 .map_err(|err| match err {
151 RequestTokenError::Request(req) => Error::ExchangeCodeError(req.to_string()),
152 RequestTokenError::ServerResponse(res) => Error::ExchangeCodeError(res.to_string()),
153 RequestTokenError::Parse(err, _) => Error::ExchangeCodeError(err.to_string()),
154 RequestTokenError::Other(err) => Error::ExchangeCodeError(err),
155 })?;
156
157 let access_token = res.access_token().secret().to_owned();
158 let refresh_token = res.refresh_token().map(|t| t.secret().clone());
159
160 Ok((access_token, refresh_token))
161 }
162}