Lewati ke konten utama

JWT Authentication Architecture

This documentation explains the JWT authentication implementation using:

  • Axum Router
  • JWT Middleware
  • Access Token Validation
  • Refresh Token Validation
  • Token Generation Service

Project Structure


Authentication Flow Overview


Route Configuration

File Structure


Source Code

pub fn create_routes(
state: AppState,
) -> Router<AppState> {
Router::new()
.route("/refresh", post(refresh))
.route("/refresh-full", post(refresh_full))
.route("/logout", post(logout))
.layer(ServiceBuilder::new().layer(middleware::from_fn(cookie_middleware)))
Router::new()
.route("/user", post(user))
.layer(ServiceBuilder::new().layer(middleware::from_fn(jwt_middleware)))
}

Route Explanation

RouteMethodMiddlewareDescription
/refreshPOSTcookie_middlewareRefresh access token
/refresh-fullPOSTcookie_middlewareFull refresh validation
/logoutPOSTcookie_middlewareLogout current session
/userPOSTjwt_middlewareProtected user endpoint

Middleware Layer Design


JWT Middleware

File Structure


Cookie Middleware

Source Code

pub async fn cookie_middleware(
jar: CookieJar,
req: Request,
next: Next
)->Result<Response,AppError> {
let cookie = jar.get("refresh-token");
if cookie.is_none() {
return Err(
AppError::Unauthorized(
0,
"xxxx".to_string(),
"xxxx".to_string()
)
)
}
Ok(next.run(req).await)
}

Cookie Middleware Explanation

Purpose

Validates the existence of the refresh token cookie.


let cookie = jar.get("refresh-token");

Retrieves the refresh-token cookie from the incoming request.


Validation Logic

if cookie.is_none()

Checks whether the refresh token exists.

If missing:

  • Request is rejected
  • Unauthorized response is returned

Continue Middleware Chain

Ok(next.run(req).await)

Passes request execution to the next middleware or handler.


JWT Middleware

Source Code

pub async fn jwt_middleware(
mut req: Request,
next: Next,
)->Result<Response,AppError>{
let auth_header = req
.headers()
.get(header::AUTHORIZATION)
.and_then(|h| h.to_str().ok());

let auth_header = match auth_header {
Some(v)=>v,
None => {
return Err(AppError::UnauthorizedAuth(41i8,"Authorization".to_string(),"xxxx".to_string()));
}
};
let token = match auth_header.strip_prefix("Bearer ") {
Some(v)=>v,
None => {
return Err(AppError::UnauthorizedAuth(41i8,"Authorization".to_string(),"xxxx".to_string()));
}
};

let claims = match verify_access_token(&token) {
Ok(v)=>v,
Err(e) => {
return Err(AppError::from(e));
}
};
req.extensions_mut().insert(claims);
Ok(next.run(req).await)
}

JWT Middleware Flow


Authorization Header Format

Expected request header:

Authorization: Bearer xxxx

Request Extension Injection

req.extensions_mut().insert(claims);

Stores validated JWT claims inside request extensions.

This allows route handlers to access authenticated user information.


JWT Utility Module

File Structure


Access Token Verification

Source Code

pub fn verify_access_token(
token:&str,
) -> Result<Claims,AppError>{
let mut validation = Validation::default();
validation.set_audience(&["pos-client"]);

let decoded = decode::<Claims>(
token,
&DecodingKey::from_secret(
std::env::var("JWT_SECRET").unwrap().as_bytes(),
),
&validation,
).map_err(|e|{
match *e.kind() {
ErrorKind::ExpiredSignature =>{
AppError::UnauthorizedAuth(41i8,"Authorization".to_string(),"xxxx".to_string())
}
ErrorKind::InvalidToken =>{
AppError::UnauthorizedAuth(41i8,"Authorization".to_string(),"xxxx".to_string())
}
ErrorKind::InvalidSignature =>{
AppError::UnauthorizedAuth(41i8,"Authorization".to_string(),"xxxx".to_string())
}
ErrorKind::InvalidAudience => {
AppError::UnauthorizedAuth(
41i8,"Authorization".to_string(),"xxxx".to_string()
)
}
_=>{
AppError::UnauthorizedAuth(0i8,"Authorization".to_string(),"xxxx".to_string())
}
}
})?;

let claims = decoded.claims;
if claims.r#type !="access-token" {
return Err(AppError::UnauthorizedAuth(0i8,"Authorization".to_string(),"xxxx".to_string()));
}
Ok(claims)
}

Access Token Validation Process


Validation Rules

ValidationDescription
Signature ValidationEnsures token integrity
Audience ValidationEnsures correct client usage
Expiration ValidationRejects expired tokens
Token Type ValidationEnsures access-token usage

Refresh Token Verification

Source Code

pub fn verify_refresh_token(
token:&str,
)->Result<RefreshClaims,AppError> {
let mut validation = Validation::default();

validation.set_audience(&["pos-client"]);

let decoded = decode::<RefreshClaims>(
token,
&DecodingKey::from_secret(
std::env::var("JWT_SECRET").unwrap().as_bytes(),
),
&validation,
).map_err(|e|{
match *e.kind() {
ErrorKind::ExpiredSignature =>{
AppError::UnauthorizedAuth(0i8,"Authorization".to_string(),"xxxx".to_string())
}
ErrorKind::InvalidToken =>{
AppError::UnauthorizedAuth(0i8,"Authorization".to_string(),"xxxx".to_string())
}
ErrorKind::InvalidSignature =>{
AppError::UnauthorizedAuth(0i8,"Authorization".to_string(),"xxxx".to_string())
}
ErrorKind::InvalidAudience => {
AppError::UnauthorizedAuth(
0i8,"Authorization".to_string(),"xxxx".to_string()
)
}
_=>{
AppError::UnauthorizedAuth(0i8,"Authorization".to_string(),"xxxx".to_string())
}
}
})?;

let claims = decoded.claims;
if claims.r#type !="refresh-token" {
return Err(AppError::UnauthorizedAuth(0i8,"Authorization".to_string(),"xxxx".to_string()));
}
Ok(claims)
}

Claims Structure

Access Claims

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Claims{
pub iss:String,
pub sub:String,
pub roles:String,
pub aud:String,

pub exp:usize,
pub iat:usize,
pub nbf:usize,

pub jti:String,
#[serde(rename = "type")]
pub r#type:String,
}

Refresh Claims

#[derive(Debug,Serialize,Deserialize)]
pub struct RefreshClaims{
pub iss:String,
pub sub:String,
pub aud:String,

pub exp:usize,
pub iat:usize,
pub nbf:usize,
pub roles:String,
pub jti:String,
#[serde(rename = "type")]
pub r#type:String,
}

Claims Field Description

FieldDescription
issToken issuer
subUser identifier
rolesUser authorization roles
audIntended audience
expExpiration timestamp
iatIssued-at timestamp
nbfActivation timestamp
jtiUnique token identifier
typeToken category

Token Response Structure

pub struct TokenResponse {
pub token: String,
pub jti: Uuid,
pub user_id: String,
pub expires_in:chrono::NaiveDateTime,
}

JWT Service

File Structure


Access Token Generation

Source Code

pub fn generate_access_token(
user_id:&str,
roles:&str,
)->Result<String,String>{
let now = Utc::now();
let expiration = now + Duration::minutes(15);
let ts = Timestamp::now(NoContext);
let jti = Uuid::new_v7(ts);
let user_id =user_id.to_string();
let claims = Claims{
iss:"pos-system".to_string(),
sub:user_id.clone(),
roles:roles.to_string(),
aud:"pos-client".to_string(),
exp:expiration.timestamp() as usize,
iat: now.timestamp() as usize,
nbf:now.timestamp() as usize,
jti:jti.clone().to_string(),
r#type:"access-token".to_string(),
};
let token = encode_jwt(&claims)?;
Ok(token)
}

Refresh Token Generation

Source Code

pub fn generate_refresh_token(
user_id:&str,
roles:&str,
)->Result<TokenResponse,String>{
let now = Utc::now();
let expiration = now + Duration::days(7);
let ts = Timestamp::now(NoContext);
let jti = Uuid::new_v7(ts);
let claims = RefreshClaims{
iss:"pos-system".to_string(),
sub:user_id.to_string(),
aud:"pos-client".to_string(),
exp:expiration.timestamp() as usize,
roles:roles.to_string(),
iat: now.timestamp() as usize,
nbf:now.timestamp() as usize,
jti:jti.clone().to_string(),
r#type:"refresh-token".to_string(),
};
let token = encode_jwt(&claims)?;
Ok(TokenResponse {
token,
jti,
user_id:user_id.to_string(),
expires_in:expiration.naive_utc(),
})
}

Token Generation Lifecycle


Access Token Properties

PropertyValue
TypeAccess Token
Expiration15 Minutes
Audiencepos-client
Issuerpos-system

Refresh Token Properties

PropertyValue
TypeRefresh Token
Expiration7 Days
Audiencepos-client
Issuerpos-system

Security Design

Access Token

Short-lived token used for API authorization.


Refresh Token

Long-lived token used for session continuation.


JWT Secret

Used for:

  • Token signature generation
  • Signature verification
  • Token integrity validation

Authentication Architecture Summary

ComponentResponsibility
RouterRegister authentication endpoints
Cookie MiddlewareValidate refresh token cookie
JWT MiddlewareValidate access token
JWT UtilityDecode and verify tokens
JWT ServiceGenerate access and refresh tokens
Claims StructureStore authentication payload
TokenResponseReturn generated token data