InfraNotes Module · v0
Welcome
Select a document from the sidebar to read it.
NATS Integration Guide for Authentication
Deprecated – historical NATS design
InfraNotes has fully migrated away from NATS. The financial analytics module now integrates with InfraNotes via HTTP APIs secured with shared JWT authentication, and the InfraNotes platform uses Kafka as its event bus. This document is kept only for historical reference and must not be used as a guide for new work.
This document outlines the previous integration between InfraNotes (main application) and InfraNotes-Module (financial analytics module) using NATS for authentication and authorization.
Overview
The integration will use NATS as a message broker to:
- Validate authentication tokens between services
- Synchronize user permissions
- Propagate authentication events (login, logout, etc.)
- Enable secure communication between services
Current Implementation
InfraNotes (Main Application)
The main application currently handles authentication with:
- JWT-based authentication with access and refresh tokens
- Role-based authorization (admin, user)
- Session management with device tracking
- MFA support and email verification
- Token blacklisting for security
InfraNotes-Module (Financial Analytics)
The module currently has:
- Placeholder authentication in
internal/common/auth/auth.go - Development mode that uses a default user ID
NATS Integration Architecture
┌────────────────┐ ┌────────────────┐
│ │ │ │
│ InfraNotes │◄───NATS Bus────►│ InfraNotes │
│ (Main App) │ │ Module │
│ │ │ │
└────────────────┘ └────────────────┘
▲ ▲
│ │
│ │
▼ ▼
┌────────────────┐ ┌────────────────┐
│ │ │ │
│ Database │ │ Database │
│ │ │ │
└────────────────┘ └────────────────┘
Message Subjects
| Subject Pattern | Description | Direction |
|---|---|---|
auth.validate.token |
Validate a JWT token | Module → Main |
auth.validate.user.<user_id> |
Validate user permissions | Module → Main |
auth.event.login |
User login notification | Main → Module |
auth.event.logout |
User logout notification | Main → Module |
auth.event.token.revoked |
Token revocation notification | Main → Module |
Message Formats
Token Validation Request
{
"token": "jwt_token_string",
"request_id": "uuid_for_request_tracking"
}
Token Validation Response
{
"valid": true,
"user_id": "user_uuid",
"user_role": "user",
"permissions": ["read:analytics", "write:documents"],
"session_id": "session_uuid",
"request_id": "uuid_from_request",
"error": ""
}
User Validation Request
{
"user_id": "user_uuid",
"resource": "analytics/documents",
"action": "read",
"request_id": "uuid_for_request_tracking"
}
User Validation Response
{
"permitted": true,
"user_id": "user_uuid",
"request_id": "uuid_from_request",
"error": ""
}
Implementation Guidelines
For InfraNotes (Main App)
- NATS Connection Setup
// Connect to NATS
nc, err := nats.Connect("nats://nats:4222", nats.Name("infranotes-main"))
if err != nil {
log.Fatalf("Failed to connect to NATS: %v", err)
}
- Authentication Validation Endpoints
// Subscribe to token validation requests
nc.Subscribe("auth.validate.token", func(msg *nats.Msg) {
var req TokenValidationRequest
if err := json.Unmarshal(msg.Data, &req); err != nil {
// Handle error
return
}
// Validate token using existing AuthService
userID, role, sessionID, err := authService.VerifyToken(req.Token)
// Prepare and send response
resp := TokenValidationResponse{
Valid: err == nil,
UserID: userID,
UserRole: role,
SessionID: sessionID,
RequestID: req.RequestID,
}
if err != nil {
resp.Error = err.Error()
}
respData, _ := json.Marshal(resp)
msg.Respond(respData)
})
- Event Publishing
// Publish authentication events
func publishAuthEvent(nc *nats.Conn, eventType string, data interface{}) error {
eventData, err := json.Marshal(data)
if err != nil {
return err
}
return nc.Publish("auth.event."+eventType, eventData)
}
For InfraNotes-Module
- NATS Connection Setup
// Connect to NATS
nc, err := nats.Connect("nats://nats:4222", nats.Name("infranotes-module"))
if err != nil {
log.Fatalf("Failed to connect to NATS: %v", err)
}
- Authentication Middleware
// NatsAuthMiddleware validates authentication with the main app via NATS
func NatsAuthMiddleware(nc *nats.Conn) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Get Authorization header
authHeader := c.Request().Header.Get("Authorization")
if authHeader == "" || !strings.HasPrefix(authHeader, "Bearer ") {
return echo.NewHTTPError(http.StatusUnauthorized, "Invalid or missing Authorization header")
}
// Extract token
token := strings.TrimPrefix(authHeader, "Bearer ")
// Create validation request
req := TokenValidationRequest{
Token: token,
RequestID: uuid.New().String(),
}
// Request validation from main app
reqData, _ := json.Marshal(req)
msg, err := nc.Request("auth.validate.token", reqData, 5*time.Second)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Authentication service unavailable")
}
// Parse response
var resp TokenValidationResponse
if err := json.Unmarshal(msg.Data, &resp); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Invalid response from auth service")
}
// Check if token is valid
if !resp.Valid {
return echo.NewHTTPError(http.StatusUnauthorized, resp.Error)
}
// Set user ID in context
userID, _ := uuid.Parse(resp.UserID)
auth.SetUserIDToContext(c, userID)
// Continue to next handler
return next(c)
}
}
}
- Event Subscription
// Subscribe to auth events for token revocation
nc.Subscribe("auth.event.token.revoked", func(msg *nats.Msg) {
var event TokenRevokedEvent
if err := json.Unmarshal(msg.Data, &event); err != nil {
log.Printf("Error parsing token revoked event: %v", err)
return
}
// Update local token blacklist or cache
tokenBlacklistService.AddToken(event.Token, event.Reason)
})
Data Models
// TokenValidationRequest represents a request to validate a token
type TokenValidationRequest struct {
Token string `json:"token"`
RequestID string `json:"request_id"`
}
// TokenValidationResponse represents the response to a token validation
type TokenValidationResponse struct {
Valid bool `json:"valid"`
UserID string `json:"user_id"`
UserRole string `json:"user_role"`
Permissions []string `json:"permissions"`
SessionID string `json:"session_id"`
RequestID string `json:"request_id"`
Error string `json:"error,omitempty"`
}
// TokenRevokedEvent represents a token revocation event
type TokenRevokedEvent struct {
Token string `json:"token"`
UserID string `json:"user_id"`
RevokedAt time.Time `json:"revoked_at"`
Reason string `json:"reason,omitempty"`
SessionID string `json:"session_id,omitempty"`
RevokedByApp string `json:"revoked_by_app"`
}
Docker Compose Updates
Both applications will need access to the NATS server. Update the docker-compose.yml files to include:
services:
nats:
image: nats:latest
ports:
- "4222:4222"
- "8222:8222"
volumes:
- ./configs/nats:/etc/nats
command: "--config /etc/nats/nats.conf"
Error Handling and Fallbacks
-
If NATS connection fails:
- Log the error
- Use fallback authentication method (e.g., direct HTTP call)
- Retry connection with exponential backoff
-
If validation request times out:
- Cache validation results to reduce dependency on NATS
- Use circuit breaker pattern to prevent cascading failures
Security Considerations
- Ensure NATS connections use TLS in production
- Implement proper NATS authentication (username/password or certificates)
- Keep token validation responses minimal (only include necessary data)
- Add rate limiting for authentication requests
- Monitor for suspicious validation patterns
Testing
- Unit tests for NATS message format validation
- Integration tests between services
- Fault injection testing (NATS server down scenarios)
- Performance testing under load
- Security testing (attempt to bypass authentication)
Deployment Plan
- Develop and test in local environment
- Deploy NATS server
- Update InfraNotes with publisher implementation
- Update InfraNotes-Module with subscriber implementation
- Verify integration end-to-end
- Monitor and adjust as needed