1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use std::io;
use failure::Error;
use oauth2::basic::{BasicClient, BasicTokenResponse};
use oauth2::reqwest::async_http_client;
use oauth2::{
AsyncCodeTokenRequest, AuthorizationCode, CsrfToken, PkceCodeChallenge, PkceCodeVerifier, Scope,
};
static SCOPE: &str = "https://www.googleapis.com/auth/youtube.readonly";
pub fn stdio_login(url: String) -> String {
println!("Open this URL in your browser:\n{}\n", url);
let mut code = String::new();
io::stdin().read_line(&mut code).unwrap();
code
}
pub(crate) fn get_oauth_url(client: &BasicClient) -> (String, PkceCodeVerifier) {
let (pkce_code_challenge, pkce_code_verifier) = PkceCodeChallenge::new_random_sha256();
let (authorize_url, _) = client
.authorize_url(CsrfToken::new_random)
.add_scope(Scope::new(SCOPE.to_string()))
.set_pkce_challenge(pkce_code_challenge)
.add_extra_param("access_type", "offline")
.url();
(authorize_url.to_string(), pkce_code_verifier)
}
pub(crate) async fn request_token(
client: &BasicClient,
code: String,
verifier: PkceCodeVerifier,
) -> Result<BasicTokenResponse, Error> {
let code = AuthorizationCode::new(code);
let token = client
.exchange_code(code)
.set_pkce_verifier(verifier)
.request_async(async_http_client)
.await?;
Ok(token)
}
pub(crate) async fn perform_oauth<H>(
client: &BasicClient,
handler: H,
) -> Result<BasicTokenResponse, Error>
where
H: Fn(String) -> String,
{
let (authorize_url, pkce_code_verifier) = get_oauth_url(client);
let code = handler(authorize_url);
request_token(client, code, pkce_code_verifier).await
}