use crate::authn::service::AuthnService;
use crate::authn::{
event::{AuthEventBuilder, AuthEventType, AuthFailureReason},
factor::FactorKind,
ids::UserId,
store::{FactorStore, IdentityStore},
};
impl<I, F> AuthnService<I, F>
where
I: IdentityStore,
F: FactorStore<Error = I::Error>,
{
#[tracing::instrument(skip(self, refresh_token))]
pub async fn refresh_oauth_token(
&self,
provider_name: &str,
refresh_token: &str,
) -> Result<axess_factors::oauth::OAuthClaims, axess_factors::oauth::OAuthError> {
use axess_factors::oauth::OAuthError;
if refresh_token.is_empty() {
self.record_oauth_refresh_failure(
AuthFailureReason::TokenRefreshNoToken,
provider_name,
)
.await?;
return Err(OAuthError::NoRefreshToken);
}
let Some(provider) = self.inner.oauth_providers.get(provider_name) else {
self.record_oauth_refresh_failure(
AuthFailureReason::TokenRefreshUnknownProvider,
provider_name,
)
.await?;
return Err(OAuthError::UnknownProvider(provider_name.to_string()));
};
let provider = provider.clone();
let claims = match provider.refresh_token(refresh_token).await {
Ok(c) => c,
Err(e) => {
self.record_oauth_refresh_failure(
AuthFailureReason::TokenRefreshProviderRejected,
provider_name,
)
.await?;
return Err(e);
}
};
let subject_user = UserId::try_new(claims.subject.as_str()).ok();
self.emit_audit(
AuthEventBuilder::success(AuthEventType::LoginAttempt)
.maybe_attributed_to(subject_user.as_ref(), None)
.with_factor(FactorKind::Federated(
crate::authn::factor::FederatedProvider::Custom(provider_name.into()),
))
.with_error(AuthFailureReason::TokenRefresh),
)
.await
.map_err(|e| OAuthError::AuditStore(e.to_string()))?;
Ok(claims)
}
async fn record_oauth_refresh_failure(
&self,
reason: AuthFailureReason,
provider_name: &str,
) -> Result<(), axess_factors::oauth::OAuthError> {
use axess_factors::oauth::OAuthError;
self.emit_audit(
AuthEventBuilder::failure(AuthEventType::LoginAttempt)
.with_factor(FactorKind::Federated(
crate::authn::factor::FederatedProvider::Custom(provider_name.into()),
))
.with_error(reason),
)
.await
.map_err(|e| OAuthError::AuditStore(e.to_string()))
}
}