> ## Documentation Index
> Fetch the complete documentation index at: https://docs.go-mizu.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Fingerprint

> Request fingerprinting middleware for client identification.

## Overview

The `fingerprint` middleware generates unique fingerprints for incoming requests based on headers, IP addresses, and other request attributes. Useful for bot detection, analytics, and rate limiting.

## Installation

```go theme={null}
import "github.com/go-mizu/mizu/middlewares/fingerprint"
```

## Quick Start

```go theme={null}
app := mizu.New()
app.Use(fingerprint.New())

app.Get("/", func(c *mizu.Ctx) error {
    hash := fingerprint.Hash(c)
    return c.JSON(200, map[string]string{"fingerprint": hash})
})
```

## Configuration

| Option          | Type                                | Default        | Description          |
| --------------- | ----------------------------------- | -------------- | -------------------- |
| `Headers`       | `[]string`                          | Common headers | Headers to include   |
| `IncludeIP`     | `bool`                              | `true`         | Include client IP    |
| `IncludeMethod` | `bool`                              | `false`        | Include HTTP method  |
| `IncludePath`   | `bool`                              | `false`        | Include request path |
| `Custom`        | `func(*mizu.Ctx) map[string]string` | -              | Custom components    |

## Default Headers

```go theme={null}
[]string{
    "User-Agent",
    "Accept",
    "Accept-Language",
    "Accept-Encoding",
    "Connection",
    "Sec-Ch-Ua",
    "Sec-Ch-Ua-Mobile",
    "Sec-Ch-Ua-Platform",
}
```

## Examples

### Basic Usage

```go theme={null}
app.Use(fingerprint.New())

app.Get("/", func(c *mizu.Ctx) error {
    info := fingerprint.Get(c)
    return c.JSON(200, map[string]any{
        "hash":       info.Hash,
        "components": info.Components,
    })
})
```

### With IP Address

```go theme={null}
app.Use(fingerprint.WithIP())

// Fingerprint includes client IP
```

### Headers Only

```go theme={null}
app.Use(fingerprint.HeadersOnly(
    "User-Agent",
    "Accept-Language",
))
```

### Full Fingerprint

```go theme={null}
app.Use(fingerprint.Full())
// Includes IP, method, path, and all default headers
```

### Custom Headers

```go theme={null}
app.Use(fingerprint.WithOptions(fingerprint.Options{
    Headers: []string{
        "User-Agent",
        "Accept",
        "Accept-Language",
        "Accept-Encoding",
        "X-Custom-Header",
    },
}))
```

### Include Request Path

```go theme={null}
app.Use(fingerprint.WithOptions(fingerprint.Options{
    IncludeIP:     true,
    IncludeMethod: true,
    IncludePath:   true,
}))
```

### Custom Components

```go theme={null}
app.Use(fingerprint.WithOptions(fingerprint.Options{
    Custom: func(c *mizu.Ctx) map[string]string {
        return map[string]string{
            "session_id": c.Cookie("session_id"),
            "user_id":    getUserID(c),
        }
    },
}))
```

### Rate Limiting by Fingerprint

```go theme={null}
app.Use(fingerprint.New())

var requestCounts = make(map[string]int)
var mu sync.Mutex

app.Use(func(next mizu.Handler) mizu.Handler {
    return func(c *mizu.Ctx) error {
        hash := fingerprint.Hash(c)

        mu.Lock()
        requestCounts[hash]++
        count := requestCounts[hash]
        mu.Unlock()

        if count > 100 {
            return c.Text(429, "Rate limit exceeded")
        }

        return next(c)
    }
})
```

### Bot Detection

```go theme={null}
app.Use(fingerprint.New())

// Known bot fingerprints
var knownBots = map[string]bool{
    "abc123...": true,
    "def456...": true,
}

app.Use(func(next mizu.Handler) mizu.Handler {
    return func(c *mizu.Ctx) error {
        hash := fingerprint.Hash(c)

        if knownBots[hash] {
            return c.Text(403, "Bot detected")
        }

        return next(c)
    }
})
```

### Analytics Tracking

```go theme={null}
app.Use(fingerprint.New())

app.Use(func(next mizu.Handler) mizu.Handler {
    return func(c *mizu.Ctx) error {
        info := fingerprint.Get(c)

        // Track unique visitors
        analytics.TrackVisitor(info.Hash, map[string]any{
            "path":       c.Request().URL.Path,
            "components": info.Components,
        })

        return next(c)
    }
})
```

### Session Correlation

```go theme={null}
app.Use(fingerprint.WithOptions(fingerprint.Options{
    IncludeIP: true,
    Custom: func(c *mizu.Ctx) map[string]string {
        return map[string]string{
            "session": c.Cookie("session_id"),
        }
    },
}))

app.Get("/", func(c *mizu.Ctx) error {
    hash := fingerprint.Hash(c)

    // Correlate with existing sessions
    existingSession := findSession(hash)
    if existingSession != nil {
        return c.JSON(200, existingSession)
    }

    // Create new session
    return c.JSON(200, createSession(hash))
})
```

### Debugging Fingerprint

```go theme={null}
app.Get("/debug/fingerprint", func(c *mizu.Ctx) error {
    info := fingerprint.Get(c)

    return c.JSON(200, map[string]any{
        "hash":       info.Hash,
        "components": info.Components,
    })
})
```

Response:

```json theme={null}
{
    "hash": "a1b2c3d4e5f6...",
    "components": {
        "User-Agent": "Mozilla/5.0...",
        "Accept": "text/html,application/xhtml+xml...",
        "Accept-Language": "en-US,en;q=0.9",
        "Accept-Encoding": "gzip, deflate, br",
        "IP": "192.168.1.1"
    }
}
```

### Fraud Detection

```go theme={null}
app.Use(fingerprint.Full())

app.Post("/checkout", func(c *mizu.Ctx) error {
    hash := fingerprint.Hash(c)
    userID := c.Get("user_id").(string)

    // Check if fingerprint is associated with fraud
    if isFraudulent(hash) {
        return c.JSON(403, map[string]string{
            "error": "Transaction blocked",
        })
    }

    // Check for fingerprint/account mismatch
    if !matchesUser(hash, userID) {
        flagForReview(userID, hash)
    }

    return processCheckout(c)
})
```

## Info Structure

```go theme={null}
type Info struct {
    Hash       string            // SHA256 hash of components
    Components map[string]string // Individual fingerprint components
}
```

## API Reference

```go theme={null}
func New() mizu.Middleware
func WithOptions(opts Options) mizu.Middleware
func HeadersOnly(headers ...string) mizu.Middleware
func WithIP() mizu.Middleware
func Full() mizu.Middleware
func Get(c *mizu.Ctx) *Info
func Hash(c *mizu.Ctx) string
```

## Hash Algorithm

The fingerprint hash is generated by:

1. Collecting all components (headers, IP, etc.)
2. Sorting component keys alphabetically
3. Concatenating as `key:value|key:value|...`
4. Computing SHA256 hash
5. Returning hex-encoded string

## Technical Details

### Implementation Architecture

The fingerprint middleware uses a context-based storage mechanism to preserve fingerprint information throughout the request lifecycle. The implementation follows these key design principles:

**Context Storage**

* Uses a private `contextKey` struct type to avoid collisions with other middleware
* Stores fingerprint information in the request context as an `*Info` pointer
* Information persists across the entire middleware chain

**Hash Generation Algorithm**

* Deterministic hashing through alphabetical key sorting
* Uses SHA256 cryptographic hash function
* Produces 64-character hexadecimal strings
* Format: `key1:value1|key2:value2|...|keyN:valueN`

**Component Collection**
The middleware collects fingerprint components in the following order:

1. Headers: Iterates through configured header list
2. IP Address: Extracted via `getClientIP()` helper
3. HTTP Method: Request method if enabled
4. Request Path: URL path if enabled
5. Custom Components: User-defined function results

**IP Address Detection**
The `getClientIP()` function implements a priority-based IP extraction:

1. First checks `X-Forwarded-For` header (takes first IP in comma-separated list)
2. Falls back to `X-Real-IP` header
3. Finally uses `RemoteAddr` from the request

### Performance Considerations

* Minimal memory allocation through pre-sized maps
* String builder for efficient concatenation
* Single-pass component collection
* Lazy evaluation: hash only computed once during middleware execution

### Thread Safety

The middleware is thread-safe as each request receives its own context and Info struct. No shared state exists between requests.

## Best Practices

* Use minimal headers for privacy compliance
* Don't rely solely on fingerprints for authentication
* Store fingerprints hashed, not raw
* Combine with other signals for bot detection
* Consider GDPR implications when storing fingerprints
* Use for analytics and fraud detection, not tracking

## Testing

The fingerprint middleware includes comprehensive test coverage for all configuration options and edge cases:

| Test Case                       | Description                       | Expected Behavior                                                          |
| ------------------------------- | --------------------------------- | -------------------------------------------------------------------------- |
| `TestNew`                       | Default middleware initialization | Generates fingerprint hash from default headers (User-Agent, Accept, etc.) |
| `TestWithOptions_IncludeIP`     | IP address inclusion              | Captures client IP from RemoteAddr and includes in components              |
| `TestWithOptions_IncludeMethod` | HTTP method inclusion             | Includes HTTP method (GET, POST, etc.) in fingerprint components           |
| `TestWithOptions_IncludePath`   | Request path inclusion            | Captures and includes the full request path in components                  |
| `TestWithOptions_Custom`        | Custom component function         | Executes custom function and merges returned map into components           |
| `TestHash`                      | Hash function retrieval           | Returns 64-character SHA256 hex string via Hash() helper                   |
| `TestConsistentHash`            | Hash consistency                  | Identical requests produce identical hash values                           |
| `TestHeadersOnly`               | Custom header filtering           | Only includes specified headers, excludes others (e.g., User-Agent)        |
| `TestWithIP`                    | WithIP() helper function          | Enables IP inclusion using convenience function                            |
| `TestFull`                      | Full() comprehensive mode         | Includes IP, Method, Path, and all default headers                         |
| `TestXForwardedFor`             | X-Forwarded-For header parsing    | Extracts first IP from comma-separated list in X-Forwarded-For header      |

### Test Coverage

All test cases validate:

* Correct component extraction and storage
* Proper context propagation
* Hash generation and consistency
* Configuration option handling
* Edge cases (empty headers, missing values, etc.)

## Related Middlewares

* [realip](/middlewares/realip) - Client IP extraction
* [ratelimit](/middlewares/ratelimit) - Rate limiting
* [ipfilter](/middlewares/ipfilter) - IP filtering
