1use failure::{ensure, Error, format_err};
2use oauth2::basic::BasicClient;
3use oauth2::{PkceCodeVerifier, TokenUrl, RedirectUrl, ClientId, AuthUrl, ClientSecret};
4use tokio::fs::{read_to_string, write};
5
6use crate::auth::{get_oauth_url, perform_oauth, request_token};
7use crate::YoutubeApi;
8use crate::token::AuthToken;
9use crate::api::YoutubeOAuth;
10use reqwest::Client;
11
12pub static CODE_REDIRECT_URI: &str = "urn:ietf:wg:oauth:2.0:oob";
13
14impl YoutubeApi {
15 pub fn new_with_oauth<S: Into<String>>(api_key: S, client_id: String, client_secret: String, redirect_uri: Option<&str>) -> Result<Self, failure::Error> {
16 let oauth_client = BasicClient::new(
17 ClientId::new(client_id.clone()),
18 Some(ClientSecret::new(client_secret.clone())),
19 AuthUrl::new("https://accounts.google.com/o/oauth2/v2/auth".to_string())?,
20 Some(TokenUrl::new(
21 "https://www.googleapis.com/oauth2/v3/token".to_string(),
22 )?),
23 )
24 .set_redirect_url(RedirectUrl::new(
25 redirect_uri.unwrap_or(CODE_REDIRECT_URI).to_string(),
26 )?);
27
28 let oauth = YoutubeOAuth {
29 client_id,
30 client_secret,
31 client: oauth_client,
32 token: AuthToken::new(),
33 };
34
35 Ok(YoutubeApi {
36 api_key: api_key.into(),
37 oauth: Some(oauth),
38 client: Client::new(),
39 })
40 }
41
42 pub async fn login<H>(&self, handler: H) -> Result<(), Error>
61 where
62 H: Fn(String) -> String,
63 {
64 let oauth = self.oauth.as_ref().ok_or_else(|| format_err!("OAuth client not configured"))?;
65 let token = perform_oauth(&oauth.client, handler).await?;
66 oauth.token.set_token(token).await;
67 Ok(())
68 }
69
70 pub fn get_oauth_url(&self) -> Result<(String, String), Error> {
71 let oauth = self.oauth.as_ref().ok_or_else(|| format_err!("OAuth client not configured"))?;
72 let (url, verifier) = get_oauth_url(&oauth.client);
73
74 Ok((url, verifier.secret().clone()))
75 }
76
77 pub async fn request_token(&mut self, code: String, verifier: String) -> Result<(), Error> {
78 let oauth = self.oauth.as_ref().ok_or_else(|| format_err!("OAuth client not configured"))?;
79 let verifier = PkceCodeVerifier::new(verifier);
80
81 let token = request_token(&oauth.client, code, verifier).await?;
82 oauth.token.set_token(token).await;
83
84 Ok(())
85 }
86
87 pub fn has_token(&self) -> bool {
88 self.oauth.as_ref().map(|oauth| oauth.token.has_token()).unwrap_or_default()
89 }
90
91 pub async fn store_token(&self) -> Result<(), Error> {
95 let oauth = self.oauth.as_ref().ok_or_else(|| format_err!("OAuth client not configured"))?;
96 ensure!(oauth.token.has_token(), "No token available to persist");
97 let token = serde_json::to_string(&oauth.token.get_token().await?)?;
98 write(".youtube-auth.json", token).await?; Ok(())
100 }
101
102 pub async fn load_token(&self) -> Result<(), Error> {
106 let oauth = self.oauth.as_ref().ok_or_else(|| format_err!("OAuth client not configured"))?;
107 let token = read_to_string(".youtube-auth.json").await?;
108 let token = serde_json::from_str(&token)?;
109 oauth.token.set_token(token).await;
110 Ok(())
111 }
112}