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 67 68 69 70 71 72 73 74
use crate::error::Error;
use crate::types::{ApplicationSecret, TokenInfo};
use http::{Uri};
use hyper::client::connect::Connection;
use hyper::header;
use std::error::Error as StdError;
use tokio::io::{AsyncRead, AsyncWrite};
use tower_service::Service;
use url::form_urlencoded;
/// Implements the [OAuth2 Refresh Token Flow](https://developers.google.com/youtube/v3/guides/authentication#devices).
///
/// Refresh an expired access token, as obtained by any other authentication flow.
/// This flow is useful when your `Token` is expired and allows to obtain a new
/// and valid access token.
pub(crate) struct RefreshFlow;
impl RefreshFlow {
/// Attempt to refresh the given token, and obtain a new, valid one.
/// If the `RefreshResult` is `RefreshResult::Error`, you may retry within an interval
/// of your choice. If it is `RefreshResult:RefreshError`, your refresh token is invalid
/// or your authorization was revoked. Therefore no further attempt shall be made,
/// and you will have to re-authorize using the `DeviceFlow`
///
/// # Arguments
/// * `authentication_url` - URL matching the one used in the flow that obtained
/// your refresh_token in the first place.
/// * `client_id` & `client_secret` - as obtained when [registering your application](https://developers.google.com/youtube/registering_an_application)
/// * `refresh_token` - obtained during previous call to `DeviceFlow::poll_token()` or equivalent
///
/// # Examples
/// Please see the crate landing page for an example.
pub(crate) async fn refresh_token<S>(
client: &hyper::Client<S>,
client_secret: &ApplicationSecret,
refresh_token: &str,
) -> Result<TokenInfo, Error>
where
S: Service<Uri> + Clone + Send + Sync + 'static,
S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
S::Future: Send + Unpin + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
log::debug!(
"refreshing access token with refresh token: {}",
refresh_token
);
let req = form_urlencoded::Serializer::new(String::new())
.extend_pairs(&[
("client_id", client_secret.client_id.as_str()),
("client_secret", client_secret.client_secret.as_str()),
("refresh_token", refresh_token),
("grant_type", "refresh_token"),
])
.finish();
let request = hyper::Request::post(&client_secret.token_uri)
.header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
.body(hyper::Body::from(req))
.unwrap();
log::debug!("Sending request: {:?}", request);
let (head, body) = client.request(request).await?.into_parts();
let body = hyper::body::to_bytes(body).await?;
log::debug!("Received response; head: {:?}, body: {:?}", head, body);
let mut token = TokenInfo::from_json(&body)?;
// If the refresh result contains a refresh_token use it, otherwise
// continue using our previous refresh_token.
token
.refresh_token
.get_or_insert_with(|| refresh_token.to_owned());
Ok(token)
}
}