Back to Home

Dynamic routes net/http: DeadDrop implementation

The article analyzes the implementation of dynamic routes and cookie authentication in pure net/http for the DeadDrop service. Manual path parsing, HMAC tokens, use case architecture. Full cycle with tests.

net/http: dynamic routes and cookie auth in DeadDrop
Advertisement 728x90

Dynamic Routes and Cookie Authentication in net/http: Implementing DeadDrop

Continuing our series on pure net/http, we implement dynamic routing for the DeadDrop service. Users will be able to view secrets via links like /secret/{id}. The standard mux doesn't support path parameters until Go 1.22, so we write our own parser.

The handler is registered at /secret/ with a trailing slash—this captures all child paths. We extract the ID manually from r.URL.Path.

func extractIDFromPath(path string) (string, bool) {
    const prefix = "/secret/"

    if !strings.HasPrefix(path, prefix) {
        return "", false
    }

    id := strings.TrimPrefix(path, prefix)
    if id == "" || strings.Contains(id, "/") {
        return "", false
    }

    return id, true
}

The function checks the prefix, trims it, and excludes invalid IDs (empty or containing /). This provides basic protection against malformed requests.

Google AdInline article slot

SecretHandler with DI

We create a handler struct for dependency injection:

type SecretHandler struct {
    // placeholder for DI
}

func NewSecretHandler() *SecretHandler {
    return &SecretHandler{}
}

func (h *SecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    id, ok := url.ExtractIDFromPath(r.URL.Path)
    if !ok {
        http.NotFound(w, r)
        return
    }
    w.Header().Set("Content-Type", "text/plain; charset=utf-8")
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("Requested secret with ID: " + id))
}

Register it in router.go: mux.Handle("/secret/", secretHandler). Test edge cases:

  • /secret/12345/ → 404
  • /secret/ → 404
  • /secret/axQ/233 → 404
  • /secret/ad31H234 → OK with ID

Use Case for Retrieving a Secret

Define the request and response:

Google AdInline article slot
type GetSecretRequest struct {
    ID       string
    Password string
}

type GetSecretResponse struct {
    ID        string
    Message   string
    FileName  string
    FileExt   string
    HasFile   bool
    ExpiresAt time.Time
}

Use case logic:

  • Retrieve the secret from storage
  • Check expiration (time.After()), delete expired ones
  • Verify password via CheckPasswordHash
  • Return data or an error

Cookie Authentication

To protect file downloads, we implement authentication via cookies. After successful password verification, set a session cookie with an encrypted access token.

  • Advantages: stateless, scalable, no session storage
  • Cookie parameters: HttpOnly, Secure, SameSite=Strict

When requesting /download/{id}:

Google AdInline article slot
  • Extract ID from the path
  • Read the auth_token cookie
  • Decode and verify the token (ID + password hash + nonce)
  • If valid—serve the file

Access Token Structure

The token is formed as base64(ID + ":" + hash + ":" + nonce), where:

  • hash = HMAC-SHA256(ID + password, secretKey)
  • nonce—a one-time number to protect against replay attacks

Example validation:

func validateDownloadToken(id, token string) bool {
    // decode token
    // extract hash and nonce
    // recalculate HMAC
    // compare
    return true // or false
}

Full Secret Workflow

  • Creation: POST /create → generate ID, hash password, save
  • Viewing: GET /secret/{id} → password input form
  • Authentication: POST password → cookie + redirect
  • Download: GET /download/{id} → check cookie → file
  • Cleanup: TTL expired → deletion

UI prototype: page with password field, "Open" button. After success—content + file link.

Routing Testing

| Test Path | Expectation | Result |

|-----------|-------------|--------|

| /secret/123/ | 404 | 404 |

| /secret/ | 404 | 404 |

| /secret/id/with/slash | 404 | 404 |

| /secret/valid123 | OK: "ID: valid123" | OK |

Edge cases cover parser security.

Key Points

  • Dynamic routes: manual parsing of r.URL.Path without third-party routers
  • Security: ID validation, slash checks, TTL for secrets
  • Authentication: cookie with HMAC token instead of sessions
  • Architecture: use case + DI, clean logic outside handlers
  • Scalability: stateless, ready for clustering

— Editorial Team

Advertisement 728x90

Read Next