We're trying to help.
Since you pasted a large block of code earlier, here is how I have changed it to own the stuff that is deserialized.
use rocket::serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
use tokio::sync::RwLock;
use oxide_auth::endpoint::UniqueValue;
use reqwest::{header, Response};
#[derive(Clone)]
pub struct Client<'r> {
config: Config<'r>,
state: Arc<RwLock<State>>,
}
unsafe impl<'r> Send for Client<'r> {}
unsafe impl<'r> Sync for Client<'r> {}
#[derive(Clone, Copy)]
pub struct Config<'r> {
pub protected_url: &'r str,
pub token_url: &'r str,
pub refresh_url: &'r str,
pub client_id: &'r str,
pub redirect_uri: &'r str,
pub client_secret: Option<&'r str>,
}
unsafe impl<'r> Send for Config<'r> {}
unsafe impl<'r> Sync for Config<'r> {}
pub enum Error {
AccessFailed,
NoToken,
AuthorizationFailed,
RefreshFailed,
Invalid(serde_json::Error),
MissingToken,
Response(String),
}
unsafe impl Send for Error {}
unsafe impl Sync for Error {}
#[derive(Debug, Default, Clone)]
pub struct State {
pub token: Option<String>,
pub refresh: Option<String>,
pub until: Option<i64>,
}
unsafe impl Send for State {}
unsafe impl Sync for State {}
#[derive(Serialize, Deserialize, Clone)]
#[serde(crate = "rocket::serde")]
pub struct TokenMap {
token_type: String,
scope: String,
#[serde(skip_serializing_if = "Option::is_none")]
access_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
refresh_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
expires_in: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
error: Option<String>,
}
impl<'r> Client<'r> {
pub fn new(config: Config<'r>) -> Self {
Client {
config,
state: Arc::new(RwLock::new(State::default())),
}
}
pub async fn authorize<'a>(&self, code: &'a str) -> Result<(), Error> {
let client = reqwest::Client::new();
let mut state = self.state.write().await;
let mut params = HashMap::new();
params.insert("grant_type", "authorization_code");
params.insert("code", code);
params.insert("redirect_uri", &self.config.redirect_uri);
let access_token_request = match &self.config.client_secret {
Some(client_secret) => client
.post(self.config.token_url)
.form(¶ms)
.basic_auth(&self.config.client_id, client_secret.get_unique())
.build()
.unwrap(),
None => {
params.insert("client_id", &self.config.client_id);
client
.post(self.config.token_url)
.form(¶ms)
.build()
.unwrap()
}
};
let token_response = client
.execute(access_token_request)
.await
.map_err(|_| Error::AuthorizationFailed)?;
let token_map: TokenMap = parse_token_response(token_response).await.unwrap();
if let Some(err) = token_map.error {
return Err(Error::Response(err.to_string()));
}
if let Some(token) = token_map.access_token {
state.token = Some(token);
state.refresh = token_map.refresh_token;
state.until = token_map.expires_in;
return Ok(());
}
Err(Error::MissingToken)
}
pub async fn retrieve_protected_page(&self) -> Result<String, Error> {
let client = reqwest::Client::new();
let state = self.state.read().await;
let token = match &state.token {
Some(token) => token,
None => return Err(Error::NoToken),
};
// Request the page with the oauth token
let page_request = client
.get(self.config.protected_url)
.header(header::AUTHORIZATION, "Bearer ".to_string() + &token)
.build()
.unwrap();
let page_response = match client.execute(page_request).await {
Ok(response) => response,
Err(_) => return Err(Error::AccessFailed),
};
let protected_page = page_response.text().await.unwrap();
Ok(protected_page)
}
pub async fn refresh(&self) -> Result<(), Error> {
let client = reqwest::Client::new();
let mut state = self.state.write().await;
let refresh = match state.refresh {
Some(ref refresh) => refresh.clone(),
None => return Err(Error::NoToken),
};
let mut params = HashMap::new();
params.insert("grant_type", "refresh_token");
params.insert("refresh_token", &refresh);
let access_token_request = match &self.config.client_secret {
Some(client_secret) => client
.post(self.config.refresh_url)
.form(¶ms)
.basic_auth(&self.config.client_id, client_secret.get_unique())
.build()
.unwrap(),
None => {
params.insert("client_id", &self.config.client_id);
client
.post(self.config.refresh_url)
.form(¶ms)
.build()
.unwrap()
}
};
let token_response = client
.execute(access_token_request)
.await
.map_err(|_| Error::RefreshFailed)?;
let token_map: TokenMap = parse_token_response(token_response).await.unwrap();
if token_map.error.is_some() || !token_map.access_token.is_some() {
return Err(Error::MissingToken);
}
let token = token_map.access_token.unwrap();
state.token = Some(token);
state.refresh = token_map.refresh_token.or(state.refresh.take());
state.until = token_map.expires_in;
Ok(())
}
pub async fn as_html(&self) -> String {
format!("{}", self.state.read().await)
}
}
pub async fn parse_token_response<'r>(response: Response) -> Result<TokenMap, serde_json::Error> {
let tkn = response.text().await;
let token = tkn.unwrap();
let res: Result<TokenMap, serde_json::Error> =
serde_json::from_str(&token /* <<< here is the error */);
match res {
Ok(val) => return Ok(val.clone()),
Err(e) => return Err(e),
};
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Error::Invalid(err)
}
}
impl<'r> fmt::Display for State {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("Token {<br>")?;
write!(f, " token: {:?},<br>", self.token)?;
write!(f, " refresh: {:?},<br>", self.refresh)?;
write!(f, " expires_in: {:?},<br>", self.until)?;
f.write_str("}")
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Error::AuthorizationFailed => f.write_str("Could not fetch bearer token"),
Error::NoToken => f.write_str("No token with which to access protected page"),
Error::AccessFailed => {
f.write_str("Access token failed to authorize for protected page")
}
Error::RefreshFailed => f.write_str("Could not refresh bearer token"),
Error::Invalid(serde) => write!(f, "Bad json response: {}", serde),
Error::MissingToken => write!(f, "No token nor error in server response"),
Error::Response(err) => write!(f, "Server error while fetching token: {}", err),
}
}
}
TokenMap
does not need a lifetime or any borrowing. Arguably your other structs should also not store any borrows.