use jsonwebtoken::{DecodingKey, Validation};
use oauth2::basic::BasicTokenType;
use oauth2::{AuthorizationCode, EmptyExtraTokenFields, RefreshToken, StandardTokenResponse};
use crate::error::{Error, OAuthError};
use crate::model::oauth2::{EveJwtClaims, EveJwtKey};
use crate::oauth2::client::OAuth2Client;
use crate::oauth2::OAuth2Endpoints;
use crate::Client;
impl<'a> OAuth2Endpoints<'a> {
pub async fn get_token(
&self,
code: &str,
) -> Result<StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>, Error> {
let oauth_client = get_oauth_client(self.client)?;
let message = "Attempting to fetch JWT token using provided authorization code";
debug!("{}", message);
match oauth_client
.exchange_code(AuthorizationCode::new(code.to_string()))
.request_async(&self.client.inner.reqwest_client)
.await
{
Ok(token) => {
debug!("{}", "JWT Token fetched successfully");
Ok(token)
}
Err(err) => {
let message = format!("Error fetching token: {:#?}", err);
error!("{}", message);
Err(Error::OAuthError(OAuthError::RequestTokenError(err)))
}
}
}
pub async fn get_token_refresh(
&self,
refresh_token: String,
) -> Result<StandardTokenResponse<EmptyExtraTokenFields, BasicTokenType>, Error> {
let oauth_client = get_oauth_client(self.client)?;
let refresh_token = RefreshToken::new(refresh_token);
let message = "Attempting to refresh JWT token using provided refresh token";
debug!("{}", message);
match oauth_client
.exchange_refresh_token(&refresh_token)
.request_async(&self.client.inner.reqwest_client)
.await
{
Ok(token) => {
debug!("{}", "JWT Token refreshed successfully");
Ok(token)
}
Err(err) => {
let message = format!("Error refreshing JWT token token: {:#?}", err);
error!("{}", message);
Err(Error::OAuthError(OAuthError::RequestTokenError(err)))
}
}
}
pub async fn validate_token(&self, token_secret: String) -> Result<EveJwtClaims, Error> {
debug!("Attempting JWT token validation");
match attempt_validation(self.client, &token_secret).await {
Ok(claims) => Ok(claims),
Err(err) => {
let cache_cleared = self.client.inner.jwt_key_cache.clear_cache().await;
if cache_cleared {
let message = format!(
"Making 2nd attempt to validate token due to previous error: {:#?}",
&err
);
debug!("{}", message);
attempt_validation(self.client, &token_secret).await
} else {
let message = format!("Failed to validate JWT token due to error: {:#?}", &err);
debug!("{}", message);
Err(err)
}
}
}
}
}
async fn attempt_validation(client: &Client, token_secret: &str) -> Result<EveJwtClaims, Error> {
trace!("Retrieving keys for validation from JWT key cache");
let jwt_keys = client.oauth2().jwk().get_jwt_keys().await?;
let mut validation = Validation::new(jsonwebtoken::Algorithm::RS256);
validation.set_audience(&[client.inner.jwt_audience.to_string()]);
validation.set_issuer(&client.inner.jwt_issuers);
trace!("Checking JWT key cache for RS256 key");
if let Some(EveJwtKey::RS256 { ref n, ref e, .. }) = &jwt_keys.get_first_rs256_key() {
trace!("Creating a decoding key from RS256 key");
let decoding_key = match DecodingKey::from_rsa_components(n, e) {
Ok(key) => {
trace!("Created decoding key from RS256 key successfully");
key
}
Err(err) => {
error!("Failed to decode RS256 key for token validation: {}", &err);
return Err(Error::OAuthError(OAuthError::ValidateTokenError(err)));
}
};
debug!("Validating token using RS256 decoding key");
match jsonwebtoken::decode::<EveJwtClaims>(token_secret, &decoding_key, &validation) {
Ok(token_data) => {
let character_id = token_data.claims.character_id()?;
let message = format!(
"Successfully validated JWT token for character ID: {}",
character_id
);
info!("{}", message);
Ok(token_data.claims)
}
Err(err) => {
let message = format!("Failed to validate token with RS256 key: {}", &err);
error!("{}", message);
Err(Error::OAuthError(OAuthError::ValidateTokenError(err)))
}
}
} else {
let message: &str =
"Failed to find RS256 key in JWT key cache when attempting to validate a JWT token.";
error!(message);
Err(Error::OAuthError(OAuthError::NoValidKeyFound(
message.to_string(),
)))
}
}
fn get_oauth_client(client: &Client) -> Result<&OAuth2Client, Error> {
trace!("Attempting to retrieve OAuth2 client from ESI client");
match client.inner.oauth2_client {
Some(ref client) => {
trace!("{}", "Found OAuth2 client on ESI client");
Ok(client)
}
None => {
error!("{}", Error::OAuthError(OAuthError::OAuth2NotConfigured));
Err(Error::OAuthError(OAuthError::OAuth2NotConfigured))
}
}
}