Skip to content

Commit d675e81

Browse files
committedFeb 21, 2024·
Address clippy lints and clean up examples
1 parent 8bd0ff1 commit d675e81

12 files changed

+344
-414
lines changed
 

‎examples/github.rs

+67-82
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,17 @@
1414
//!
1515
1616
use oauth2::basic::BasicClient;
17-
1817
// Alternatively, this can be `oauth2::curl::http_client` or a custom client.
1918
use oauth2::reqwest::http_client;
2019
use oauth2::{
2120
AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, RedirectUrl, Scope,
2221
TokenResponse, TokenUrl,
2322
};
23+
use url::Url;
24+
2425
use std::env;
2526
use std::io::{BufRead, BufReader, Write};
2627
use std::net::TcpListener;
27-
use url::Url;
2828

2929
fn main() {
3030
let github_client_id = ClientId::new(
@@ -60,88 +60,73 @@ fn main() {
6060
.add_scope(Scope::new("user:email".to_string()))
6161
.url();
6262

63+
println!("Open this URL in your browser:\n{authorize_url}\n");
64+
65+
let (code, state) = {
66+
// A very naive implementation of the redirect server.
67+
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
68+
69+
// The server will terminate itself after collecting the first code.
70+
let Some(mut stream) = listener.incoming().flatten().next() else {
71+
panic!("listener terminated without accepting a connection");
72+
};
73+
74+
let mut reader = BufReader::new(&stream);
75+
76+
let mut request_line = String::new();
77+
reader.read_line(&mut request_line).unwrap();
78+
79+
let redirect_url = request_line.split_whitespace().nth(1).unwrap();
80+
let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();
81+
82+
let code = url
83+
.query_pairs()
84+
.find(|(key, _)| key == "code")
85+
.map(|(_, code)| AuthorizationCode::new(code.into_owned()))
86+
.unwrap();
87+
88+
let state = url
89+
.query_pairs()
90+
.find(|(key, _)| key == "state")
91+
.map(|(_, state)| CsrfToken::new(state.into_owned()))
92+
.unwrap();
93+
94+
let message = "Go back to your terminal :)";
95+
let response = format!(
96+
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
97+
message.len(),
98+
message
99+
);
100+
stream.write_all(response.as_bytes()).unwrap();
101+
102+
(code, state)
103+
};
104+
105+
println!("Github returned the following code:\n{}\n", code.secret());
63106
println!(
64-
"Open this URL in your browser:\n{}\n",
65-
authorize_url.to_string()
107+
"Github returned the following state:\n{} (expected `{}`)\n",
108+
state.secret(),
109+
csrf_state.secret()
66110
);
67111

68-
// A very naive implementation of the redirect server.
69-
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
70-
for stream in listener.incoming() {
71-
if let Ok(mut stream) = stream {
72-
let code;
73-
let state;
74-
{
75-
let mut reader = BufReader::new(&stream);
76-
77-
let mut request_line = String::new();
78-
reader.read_line(&mut request_line).unwrap();
79-
80-
let redirect_url = request_line.split_whitespace().nth(1).unwrap();
81-
let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();
82-
83-
let code_pair = url
84-
.query_pairs()
85-
.find(|pair| {
86-
let &(ref key, _) = pair;
87-
key == "code"
88-
})
89-
.unwrap();
90-
91-
let (_, value) = code_pair;
92-
code = AuthorizationCode::new(value.into_owned());
93-
94-
let state_pair = url
95-
.query_pairs()
96-
.find(|pair| {
97-
let &(ref key, _) = pair;
98-
key == "state"
99-
})
100-
.unwrap();
101-
102-
let (_, value) = state_pair;
103-
state = CsrfToken::new(value.into_owned());
104-
}
105-
106-
let message = "Go back to your terminal :)";
107-
let response = format!(
108-
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
109-
message.len(),
110-
message
111-
);
112-
stream.write_all(response.as_bytes()).unwrap();
113-
114-
println!("Github returned the following code:\n{}\n", code.secret());
115-
println!(
116-
"Github returned the following state:\n{} (expected `{}`)\n",
117-
state.secret(),
118-
csrf_state.secret()
119-
);
120-
121-
// Exchange the code with a token.
122-
let token_res = client.exchange_code(code).request(http_client);
123-
124-
println!("Github returned the following token:\n{:?}\n", token_res);
125-
126-
if let Ok(token) = token_res {
127-
// NB: Github returns a single comma-separated "scope" parameter instead of multiple
128-
// space-separated scopes. Github-specific clients can parse this scope into
129-
// multiple scopes by splitting at the commas. Note that it's not safe for the
130-
// library to do this by default because RFC 6749 allows scopes to contain commas.
131-
let scopes = if let Some(scopes_vec) = token.scopes() {
132-
scopes_vec
133-
.iter()
134-
.map(|comma_separated| comma_separated.split(','))
135-
.flatten()
136-
.collect::<Vec<_>>()
137-
} else {
138-
Vec::new()
139-
};
140-
println!("Github returned the following scopes:\n{:?}\n", scopes);
141-
}
142-
143-
// The server will terminate itself after collecting the first code.
144-
break;
145-
}
112+
// Exchange the code with a token.
113+
let token_res = client.exchange_code(code).request(http_client);
114+
115+
println!("Github returned the following token:\n{:?}\n", token_res);
116+
117+
if let Ok(token) = token_res {
118+
// NB: Github returns a single comma-separated "scope" parameter instead of multiple
119+
// space-separated scopes. Github-specific clients can parse this scope into
120+
// multiple scopes by splitting at the commas. Note that it's not safe for the
121+
// library to do this by default because RFC 6749 allows scopes to contain commas.
122+
let scopes = if let Some(scopes_vec) = token.scopes() {
123+
scopes_vec
124+
.iter()
125+
.flat_map(|comma_separated| comma_separated.split(','))
126+
.collect::<Vec<_>>()
127+
} else {
128+
Vec::new()
129+
};
130+
println!("Github returned the following scopes:\n{:?}\n", scopes);
146131
}
147132
}

‎examples/github_async.rs

+55-70
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@
1414
//!
1515
1616
use oauth2::basic::BasicClient;
17-
1817
// Alternatively, this can be `oauth2::curl::http_client` or a custom client.
1918
use oauth2::reqwest::async_http_client;
2019
use oauth2::{
2120
AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, RedirectUrl, Scope,
2221
TokenResponse, TokenUrl,
2322
};
24-
use std::env;
2523
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
2624
use tokio::net::TcpListener;
2725
use url::Url;
2826

27+
use std::env;
28+
2929
#[tokio::main]
3030
async fn main() {
3131
let github_client_id = ClientId::new(
@@ -61,18 +61,14 @@ async fn main() {
6161
.add_scope(Scope::new("user:email".to_string()))
6262
.url();
6363

64-
println!(
65-
"Open this URL in your browser:\n{}\n",
66-
authorize_url.to_string()
67-
);
64+
println!("Open this URL in your browser:\n{authorize_url}\n");
65+
66+
let (code, state) = {
67+
// A very naive implementation of the redirect server.
68+
let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
6869

69-
// A very naive implementation of the redirect server.
70-
let listener = TcpListener::bind("127.0.0.1:8080").await.unwrap();
71-
loop {
72-
if let Ok((mut stream, _)) = listener.accept().await {
73-
let code;
74-
let state;
75-
{
70+
loop {
71+
if let Ok((mut stream, _)) = listener.accept().await {
7672
let mut reader = BufReader::new(&mut stream);
7773

7874
let mut request_line = String::new();
@@ -81,71 +77,60 @@ async fn main() {
8177
let redirect_url = request_line.split_whitespace().nth(1).unwrap();
8278
let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();
8379

84-
let code_pair = url
80+
let code = url
8581
.query_pairs()
86-
.find(|pair| {
87-
let &(ref key, _) = pair;
88-
key == "code"
89-
})
82+
.find(|(key, _)| key == "code")
83+
.map(|(_, code)| AuthorizationCode::new(code.into_owned()))
9084
.unwrap();
9185

92-
let (_, value) = code_pair;
93-
code = AuthorizationCode::new(value.into_owned());
94-
95-
let state_pair = url
86+
let state = url
9687
.query_pairs()
97-
.find(|pair| {
98-
let &(ref key, _) = pair;
99-
key == "state"
100-
})
88+
.find(|(key, _)| key == "state")
89+
.map(|(_, state)| CsrfToken::new(state.into_owned()))
10190
.unwrap();
10291

103-
let (_, value) = state_pair;
104-
state = CsrfToken::new(value.into_owned());
105-
}
92+
let message = "Go back to your terminal :)";
93+
let response = format!(
94+
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
95+
message.len(),
96+
message
97+
);
98+
stream.write_all(response.as_bytes()).await.unwrap();
10699

107-
let message = "Go back to your terminal :)";
108-
let response = format!(
109-
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
110-
message.len(),
111-
message
112-
);
113-
stream.write_all(response.as_bytes()).await.unwrap();
114-
115-
println!("Github returned the following code:\n{}\n", code.secret());
116-
println!(
117-
"Github returned the following state:\n{} (expected `{}`)\n",
118-
state.secret(),
119-
csrf_state.secret()
120-
);
121-
122-
// Exchange the code with a token.
123-
let token_res = client
124-
.exchange_code(code)
125-
.request_async(async_http_client)
126-
.await;
127-
128-
println!("Github returned the following token:\n{:?}\n", token_res);
129-
130-
if let Ok(token) = token_res {
131-
// NB: Github returns a single comma-separated "scope" parameter instead of multiple
132-
// space-separated scopes. Github-specific clients can parse this scope into
133-
// multiple scopes by splitting at the commas. Note that it's not safe for the
134-
// library to do this by default because RFC 6749 allows scopes to contain commas.
135-
let scopes = if let Some(scopes_vec) = token.scopes() {
136-
scopes_vec
137-
.iter()
138-
.map(|comma_separated| comma_separated.split(','))
139-
.flatten()
140-
.collect::<Vec<_>>()
141-
} else {
142-
Vec::new()
143-
};
144-
println!("Github returned the following scopes:\n{:?}\n", scopes);
100+
// The server will terminate itself after collecting the first code.
101+
break (code, state);
145102
}
146-
147-
// The server will terminate itself after collecting the first code.
148-
break;
149103
}
104+
};
105+
106+
println!("Github returned the following code:\n{}\n", code.secret());
107+
println!(
108+
"Github returned the following state:\n{} (expected `{}`)\n",
109+
state.secret(),
110+
csrf_state.secret()
111+
);
112+
113+
// Exchange the code with a token.
114+
let token_res = client
115+
.exchange_code(code)
116+
.request_async(async_http_client)
117+
.await;
118+
119+
println!("Github returned the following token:\n{:?}\n", token_res);
120+
121+
if let Ok(token) = token_res {
122+
// NB: Github returns a single comma-separated "scope" parameter instead of multiple
123+
// space-separated scopes. Github-specific clients can parse this scope into
124+
// multiple scopes by splitting at the commas. Note that it's not safe for the
125+
// library to do this by default because RFC 6749 allows scopes to contain commas.
126+
let scopes = if let Some(scopes_vec) = token.scopes() {
127+
scopes_vec
128+
.iter()
129+
.flat_map(|comma_separated| comma_separated.split(','))
130+
.collect::<Vec<_>>()
131+
} else {
132+
Vec::new()
133+
};
134+
println!("Github returned the following scopes:\n{:?}\n", scopes);
150135
}
151136
}

‎examples/google.rs

+71-84
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ use oauth2::{
2020
AuthUrl, AuthorizationCode, ClientId, ClientSecret, CsrfToken, PkceCodeChallenge, RedirectUrl,
2121
RevocationUrl, Scope, TokenUrl,
2222
};
23+
use url::Url;
24+
2325
use std::env;
2426
use std::io::{BufRead, BufReader, Write};
2527
use std::net::TcpListener;
26-
use url::Url;
2728

2829
fn main() {
2930
let google_client_id = ClientId::new(
@@ -73,90 +74,76 @@ fn main() {
7374
.set_pkce_challenge(pkce_code_challenge)
7475
.url();
7576

77+
println!("Open this URL in your browser:\n{authorize_url}\n");
78+
79+
let (code, state) = {
80+
// A very naive implementation of the redirect server.
81+
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
82+
83+
// The server will terminate itself after collecting the first code.
84+
let Some(mut stream) = listener.incoming().flatten().next() else {
85+
panic!("listener terminated without accepting a connection");
86+
};
87+
88+
let mut reader = BufReader::new(&stream);
89+
90+
let mut request_line = String::new();
91+
reader.read_line(&mut request_line).unwrap();
92+
93+
let redirect_url = request_line.split_whitespace().nth(1).unwrap();
94+
let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();
95+
96+
let code = url
97+
.query_pairs()
98+
.find(|(key, _)| key == "code")
99+
.map(|(_, code)| AuthorizationCode::new(code.into_owned()))
100+
.unwrap();
101+
102+
let state = url
103+
.query_pairs()
104+
.find(|(key, _)| key == "state")
105+
.map(|(_, state)| CsrfToken::new(state.into_owned()))
106+
.unwrap();
107+
108+
let message = "Go back to your terminal :)";
109+
let response = format!(
110+
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
111+
message.len(),
112+
message
113+
);
114+
stream.write_all(response.as_bytes()).unwrap();
115+
116+
(code, state)
117+
};
118+
119+
println!("Google returned the following code:\n{}\n", code.secret());
120+
println!(
121+
"Google returned the following state:\n{} (expected `{}`)\n",
122+
state.secret(),
123+
csrf_state.secret()
124+
);
125+
126+
// Exchange the code with a token.
127+
let token_response = client
128+
.exchange_code(code)
129+
.set_pkce_verifier(pkce_code_verifier)
130+
.request(http_client);
131+
76132
println!(
77-
"Open this URL in your browser:\n{}\n",
78-
authorize_url.to_string()
133+
"Google returned the following token:\n{:?}\n",
134+
token_response
79135
);
80136

81-
// A very naive implementation of the redirect server.
82-
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
83-
for stream in listener.incoming() {
84-
if let Ok(mut stream) = stream {
85-
let code;
86-
let state;
87-
{
88-
let mut reader = BufReader::new(&stream);
89-
90-
let mut request_line = String::new();
91-
reader.read_line(&mut request_line).unwrap();
92-
93-
let redirect_url = request_line.split_whitespace().nth(1).unwrap();
94-
let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();
95-
96-
let code_pair = url
97-
.query_pairs()
98-
.find(|pair| {
99-
let &(ref key, _) = pair;
100-
key == "code"
101-
})
102-
.unwrap();
103-
104-
let (_, value) = code_pair;
105-
code = AuthorizationCode::new(value.into_owned());
106-
107-
let state_pair = url
108-
.query_pairs()
109-
.find(|pair| {
110-
let &(ref key, _) = pair;
111-
key == "state"
112-
})
113-
.unwrap();
114-
115-
let (_, value) = state_pair;
116-
state = CsrfToken::new(value.into_owned());
117-
}
118-
119-
let message = "Go back to your terminal :)";
120-
let response = format!(
121-
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
122-
message.len(),
123-
message
124-
);
125-
stream.write_all(response.as_bytes()).unwrap();
126-
127-
println!("Google returned the following code:\n{}\n", code.secret());
128-
println!(
129-
"Google returned the following state:\n{} (expected `{}`)\n",
130-
state.secret(),
131-
csrf_state.secret()
132-
);
133-
134-
// Exchange the code with a token.
135-
let token_response = client
136-
.exchange_code(code)
137-
.set_pkce_verifier(pkce_code_verifier)
138-
.request(http_client);
139-
140-
println!(
141-
"Google returned the following token:\n{:?}\n",
142-
token_response
143-
);
144-
145-
// Revoke the obtained token
146-
let token_response = token_response.unwrap();
147-
let token_to_revoke: StandardRevocableToken = match token_response.refresh_token() {
148-
Some(token) => token.into(),
149-
None => token_response.access_token().into(),
150-
};
151-
152-
client
153-
.revoke_token(token_to_revoke)
154-
.unwrap()
155-
.request(http_client)
156-
.expect("Failed to revoke token");
157-
158-
// The server will terminate itself after revoking the token.
159-
break;
160-
}
161-
}
137+
// Revoke the obtained token
138+
let token_response = token_response.unwrap();
139+
let token_to_revoke: StandardRevocableToken = match token_response.refresh_token() {
140+
Some(token) => token.into(),
141+
None => token_response.access_token().into(),
142+
};
143+
144+
client
145+
.revoke_token(token_to_revoke)
146+
.unwrap()
147+
.request(http_client)
148+
.expect("Failed to revoke token");
162149
}

‎examples/google_devicecode.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use oauth2::devicecode::{DeviceAuthorizationResponse, ExtraDeviceAuthorizationFi
1919
use oauth2::reqwest::http_client;
2020
use oauth2::{AuthType, AuthUrl, ClientId, ClientSecret, DeviceAuthorizationUrl, Scope, TokenUrl};
2121
use serde::{Deserialize, Serialize};
22+
2223
use std::collections::HashMap;
2324
use std::env;
2425

@@ -68,8 +69,8 @@ fn main() {
6869
// Display the URL and user-code.
6970
println!(
7071
"Open this URL in your browser:\n{}\nand enter the code: {}",
71-
details.verification_uri().to_string(),
72-
details.user_code().secret().to_string()
72+
details.verification_uri(),
73+
details.user_code().secret(),
7374
);
7475

7576
// Now poll for the token

‎examples/letterboxd.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ impl SigningHttpClient {
117117
.append_pair("timestamp", &format!("{}", timestamp));
118118

119119
// create signature
120-
let mut hmac = Hmac::<Sha256>::new_from_slice(&self.client_secret.secret().as_bytes())
120+
let mut hmac = Hmac::<Sha256>::new_from_slice(self.client_secret.secret().as_bytes())
121121
.expect("HMAC can take key of any size");
122122
hmac.update(method.as_str().as_bytes());
123123
hmac.update(&[b'\0']);

‎examples/microsoft_devicecode_common_user.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use oauth2::basic::BasicClient;
22
use oauth2::devicecode::StandardDeviceAuthorizationResponse;
33
use oauth2::reqwest::async_http_client;
44
use oauth2::{AuthUrl, ClientId, DeviceAuthorizationUrl, Scope, TokenUrl};
5+
56
use std::error::Error;
67

78
#[tokio::main]
@@ -27,8 +28,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
2728

2829
eprintln!(
2930
"Open this URL in your browser:\n{}\nand enter the code: {}",
30-
details.verification_uri().to_string(),
31-
details.user_code().secret().to_string()
31+
details.verification_uri(),
32+
details.user_code().secret(),
3233
);
3334

3435
let token_result = client

‎examples/microsoft_devicecode_tenant_user.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use oauth2::basic::BasicClient;
22
use oauth2::devicecode::StandardDeviceAuthorizationResponse;
33
use oauth2::reqwest::async_http_client;
44
use oauth2::{AuthUrl, ClientId, DeviceAuthorizationUrl, Scope, TokenUrl};
5+
56
use std::error::Error;
67

78
// Reference: https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code
@@ -36,8 +37,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
3637

3738
eprintln!(
3839
"Open this URL in your browser:\n{}\nand enter the code: {}",
39-
details.verification_uri().to_string(),
40-
details.user_code().secret().to_string()
40+
details.verification_uri(),
41+
details.user_code().secret(),
4142
);
4243

4344
let token_result = client

‎examples/msgraph.rs

+55-68
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use oauth2::{
3030
use std::env;
3131
use std::io::{BufRead, BufReader, Write};
3232
use std::net::TcpListener;
33+
3334
use url::Url;
3435

3536
fn main() {
@@ -78,75 +79,61 @@ fn main() {
7879
.set_pkce_challenge(pkce_code_challenge)
7980
.url();
8081

82+
println!("Open this URL in your browser:\n{authorize_url}\n");
83+
84+
let (code, state) = {
85+
// A very naive implementation of the redirect server.
86+
let listener = TcpListener::bind("127.0.0.1:3003").unwrap();
87+
88+
// The server will terminate itself after collecting the first code.
89+
let Some(mut stream) = listener.incoming().flatten().next() else {
90+
panic!("listener terminated without accepting a connection");
91+
};
92+
93+
let mut reader = BufReader::new(&stream);
94+
95+
let mut request_line = String::new();
96+
reader.read_line(&mut request_line).unwrap();
97+
98+
let redirect_url = request_line.split_whitespace().nth(1).unwrap();
99+
let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();
100+
101+
let code = url
102+
.query_pairs()
103+
.find(|(key, _)| key == "code")
104+
.map(|(_, code)| AuthorizationCode::new(code.into_owned()))
105+
.unwrap();
106+
107+
let state = url
108+
.query_pairs()
109+
.find(|(key, _)| key == "state")
110+
.map(|(_, state)| CsrfToken::new(state.into_owned()))
111+
.unwrap();
112+
113+
let message = "Go back to your terminal :)";
114+
let response = format!(
115+
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
116+
message.len(),
117+
message
118+
);
119+
stream.write_all(response.as_bytes()).unwrap();
120+
121+
(code, state)
122+
};
123+
124+
println!("MS Graph returned the following code:\n{}\n", code.secret());
81125
println!(
82-
"Open this URL in your browser:\n{}\n",
83-
authorize_url.to_string()
126+
"MS Graph returned the following state:\n{} (expected `{}`)\n",
127+
state.secret(),
128+
csrf_state.secret()
84129
);
85130

86-
// A very naive implementation of the redirect server.
87-
let listener = TcpListener::bind("127.0.0.1:3003").unwrap();
88-
for stream in listener.incoming() {
89-
if let Ok(mut stream) = stream {
90-
let code;
91-
let state;
92-
{
93-
let mut reader = BufReader::new(&stream);
94-
95-
let mut request_line = String::new();
96-
reader.read_line(&mut request_line).unwrap();
97-
98-
let redirect_url = request_line.split_whitespace().nth(1).unwrap();
99-
let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();
100-
101-
let code_pair = url
102-
.query_pairs()
103-
.find(|pair| {
104-
let &(ref key, _) = pair;
105-
key == "code"
106-
})
107-
.unwrap();
108-
109-
let (_, value) = code_pair;
110-
code = AuthorizationCode::new(value.into_owned());
111-
112-
let state_pair = url
113-
.query_pairs()
114-
.find(|pair| {
115-
let &(ref key, _) = pair;
116-
key == "state"
117-
})
118-
.unwrap();
119-
120-
let (_, value) = state_pair;
121-
state = CsrfToken::new(value.into_owned());
122-
}
123-
124-
let message = "Go back to your terminal :)";
125-
let response = format!(
126-
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
127-
message.len(),
128-
message
129-
);
130-
stream.write_all(response.as_bytes()).unwrap();
131-
132-
println!("MS Graph returned the following code:\n{}\n", code.secret());
133-
println!(
134-
"MS Graph returned the following state:\n{} (expected `{}`)\n",
135-
state.secret(),
136-
csrf_state.secret()
137-
);
138-
139-
// Exchange the code with a token.
140-
let token = client
141-
.exchange_code(code)
142-
// Send the PKCE code verifier in the token request
143-
.set_pkce_verifier(pkce_code_verifier)
144-
.request(http_client);
145-
146-
println!("MS Graph returned the following token:\n{:?}\n", token);
147-
148-
// The server will terminate itself after collecting the first code.
149-
break;
150-
}
151-
}
131+
// Exchange the code with a token.
132+
let token = client
133+
.exchange_code(code)
134+
// Send the PKCE code verifier in the token request
135+
.set_pkce_verifier(pkce_code_verifier)
136+
.request(http_client);
137+
138+
println!("MS Graph returned the following token:\n{:?}\n", token);
152139
}

‎examples/wunderlist.rs

+62-76
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,13 @@ use oauth2::{
3030
EmptyExtraTokenFields, ExtraTokenFields, RedirectUrl, RefreshToken, Scope, TokenResponse,
3131
TokenUrl,
3232
};
33-
3433
use serde::{Deserialize, Serialize};
35-
use std::time::Duration;
34+
use url::Url;
3635

3736
use std::env;
3837
use std::io::{BufRead, BufReader, Write};
3938
use std::net::TcpListener;
40-
use url::Url;
39+
use std::time::Duration;
4140

4241
type SpecialTokenResponse = NonStandardTokenResponse<EmptyExtraTokenFields>;
4342
type SpecialClient = Client<
@@ -167,80 +166,67 @@ fn main() {
167166
// Generate the authorization URL to which we'll redirect the user.
168167
let (authorize_url, csrf_state) = client.authorize_url(CsrfToken::new_random).url();
169168

169+
println!("Open this URL in your browser:\n{authorize_url}\n");
170+
171+
let (code, state) = {
172+
// A very naive implementation of the redirect server.
173+
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
174+
175+
// The server will terminate itself after collecting the first code.
176+
let Some(mut stream) = listener.incoming().flatten().next() else {
177+
panic!("listener terminated without accepting a connection");
178+
};
179+
180+
let mut reader = BufReader::new(&stream);
181+
182+
let mut request_line = String::new();
183+
reader.read_line(&mut request_line).unwrap();
184+
185+
let redirect_url = request_line.split_whitespace().nth(1).unwrap();
186+
let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();
187+
188+
let code = url
189+
.query_pairs()
190+
.find(|(key, _)| key == "code")
191+
.map(|(_, code)| AuthorizationCode::new(code.into_owned()))
192+
.unwrap();
193+
194+
let state = url
195+
.query_pairs()
196+
.find(|(key, _)| key == "state")
197+
.map(|(_, state)| CsrfToken::new(state.into_owned()))
198+
.unwrap();
199+
200+
let message = "Go back to your terminal :)";
201+
let response = format!(
202+
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
203+
message.len(),
204+
message
205+
);
206+
stream.write_all(response.as_bytes()).unwrap();
207+
208+
(code, state)
209+
};
210+
211+
println!(
212+
"Wunderlist returned the following code:\n{}\n",
213+
code.secret()
214+
);
170215
println!(
171-
"Open this URL in your browser:\n{}\n",
172-
authorize_url.to_string()
216+
"Wunderlist returned the following state:\n{} (expected `{}`)\n",
217+
state.secret(),
218+
csrf_state.secret()
173219
);
174220

175-
// A very naive implementation of the redirect server.
176-
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
177-
for stream in listener.incoming() {
178-
if let Ok(mut stream) = stream {
179-
let code;
180-
let state;
181-
{
182-
let mut reader = BufReader::new(&stream);
183-
184-
let mut request_line = String::new();
185-
reader.read_line(&mut request_line).unwrap();
186-
187-
let redirect_url = request_line.split_whitespace().nth(1).unwrap();
188-
let url = Url::parse(&("http://localhost".to_string() + redirect_url)).unwrap();
189-
190-
let code_pair = url
191-
.query_pairs()
192-
.find(|pair| {
193-
let &(ref key, _) = pair;
194-
key == "code"
195-
})
196-
.unwrap();
197-
198-
let (_, value) = code_pair;
199-
code = AuthorizationCode::new(value.into_owned());
200-
201-
let state_pair = url
202-
.query_pairs()
203-
.find(|pair| {
204-
let &(ref key, _) = pair;
205-
key == "state"
206-
})
207-
.unwrap();
208-
209-
let (_, value) = state_pair;
210-
state = CsrfToken::new(value.into_owned());
211-
}
212-
213-
let message = "Go back to your terminal :)";
214-
let response = format!(
215-
"HTTP/1.1 200 OK\r\ncontent-length: {}\r\n\r\n{}",
216-
message.len(),
217-
message
218-
);
219-
stream.write_all(response.as_bytes()).unwrap();
220-
221-
println!(
222-
"Wunderlist returned the following code:\n{}\n",
223-
code.secret()
224-
);
225-
println!(
226-
"Wunderlist returned the following state:\n{} (expected `{}`)\n",
227-
state.secret(),
228-
csrf_state.secret()
229-
);
230-
231-
// Exchange the code with a token.
232-
let token_res = client
233-
.exchange_code(code)
234-
.add_extra_param("client_id", client_id_str)
235-
.add_extra_param("client_secret", client_secret_str)
236-
.request(http_client);
237-
238-
println!(
239-
"Wunderlist returned the following token:\n{:?}\n",
240-
token_res
241-
);
242-
243-
break;
244-
}
245-
}
221+
// Exchange the code with a token.
222+
let token_res = client
223+
.exchange_code(code)
224+
.add_extra_param("client_id", client_id_str)
225+
.add_extra_param("client_secret", client_secret_str)
226+
.request(http_client);
227+
228+
println!(
229+
"Wunderlist returned the following token:\n{:?}\n",
230+
token_res
231+
);
246232
}

‎src/lib.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -1116,7 +1116,7 @@ impl<'a> AuthorizationRequest<'a> {
11161116
/// Enables custom flows other than the `code` and `token` (implicit flow) grant.
11171117
///
11181118
pub fn set_response_type(mut self, response_type: &ResponseType) -> Self {
1119-
self.response_type = (&**response_type).to_owned().into();
1119+
self.response_type = (**response_type).to_owned().into();
11201120
self
11211121
}
11221122

@@ -1156,13 +1156,13 @@ impl<'a> AuthorizationRequest<'a> {
11561156
let url = {
11571157
let mut pairs: Vec<(&str, &str)> = vec![
11581158
("response_type", self.response_type.as_ref()),
1159-
("client_id", &self.client_id),
1159+
("client_id", self.client_id),
11601160
("state", self.state.secret()),
11611161
];
11621162

11631163
if let Some(ref pkce_challenge) = self.pkce_challenge {
1164-
pairs.push(("code_challenge", &pkce_challenge.as_str()));
1165-
pairs.push(("code_challenge_method", &pkce_challenge.method().as_str()));
1164+
pairs.push(("code_challenge", pkce_challenge.as_str()));
1165+
pairs.push(("code_challenge_method", pkce_challenge.method().as_str()));
11661166
}
11671167

11681168
if let Some(ref redirect_url) = self.redirect_url {
@@ -1176,7 +1176,7 @@ impl<'a> AuthorizationRequest<'a> {
11761176
let mut url: Url = self.auth_url.url().to_owned();
11771177

11781178
url.query_pairs_mut()
1179-
.extend_pairs(pairs.iter().map(|&(k, v)| (k, &v[..])));
1179+
.extend_pairs(pairs.iter().map(|&(k, v)| (k, v)));
11801180

11811181
url.query_pairs_mut()
11821182
.extend_pairs(self.extra_params.iter().cloned());
@@ -1976,19 +1976,19 @@ fn endpoint_request<'a>(
19761976
// before using them as HTTP Basic auth username and password. Note that this is
19771977
// not standard for ordinary Basic auth, so curl won't do it for us.
19781978
let urlencoded_id: String =
1979-
form_urlencoded::byte_serialize(&client_id.as_bytes()).collect();
1979+
form_urlencoded::byte_serialize(client_id.as_bytes()).collect();
19801980
let urlencoded_secret: String =
19811981
form_urlencoded::byte_serialize(secret.secret().as_bytes()).collect();
19821982
let b64_credential =
1983-
base64::encode(&format!("{}:{}", &urlencoded_id, urlencoded_secret));
1983+
base64::encode(format!("{}:{}", &urlencoded_id, urlencoded_secret));
19841984
headers.append(
19851985
AUTHORIZATION,
19861986
HeaderValue::from_str(&format!("Basic {}", &b64_credential)).unwrap(),
19871987
);
19881988
}
19891989
(AuthType::RequestBody, _) | (AuthType::BasicAuth, None) => {
19901990
params.push(("client_id", client_id));
1991-
if let Some(ref client_secret) = client_secret {
1991+
if let Some(client_secret) = client_secret {
19921992
params.push(("client_secret", client_secret.secret()));
19931993
}
19941994
}
@@ -2001,7 +2001,7 @@ fn endpoint_request<'a>(
20012001
params.extend_from_slice(
20022002
extra_params
20032003
.iter()
2004-
.map(|&(ref k, ref v)| (k.as_ref(), v.as_ref()))
2004+
.map(|(k, v)| (k.as_ref(), v.as_ref()))
20052005
.collect::<Vec<_>>()
20062006
.as_slice(),
20072007
);

‎src/tests.rs

+11-11
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ fn test_exchange_code_successful_with_complete_json_response() {
394394
token.scopes()
395395
);
396396
assert_eq!(3600, token.expires_in().unwrap().as_secs());
397-
assert_eq!("foobar", token.refresh_token().clone().unwrap().secret());
397+
assert_eq!("foobar", token.refresh_token().unwrap().secret());
398398

399399
// Ensure that serialization produces an equivalent JSON value.
400400
let serialized_json = serde_json::to_string(&token).unwrap();
@@ -960,8 +960,8 @@ fn test_exchange_code_with_simple_json_error() {
960960
assert!(token.is_err());
961961

962962
let token_err = token.err().unwrap();
963-
match &token_err {
964-
&RequestTokenError::ServerResponse(ref error_response) => {
963+
match token_err {
964+
RequestTokenError::ServerResponse(ref error_response) => {
965965
assert_eq!(
966966
BasicErrorResponseType::InvalidRequest,
967967
*error_response.error()
@@ -1217,15 +1217,15 @@ mod colorful_extension {
12171217
StandardErrorResponse<ColorfulErrorResponseType>,
12181218
>;
12191219

1220-
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
1220+
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
12211221
#[serde(rename_all = "lowercase")]
12221222
pub enum ColorfulTokenType {
12231223
Green,
12241224
Red,
12251225
}
12261226
impl TokenType for ColorfulTokenType {}
12271227

1228-
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
1228+
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
12291229
pub struct ColorfulFields {
12301230
#[serde(rename = "shape")]
12311231
#[serde(skip_serializing_if = "Option::is_none")]
@@ -1243,7 +1243,7 @@ mod colorful_extension {
12431243
}
12441244
impl ExtraTokenFields for ColorfulFields {}
12451245

1246-
#[derive(Clone, Deserialize, PartialEq, Serialize)]
1246+
#[derive(Clone, Deserialize, PartialEq, Eq, Serialize)]
12471247
#[serde(rename_all = "snake_case")]
12481248
pub enum ColorfulErrorResponseType {
12491249
TooDark,
@@ -1285,7 +1285,7 @@ mod colorful_extension {
12851285
impl RevocableToken for ColorfulRevocableToken {
12861286
fn secret(&self) -> &str {
12871287
match self {
1288-
ColorfulRevocableToken::Red(secret) => &secret,
1288+
ColorfulRevocableToken::Red(secret) => secret,
12891289
}
12901290
}
12911291

@@ -1402,7 +1402,7 @@ fn test_extension_successful_with_complete_json_response() {
14021402
token.scopes()
14031403
);
14041404
assert_eq!(3600, token.expires_in().unwrap().as_secs());
1405-
assert_eq!("foobar", token.refresh_token().clone().unwrap().secret());
1405+
assert_eq!("foobar", token.refresh_token().unwrap().secret());
14061406
assert_eq!(Some(&"round".to_string()), token.extra_fields().shape());
14071407
assert_eq!(12, token.extra_fields().height());
14081408

@@ -1458,7 +1458,7 @@ fn test_extension_with_simple_json_error() {
14581458

14591459
let token_err = token.err().unwrap();
14601460
match &token_err {
1461-
&RequestTokenError::ServerResponse(ref error_response) => {
1461+
RequestTokenError::ServerResponse(ref error_response) => {
14621462
assert_eq!(ColorfulErrorResponseType::TooLight, *error_response.error());
14631463
assert_eq!(
14641464
Some(&"stuff happened".to_string()),
@@ -1694,7 +1694,7 @@ fn test_token_introspection_successful_with_basic_auth_minimal_response() {
16941694
))
16951695
.unwrap();
16961696

1697-
assert_eq!(true, introspection_response.active);
1697+
assert!(introspection_response.active);
16981698
assert_eq!(None, introspection_response.scopes);
16991699
assert_eq!(None, introspection_response.client_id);
17001700
assert_eq!(None, introspection_response.username);
@@ -1757,7 +1757,7 @@ fn test_token_introspection_successful_with_basic_auth_full_response() {
17571757
))
17581758
.unwrap();
17591759

1760-
assert_eq!(true, introspection_response.active);
1760+
assert!(introspection_response.active);
17611761
assert_eq!(
17621762
Some(vec![
17631763
Scope::new("email".to_string()),

‎src/types.rs

+4-7
Original file line numberDiff line numberDiff line change
@@ -501,12 +501,9 @@ impl PkceCodeChallenge {
501501
// The RFC specifies that the code verifier must have "a minimum length of 43
502502
// characters and a maximum length of 128 characters".
503503
// This implies 32-96 octets of random data to be base64 encoded.
504-
assert!(num_bytes >= 32 && num_bytes <= 96);
504+
assert!((32..=96).contains(&num_bytes));
505505
let random_bytes: Vec<u8> = (0..num_bytes).map(|_| thread_rng().gen::<u8>()).collect();
506-
PkceCodeVerifier::new(base64::encode_config(
507-
&random_bytes,
508-
base64::URL_SAFE_NO_PAD,
509-
))
506+
PkceCodeVerifier::new(base64::encode_config(random_bytes, base64::URL_SAFE_NO_PAD))
510507
}
511508

512509
///
@@ -523,7 +520,7 @@ impl PkceCodeChallenge {
523520
assert!(code_verifier.secret().len() >= 43 && code_verifier.secret().len() <= 128);
524521

525522
let digest = Sha256::digest(code_verifier.secret().as_bytes());
526-
let code_challenge = base64::encode_config(&digest, base64::URL_SAFE_NO_PAD);
523+
let code_challenge = base64::encode_config(digest, base64::URL_SAFE_NO_PAD);
527524

528525
Self {
529526
code_challenge,
@@ -619,7 +616,7 @@ new_secret_type![
619616
///
620617
pub fn new_random_len(num_bytes: u32) -> Self {
621618
let random_bytes: Vec<u8> = (0..num_bytes).map(|_| thread_rng().gen::<u8>()).collect();
622-
CsrfToken::new(base64::encode_config(&random_bytes, base64::URL_SAFE_NO_PAD))
619+
CsrfToken::new(base64::encode_config(random_bytes, base64::URL_SAFE_NO_PAD))
623620
}
624621
}
625622
];

0 commit comments

Comments
 (0)
Please sign in to comment.