youtube_api/
auth.rs

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