use crate::error::{AuthError, CoreError, Result, TokenErrorType};
use crate::domain::model::{Claims, IssuedTokens, RefreshToken, TokenType};
use crate::intern::session::SessionService;
impl SessionService {
#[tracing::instrument(name = "auth.consume_refresh_token", skip(self, refresh_token))]
pub async fn consume_refresh_token(&self, refresh_token: &str) -> Result<String> {
let claims: Claims = self.crypto.jwt()?.decode(refresh_token)?;
if claims.token_type != TokenType::RefreshToken {
return Err(CoreError::Unauthenticated(AuthError::TokenInvalid {
token_type: TokenErrorType::RefreshToken,
}));
}
match self.jwt_repository.find_and_consume(&claims).await {
Ok(_) => Ok(claims.sub),
Err(CoreError::Unauthenticated { .. }) => {
self.jwt_repository.revoke(&claims.sub).await?;
Err(CoreError::Unauthenticated(AuthError::TokenReplay {
token_type: TokenErrorType::RefreshToken,
}))
}
Err(e) => Err(e), }
}
#[tracing::instrument(
name = "auth.issue_jwt", skip(self), fields(user.id = user_id)
)]
pub async fn issue_jwt(
&self,
user_id: &str,
full_permissions: Vec<String>,
) -> Result<IssuedTokens> {
let jwt_config = self.configuration.auth.jwt()?;
let rbac_config = &self.configuration.auth.rbac;
let jwt = self.crypto.jwt()?;
let (access_claims, refresh_claims) = Claims::new(user_id, &rbac_config.default_role)
.with_issuer(&jwt_config.issuer)
.with_audience(&jwt_config.audience)
.with_permissions(full_permissions)
.into_token_pair(jwt_config);
let jti = refresh_claims.jti;
let refresh_token = RefreshToken::new(&jti.to_string())
.with_user_id(user_id)
.with_expire_at(jwt_config.refresh_token_expires_in);
self.jwt_repository.insert(refresh_token).await?;
let access = jwt.encode(access_claims)?;
let refresh = jwt.encode(refresh_claims)?;
Ok(IssuedTokens::default()
.with_access_token(&access)
.with_refresh_token(&refresh)
.with_jti(jti))
}
#[tracing::instrument(name = "auth.invalidate_jwt", skip(self, refresh_token))]
pub async fn invalidate_jwt(&self, refresh_token: &str) -> Result<()> {
let claims: Claims = self.crypto.jwt()?.decode(refresh_token)?;
if claims.token_type != TokenType::RefreshToken {
return Err(CoreError::from(AuthError::TokenInvalid {
token_type: TokenErrorType::RefreshToken,
}));
}
self.jwt_repository.invalidate(claims.jti).await?;
Ok(())
}
#[tracing::instrument(name = "auth.logout_all", skip(self), fields(user.id = user_id))]
pub async fn logout_all(&self, user_id: &str) -> Result<()> {
self.jwt_repository.revoke(user_id).await?;
self.session_repository.revoke(user_id).await?;
Ok(())
}
#[tracing::instrument(name = "auth.find_jwt_by_jti", skip(self, jti))]
pub async fn find_jwt_by_jti(&self, jti: &str) -> Result<RefreshToken> {
self.jwt_repository.find_by_jti(jti).await
}
}