/

User Identification

ByteHide Logger provides user identification features to track which user performed actions. This is essential for auditing, debugging, and understanding user behavior in production systems.

AuthUser Struct

The AuthUser struct contains user authentication information:

Go
type AuthUser struct {
    ID    string // Unique identifier
    Email string // User's email address
    Token string // Authentication token
}

Setting Authenticated Users

Basic User Identification

Set the authenticated user when they log in or their context becomes available:

Go
import "github.com/bytehide/bytehide-logs-go"

user := &logs.AuthUser{
    ID:    "user_12345",
    Email: "john.doe@example.com",
    Token: "eyJhbGc...", // JWT or session token
}

logs.SetUser(user)

logs.Info("User logged in successfully")

User Identification in HTTP Middleware

Extract user info from authentication context and set globally:

Go
func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Extract user from JWT or session
        user, err := extractUser(r)
        if err != nil {
            logs.Warn("Failed to extract user information")
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }
        
        // Set user in logger
        authUser := &logs.AuthUser{
            ID:    user.ID,
            Email: user.Email,
            Token: user.SessionToken,
        }
        logs.SetUser(authUser)
        
        logs.WithContext("userId", user.ID).
            WithContext("email", user.Email).
            Info("User authenticated")
        
        next.ServeHTTP(w, r)
    })
}

func extractUser(r *http.Request) (*User, error) {
    // Extract from Authorization header, session cookie, etc.
    token := r.Header.Get("Authorization")
    if token == "" {
        return nil, fmt.Errorf("no authorization token")
    }
    
    user, err := parseToken(token)
    if err != nil {
        return nil, fmt.Errorf("invalid token: %w", err)
    }
    
    return user, nil
}

User Identification in Service Layer

Set user context when processing user-specific operations:

Go
type OrderService struct {
    repo OrderRepository
}

func (s *OrderService) CreateOrder(user *AuthUser, request *CreateOrderRequest) (*Order, error) {
    // Set user in logger
    logs.SetUser(user)
    
    logs.WithContext("userId", user.ID).
        WithContext("email", user.Email).
        Info("User creating order")
    
    order := &Order{
        ID:         generateID(),
        UserID:     user.ID,
        CustomerID: request.CustomerID,
        CreatedAt:  time.Now(),
    }
    
    err := s.repo.Save(order)
    if err != nil {
        logs.WithContext("userId", user.ID).
            Error("Failed to create order", err)
        return nil, err
    }
    
    logs.WithContext("userId", user.ID).
        WithContext("orderId", order.ID).
        Info("Order created successfully")
    
    return order, nil
}

Anonymous Users

Use SetAnonymousID() for unauthenticated users or bots:

Go
import "github.com/bytehide/bytehide-logs-go"

// Set anonymous user ID (e.g., from session, device, or request)
anonId := generateSessionID()
logs.SetAnonymousID(anonId)

logs.WithContext("anonId", anonId).
    Info("Anonymous user visited")

Anonymous User in Public APIs

Go
func handlePublicAPIRequest(w http.ResponseWriter, r *http.Request) {
    // Generate or extract anonymous ID
    anonID := extractOrGenerateAnonID(r)
    logs.SetAnonymousID(anonID)
    
    logs.WithContext("anonId", anonID).
        WithContext("path", r.URL.Path).
        WithContext("method", r.Method).
        Debug("Processing public API request")
    
    // Process request
    result, err := processRequest(r)
    if err != nil {
        logs.Error("Public API request failed", err)
        http.Error(w, "Internal Server Error", http.StatusInternalServerError)
        return
    }
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(result)
}

func extractOrGenerateAnonID(r *http.Request) string {
    // Try to get from cookie
    cookie, err := r.Cookie("session_id")
    if err == nil {
        return cookie.Value
    }
    
    // Generate new ID
    return generateSessionID()
}

User Lifecycle

Login Flow

Go
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
    var loginReq struct {
        Email    string `json:"email"`
        Password string `json:"password"`
    }
    
    err := json.NewDecoder(r.Body).Decode(&loginReq)
    if err != nil {
        logs.WithContext("email", loginReq.Email).
            Error("Failed to decode login request", err)
        http.Error(w, "Bad Request", http.StatusBadRequest)
        return
    }
    
    // Authenticate user
    user, err := h.service.AuthenticateUser(loginReq.Email, loginReq.Password)
    if err != nil {
        logs.WithContext("email", loginReq.Email).
            Warn("User authentication failed")
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }
    
    // Set user in logger
    authUser := &logs.AuthUser{
        ID:    user.ID,
        Email: user.Email,
        Token: user.Token,
    }
    logs.SetUser(authUser)
    
    logs.WithContext("userId", user.ID).
        WithContext("email", user.Email).
        Info("User logged in successfully")
    
    // Return token
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{
        "token": user.Token,
    })
}

Logout Flow

Go
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
    user, err := extractUserFromRequest(r)
    if err != nil {
        logs.Warn("Failed to extract user for logout")
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }
    
    // Log logout with current user context
    authUser := &logs.AuthUser{
        ID:    user.ID,
        Email: user.Email,
    }
    logs.SetUser(authUser)
    
    logs.WithContext("userId", user.ID).
        WithContext("email", user.Email).
        Info("User logged out")
    
    // Clear user from logger
    logs.SetUser(nil)
    
    // Return success
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{
        "status": "logged_out",
    })
}

User Session Management

Go
func (s *SessionService) CreateSession(user *AuthUser) (string, error) {
    logs.SetUser(user)
    
    logs.WithContext("userId", user.ID).
        Trace("Creating new session")
    
    session := &Session{
        ID:        generateSessionID(),
        UserID:    user.ID,
        CreatedAt: time.Now(),
        ExpiresAt: time.Now().Add(24 * time.Hour),
    }
    
    err := s.repo.Save(session)
    if err != nil {
        logs.WithContext("userId", user.ID).
            Error("Failed to create session", err)
        return "", err
    }
    
    logs.WithContext("userId", user.ID).
        WithContext("sessionId", session.ID).
        Debug("Session created successfully")
    
    return session.ID, nil
}

func (s *SessionService) ValidateSession(sessionID string) (*AuthUser, error) {
    session, err := s.repo.Get(sessionID)
    if err != nil {
        logs.WithContext("sessionId", sessionID).
            Debug("Session not found")
        return nil, err
    }
    
    if time.Now().After(session.ExpiresAt) {
        logs.WithContext("sessionId", sessionID).
            Warn("Session expired")
        return nil, fmt.Errorf("session expired")
    }
    
    user := &logs.AuthUser{
        ID: session.UserID,
    }
    logs.SetUser(user)
    
    logs.WithContext("userId", session.UserID).
        Trace("Session validated successfully")
    
    return user, nil
}

Best Practices

User Identification Best Practices

  • Set user early in request processing pipeline (typically in middleware)
  • Use AuthUser for authenticated requests with ID, Email, and Token
  • Use SetAnonymousID() for unauthenticated requests to distinguish them
  • Clear user context on logout with logs.SetUser(nil)
  • Include user ID in log context for additional clarity
  • Protect sensitive data - don't log full tokens or passwords (use data masking)
  • Maintain user consistency throughout request lifecycle

Token Security

While the AuthUser struct includes a Token field for tracking authentication state, never log or expose full token values. Use data masking to protect sensitive tokens.

Next Steps

Previous
Data Masking