# How Authentication Secures Zakirullin Files API Endpoints: Token-Based Implementation

> Learn how Zakirullin Files API endpoints use secure stateless token-based authentication with SHA-256 hashing and server-side salts for robust data protection.

- Repository: [Artem Zakirullin/files.md](https://github.com/zakirullin/files.md)
- Tags: deep-dive
- Published: 2026-05-21

---

**The Zakirullin Files API protects all sync endpoints using a stateless token-based authentication scheme where permanent tokens are hashed with SHA-256 and a server-side salt, requiring clients to present valid credentials via HTTP-only cookies or Authorization headers.**

The Zakirullin Files project implements a robust security layer for its synchronization API. Understanding how authentication secures Zakirullin Files API endpoints reveals a design that prioritizes token confidentiality and stateless validation. This guide examines the token issuance flow, storage mechanisms, and request validation logic implemented in the [`zakirullin/files.md`](https://github.com/zakirullin/files.md/blob/main/zakirullin/files.md) repository.

## Token-Based Authentication Architecture

The service secures sensitive endpoints like `/syncFile` and `/syncFilenames` through a **permanent token system** managed by the `tokenMiddleware`. Unlike session-based approaches, this implementation generates cryptographically random tokens that identify users without maintaining server-side session state. Each token undergoes salting and hashing before persistence, ensuring that even disk-level compromises do not expose valid credentials.

## How Tokens Are Issued and Stored

### One-Time Token Exchange

The authentication flow begins at the `/token` endpoint, handled by the `IssueToken` function in [`server/sync/tokens.go`](https://github.com/zakirullin/files.md/blob/main/server/sync/tokens.go). Clients initiate the process by POSTing a JSON payload containing a one-time token. Upon validation, the server invokes `genToken()` to create a new permanent credential, immediately hashing it via `hashToken` before storage.

```go
// Token issuance handler (server/sync/tokens.go)
func IssueToken(w http.ResponseWriter, r *http.Request) {
    // expects JSON: {"oneTimeToken":"<otp>"}
    permanentToken, ok := issueNewPermanentToken(r)
    if !ok {
        http.Error(w, "Invalid or expired token", http.StatusUnauthorized)
        return
    }
    // set the token as a secure cookie
    setAuthCookie(w, permanentToken)

    // also return it in the JSON body
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]string{"token": permanentToken})
}

```

### Secure Hash Storage

Permanent tokens are never stored in plaintext. The `hashToken` function applies **SHA-256 hashing** combined with `config.ServerCfg.TokensSalt` before writing to disk via `tokens.Write`. This salted-hash approach ensures that token verification occurs through constant-time comparison of hashed values, mitigating rainbow table attacks.

## How Clients Authenticate API Requests

### Cookie-Based Transmission

The primary authentication method uses an **HTTP-only, Secure, SameSite=None cookie** named `token`. The `setAuthCookie` function configures these flags during the `/token` exchange, preventing JavaScript access and ensuring transmission only over HTTPS connections. This mechanism protects against XSS attacks while allowing cross-origin synchronization requests.

### Header-Based Fallback

When cookies are unavailable, clients may present the token in the **Authorization request header**. The `tokenMiddleware` checks for this header as a secondary validation method. Notably, when authentication succeeds via header, the middleware automatically migrates the token to a cookie for subsequent requests, optimizing future validation performance.

```go
// Middleware protecting API endpoints (server/sync/tokens.go)
func tokenMiddleware(next http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // 1️⃣ Try the cookie first
        var token string
        fromCookie := false
        if c, err := r.Cookie(AuthCookieName); err == nil && c.Value != "" {
            token = c.Value
            fromCookie = true
        }
        // 2️⃣ Fallback to Authorization header
        if token == "" {
            token = r.Header.Get("Authorization")
        }

        // 3️⃣ Validate the token
        userID, ok := findUserID(token)
        if !ok {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }

        // 4️⃣ Migrate header‑sent tokens to a cookie
        if !fromCookie {
            setAuthCookie(w, token)
        }

        // 5️⃣ Pass the authenticated user ID downstream
        ctx := context.WithValue(r.Context(), "userID", userID)
        next(w, r.WithContext(ctx))
    }
}

```

## The Token Middleware Validation Flow

Every protected route registers with `tokenMiddleware` wrapping the handler, as seen in the route registration logic (typically located in [`server/sync/webserver.go`](https://github.com/zakirullin/files.md/blob/main/server/sync/webserver.go) or similar). The middleware extracts credentials, validates them against disk-stored hashes via `findUserID` and `tokens.Read`, and injects the `userID` into the request context. Invalid or missing tokens trigger a **401 Unauthorized** response and may activate temporary IP blocking mechanisms detailed in [`server/sync/tokens_dbg.go`](https://github.com/zakirullin/files.md/blob/main/server/sync/tokens_dbg.go).

```go
// Example protected endpoint registration
func registerRoutes(r *mux.Router) {
    // All sync endpoints require a valid token
    r.HandleFunc("/syncFile", corsMiddleware(panicMiddleware(tokenMiddleware(gzipMiddleware(SyncFile)))))
    r.HandleFunc("/syncFilenames", corsMiddleware(panicMiddleware(tokenMiddleware(gzipMiddleware(SyncFilenames)))))
    // Token issuance itself is public (POST only)
    r.HandleFunc("/token", corsMiddleware(panicMiddleware(IssueToken)))
}

```

## Security Implementation Details

The authentication system relies on configuration parameters defined in `server/config/*`, specifically `ServerCfg.TokensSalt` and `ServerCfg.TokensDir`. These control the hashing entropy and storage location respectively. The use of `genToken()` ensures cryptographically random token generation, while the middleware's dual-source extraction (cookie优先, header fallback) provides flexibility without compromising security boundaries.

## Summary

- **Token-based scheme**: All API endpoints except `/token` require a valid permanent token validated by `tokenMiddleware`.
- **Secure storage**: Tokens are stored as SHA-256 salted hashes via `hashToken` and `tokens.Write`, never in plaintext.
- **Dual presentation**: Clients authenticate using HTTP-only Secure cookies or the Authorization header, with automatic migration to cookies.
- **Context injection**: Successful validation injects `userID` into the request context for downstream handlers.
- **Failure handling**: Invalid tokens return 401 status codes and may trigger IP-based rate limiting.

## Frequently Asked Questions

### What type of authentication does Zakirullin Files use?

The implementation uses **stateless token-based authentication**. Clients obtain permanent tokens by exchanging one-time tokens at the `/token` endpoint, then present these tokens via cookies or headers for all subsequent API calls to endpoints like `/syncFile`.

### How are tokens stored securely on the server?

Tokens are hashed using **SHA-256 with a server-side salt** (`config.ServerCfg.TokensSalt`) before persistence. The `hashToken` function in [`server/sync/tokens.go`](https://github.com/zakirullin/files.md/blob/main/server/sync/tokens.go) processes tokens before `tokens.Write` stores them in the configured `TokensDir`, ensuring that raw token values never exist on disk.

### What happens if a token is invalid or missing?

The `tokenMiddleware` returns a **401 Unauthorized** status code when `findUserID` fails to locate a matching hashed token. Repeated failures may trigger temporary IP blocking mechanisms implemented in the debug instrumentation file ([`server/sync/tokens_dbg.go`](https://github.com/zakirullin/files.md/blob/main/server/sync/tokens_dbg.go)), protecting against brute-force attempts.

### Can clients use headers instead of cookies for authentication?

Yes, while HTTP-only cookies are the preferred method, clients may present tokens in the **Authorization header** as a fallback. The middleware automatically detects header-based authentication and migrates the token to a secure cookie via `setAuthCookie` for subsequent requests, maintaining security while improving user experience.