How Macro Microservices Handle Authentication with JWT Tokens
Macro's microservice architecture uses HS256-signed JWT tokens validated by shared Axum middleware, with secrets fetched from AWS Secrets Manager at startup and user identity extracted into strongly-typed MacroUserId for downstream authorization.
Every service in the Macro repository implements the same authentication flow through reusable crates. This design ensures stateless, consistent JWT validation across the entire service mesh without duplicating security logic.
JWT Authentication Architecture Overview
The authentication system spans two core crates and multiple service implementations. According to the macro-inc/macro source code, the flow depends on environment-driven configuration, a trait-based validator abstraction, and Axum middleware integration.
Key Components
crates/decode_jwt/src/lib.rs— Token structures (MacroAccessToken,DocumentPermissionsToken) and decoding primitivescrates/macro_authorization/src/outbound/macro_auth.rs—MacroAuthJwtValidatorimplementationcrates/macro_authorization/src/lib.rs— Public exports and theJwtValidatortrait- Service
main.rsfiles — Bootstrap and middleware injection
Environment-Driven JWT Configuration
Each service initializes authentication by building a JwtValidationArgs struct. The async constructor JwtValidationArgs::new_with_secret_manager (defined in crates/decode_jwt/src/lib.rs at lines 31-35) fetches:
jwt_secret— The HS256 signing keyaudience— Token audience claimissuer— Token issuer claim
In production, credentials come from AWS Secrets Manager. In development, they load from local environment variables.
// Typically in each service's main.rs
let jwt_args = JwtValidationArgs::new_with_secret_manager(
environment.clone(),
&secretsmanager_client,
)
.await
.context("failed to build jwt validation args")?;
This pattern appears in services/static_file_service/src/main.rs and services/document_storage_service/src/main.rs, ensuring every service bootstraps authentication identically.
The JwtValidator Trait and MacroAuthJwtValidator
The macro_authorization crate defines a reusable validation layer. In crates/macro_authorization/src/outbound/macro_auth.rs, the MacroAuthJwtValidator::new method accepts JwtValidationArgs and stores:
- The decoded JWT secret
- Expected audience and issuer values
This validator implements the JwtValidator trait, enabling dependency injection and test mocking.
let validator = MacroAuthJwtValidator::new(jwt_args);
The validator is then wrapped into MacroAuthorizationState and injected into Axum routers as application state.
Token Validation in Axum Middleware
When requests arrive, middleware extracts the Bearer token and delegates to MacroAuthJwtValidator::validate. The implementation in services/mcp_auth_proxy/src/inbound/middleware.rs (lines 73-89) demonstrates this pattern:
async fn auth_middleware(
State(jwt_args): axum::extract::State<JwtValidationArgs>,
axum::extract::TypedHeader(bearer): axum::extract::TypedHeader<Bearer>,
) -> Result<JwtToken, MacroAuthError> {
handler(&jwt_args, &bearer.token())
}
Internally, validate_macro_access_token_inner (in decode_jwt.rs, lines 66-78) calls jsonwebtoken::decode with:
- Algorithm:
HS256 - Audience validation: Matches configured audience
- Issuer validation: Matches configured issuer
Error Handling
The validator returns specific error variants for debugging and metrics:
MacroAuthError::JwtExpired— Tokenexpclaim is in the pastMacroAuthError::JwtValidationFailed— Signature mismatch, audience/issuer mismatch, or malformed token
Extracting User Identity from Validated Tokens
After signature verification, the token payload deserializes into MacroAccessToken (defined in decode_jwt.rs, lines 93-112). This struct contains:
pub struct MacroAccessToken {
pub macro_user_id: String,
// ... additional claims
}
The macro_user_id field parses into a strongly-typed MacroUserId that downstream handlers use for authorization decisions. The MacroAuthorizationExtractor helper (used across all services) provides convenient access:
async fn protected_endpoint(
State(auth_state): State<MacroAuthorizationState>,
extractor: MacroAuthorizationExtractor,
) -> Result<Json<...>, ApiError> {
let user_id = extractor.user_id()?;
// `user_id` is a `MacroUserId` derived from the JWT
...
}
Service-Specific JWT Variants: Document Permissions
Some services extend the pattern for fine-grained access control. The document storage service implements a DocumentPermissionsToken for temporary, scoped access to stored files.
Token Creation
In services/document_storage_service/src/api/documents/permissions_token/create_permission_token.rs, the service creates permission tokens using:
- The same
jwt_secretfromconfig.document_permission_jwt - A distinct claim structure encoding document IDs and access levels
Token Validation
The counterpart in services/document_storage_service/src/api/documents/permissions_token/validate_permissions_token.rs validates these tokens using identical cryptographic primitives but different deserialization logic.
Why This Design Scales
Stateless validation — No session store or inter-service authentication calls. Each service independently verifies tokens using locally cached secrets.
Centralized secret management — Secrets fetch once at startup via new_with_secret_manager. No runtime secret retrieval latency or failure modes.
Uniform middleware — Every service uses MacroAuthJwtValidator and MacroAuthorizationExtractor. Security fixes apply globally by updating the shared crate.
Strong typing — Raw JWT claims convert to MacroUserId at the trust boundary, preventing string-typed identity bugs in business logic.
Summary
- Macro microservices authenticate with HS256-signed JWT tokens validated by shared Axum middleware
JwtValidationArgs::new_with_secret_managersources secrets from AWS Secrets Manager or environment variables at startupMacroAuthJwtValidator(incrates/macro_authorization/src/outbound/macro_auth.rs) provides the concrete validation implementation- Validated tokens deserialize into
MacroAccessTokenwith strongly-typedMacroUserIdextraction - Services like document storage extend the pattern with service-specific permission tokens using the same cryptographic foundation
- The architecture is stateless, centrally configured, and uniformly implemented across all services
Frequently Asked Questions
What JWT algorithm does Macro use for token signing?
Macro uses HS256 (HMAC with SHA-256). The jsonwebtoken::decode call in validate_macro_access_token_inner explicitly specifies this algorithm, and secrets are symmetric keys fetched from AWS Secrets Manager or environment variables.
How does Macro handle JWT expiration?
The validator returns MacroAuthError::JwtExpired when a token's exp claim is in the past. This error propagates through the Axum middleware and converts to an appropriate HTTP 401 response. Services do not implement custom expiration handling—it's uniform across the codebase.
Can services use different JWT secrets?
Yes. Services read from jwt_secret (for standard access tokens) or service-specific config like config.document_permission_jwt (for document permissions in the storage service). All secrets still originate from the same secret manager infrastructure, but different keys can be configured per-service and per-token-type.
Is the JWT validation logic testable?
Yes. The JwtValidator trait abstraction in crates/macro_authorization/src/lib.rs allows mocking validators in unit tests. Services inject MacroAuthJwtValidator through Axum's state system, so test suites can substitute stub implementations without HTTP middleware changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →