← Back to Blog

The Gateway Guardian: Multi-Factor Authentication Architecture in Industrial Systems

In distributed systems, authentication is the first line of defense. Different services may require different authentication methods — some only need simple OAuth tokens, while others require more complex TVM (Ticket Valet Machine) ticket verification. How do we design a flexible yet secure authentication architecture? Let's analyze an industrial authentication interface design.

The Core Problem: Balancing Flexibility and Security

The fundamental challenges in authentication systems:

  1. Multiple auth methods: OAuth, TVM, API Key, etc.
  2. Security requirements: Different levels need different auth strength
  3. Extensibility: Adding new auth methods shouldn't affect existing code

The solution is interface abstraction + composition pattern:

  • Define unified authentication interface
  • Implement multiple authentication methods
  • Support multi-factor combined authentication

Core Design in Industrial Implementation

In an industrial cloud storage system, I found an elegant authentication interface design. Its design choices are remarkably pragmatic:

Design One: Authentication Interface Abstraction

class IAuth {
public:
    virtual void UpdateRequest(NNeh::THttpRequest& request) const = 0;
};

Choice: Use abstract interface to define authentication methods

Trade-off considerations:

  • Pros: Unified interface, easy to extend
  • Cons: Virtual function overhead

Design Two: OAuth Token Authentication

class TOAuthToken : public IAuth {
public:
    TString Token;
    void UpdateRequest(NNeh::THttpRequest& request) const override;
};

Choice: Implement OAuth token authentication

Trade-off considerations:

  • Pros: Simple to use, widely supported
  • Cons: Token leakage risk

Design Three: TVM Ticket Authentication

class TTvmAuth : public IAuth {
public:
    TAtomicSharedPtr<NTvmAuth::TTvmClient> Client;
    NTvmAuth::TTvmId DestinationTvmId;
    void UpdateRequest(NNeh::THttpRequest& request) const override;
};

Choice: Implement internal TVM ticket authentication

Trade-off considerations:

  • Pros: More secure, tickets have expiration
  • Cons: Requires TVM service support

Clean-room Reimplementation: Go Implementation

To demonstrate the design thinking, I reimplemented the core logic in Go:

package main

import (
	"fmt"
	"sync"
)

// AuthMethod defines the authentication interface
type AuthMethod interface {
	Authenticate(req *HttpRequest)
}

// HttpRequest represents an HTTP request
type HttpRequest struct {
	URL     string
	Headers map[string]string
	mu      sync.RWMutex
}

func (r *HttpRequest) SetHeader(key, value string) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.Headers[key] = value
}

// OAuthToken implements OAuth token authentication
type OAuthToken struct {
	Token string
}

func (t *OAuthToken) Authenticate(req *HttpRequest) {
	req.SetHeader("Authorization", "OAuth "+t.Token)
}

// TvmClient represents a TVM client
type TvmClient struct {
	mu      sync.RWMutex
	tickets map[int]string
}

func (c *TvmClient) GetTicket(destID int) string {
	c.mu.Lock()
	defer c.mu.Unlock()
	if ticket, ok := c.tickets[destID]; ok {
		return ticket
	}
	c.tickets[destID] = fmt.Sprintf("tvm_ticket_%d", destID)
	return c.tickets[destID]
}

// TvmAuth implements TVM ticket authentication
type TvmAuth struct {
	Client         *TvmClient
	DestinationID int
}

func (t *TvmAuth) Authenticate(req *HttpRequest) {
	ticket := t.Client.GetTicket(t.DestinationID)
	req.SetHeader("X-Ya-Tvm-Ticket", ticket)
}

// MultiFactorAuth combines multiple auth methods
type MultiFactorAuth struct {
	methods []AuthMethod
}

func (m *MultiFactorAuth) Authenticate(req *HttpRequest) {
	for _, method := range m.methods {
		method.Authenticate(req)
	}
}

func main() {
	// OAuth authentication
	req1 := &HttpRequest{URL: "https://api.example.com", Headers: make(map[string]string)}
	oauth := &OAuthToken{Token: "ya29..."}
	oauth.Authenticate(req1)
	
	// TVM authentication
	tvmClient := &TvmClient{tickets: make(map[int]string)}
	tvm := &TvmAuth{Client: tvmClient, DestinationID: 12345}
	req2 := &HttpRequest{URL: "https://internal.example.com", Headers: make(map[string]string)}
	tvm.Authenticate(req2)
	
	// Multi-factor authentication
	req3 := &HttpRequest{URL: "https://secure.example.com", Headers: make(map[string]string)}
	mfa := &MultiFactorAuth{methods: []AuthMethod{oauth, tvm}}
	mfa.Authenticate(req3)
	
	fmt.Printf("OAuth: %v\n", req1.Headers)
	fmt.Printf("TVM: %v\n", req2.Headers)
	fmt.Printf("MFA: %v\n", req3.Headers)
}

Output:

OAuth: map[Authorization:OAuth ya29...]
TVM: map[X-Ya-Tvm-Ticket:tvm_ticket_12345]
MFA: map[Authorization:OAuth ya29... X-Ya-Tvm-Ticket:tvm_ticket_12345]

When to Use Multi-Factor Authentication

Good fit:

  • High-security required interfaces
  • Services requiring different security levels
  • Audit tracing needed

Poor fit:

  • Public APIs
  • Performance-sensitive scenarios
  • Simple internal services

Summary

design in industrial authentication systems is full of trade-offs:

  • Interface abstraction vs. direct implementation: flexibility vs. performance
  • OAuth vs. TVM: simplicity vs. security
  • Single-factor vs. multi-factor: convenience vs. security

In Go, we can implement similar designs more concisely, but the core trade-offs remain the same — there's no absolutely secure solution, only choices that fit the scenario.