# How Authentication and Password Hashing Are Configured in Frigate

> Learn how Frigate configures authentication and password hashing using PBKDF2-SHA256 with JWT sessions and role based access control. Secure your setup with declarative settings.

- Repository: [Blake Blackshear/frigate](https://github.com/blakeblackshear/frigate)
- Tags: how-to-guide
- Published: 2026-05-25

---

**Frigate configures authentication using JWT-based sessions with PBKDF2-SHA256 password hashing (600,000 iterations by default), role-based access control, and declarative settings in `AuthConfig`.**

Frigate is an open-source Network Video Recorder (NVR) that protects its web interface and API with native authentication mechanisms. Understanding how authentication and password hashing are configured in Frigate helps administrators secure video streams and user credentials effectively. The implementation is distributed across configuration models, API utilities, and database layers within the `blakeblackshear/frigate` repository.

## AuthConfig Declarative Settings

All authentication behavior is governed by the **`AuthConfig`** class defined in [[`frigate/config/auth.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/config/auth.py)](https://github.com/blakeblackshear/frigate/blob/dev/frigate/config/auth.py#L10-L68). This Pydantic model validates settings at startup and exposes the following key fields:

- **`enabled`** – Globally toggles authentication on or off.
- **`hash_iterations`** – Configurable work factor for PBKDF2 (default `600000`, aligning with OWASP recommendations).
- **`roles`** – Dictionary mapping role names (e.g., `admin`, `viewer`) to lists of authorized cameras; an empty list grants access to all cameras.
- **`session_length`** – JWT token lifetime in seconds (default `86400`).
- **`refresh_time`** – Window before expiry to refresh tokens (default `1800`).
- **`cookie_secure`** – Sets the `Secure` flag on JWT cookies; must be `True` when serving over TLS.
- **`cookie_name`** – Configurable name for the session cookie (default `frigate_token`).

These values are sourced from your main [`config.yml`](https://github.com/blakeblackshear/frigate/blob/main/config.yml) and are immutable until the next restart.

## PBKDF2-SHA256 Password Hashing

Password hashing and verification are implemented in [[`frigate/api/auth.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/api/auth.py)](https://github.com/blakeblackshear/frigate/blob/dev/frigate/api/auth.py#L64-L84) using Python’s standard library. Frigate uses **PBKDF2-HMAC-SHA256** with a configurable iteration count and a random 16-byte salt.

### Hash Generation

The `hash_password` function generates strings in the format `pbkdf2_sha256$<iterations>$<salt>$<base64_hash>`:

```python
def hash_password(password: str, salt=None, iterations=600000):
    if salt is None:
        salt = secrets.token_hex(16)  # 16-byte random salt

    pw_hash = hashlib.pbkdf2_hmac(
        "sha256",
        password.encode("utf-8"),
        salt.encode("utf-8"),
        iterations
    )
    b64_hash = base64.b64encode(pw_hash).decode("ascii").strip()
    return f"{PASSWORD_HASH_ALGORITHM}${iterations}${salt}${b64_hash}"

```

- **`PASSWORD_HASH_ALGORITHM`** is the constant `pbkdf2_sha256`.
- **`iterations`** defaults to `600000` but is overridden by `AuthConfig.hash_iterations` when creating users.
- **`salt`** is generated via `secrets.token_hex(16)` to ensure cryptographic randomness.

### Hash Verification

The `verify_password` function prevents timing attacks by using `secrets.compare_digest` and validates the stored hash structure before computation:

```python
def verify_password(password, password_hash):
    if (password_hash or "").count("$") != 3:
        return False
    algorithm, iterations, salt, b64_hash = password_hash.split("$", 3)
    iterations = int(iterations)
    assert algorithm == PASSWORD_HASH_ALGORITHM
    compare_hash = hash_password(password, salt, iterations)
    return secrets.compare_digest(password_hash, compare_hash)

```

This ensures constant-time comparison and rejects malformed hashes immediately.

## JWT Session and Secret Management

Frigate issues JSON Web Tokens (JWT) upon successful login. The signing secret is retrieved by `get_jwt_secret()` in [[`frigate/api/auth.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/api/auth.py)](https://github.com/blakeblackshear/frigate/blob/dev/frigate/api/auth.py#L30-L61) using the following precedence:

1. **`FRIGATE_JWT_SECRET`** environment variable.
2. Docker secrets (mounted files).
3. Home Assistant add-on context.
4. A persistent file named `.jwt_secret` in the configuration directory (auto-generated on first run).

The `create_encoded_jwt` function generates tokens that are stored in HTTP-only cookies. The cookie name and security attributes are controlled by `AuthConfig.cookie_name` and `AuthConfig.cookie_secure`.

## Role-Based Access Control

Frigate enforces coarse-grained authorization through the **`require_role`** dependency. Roles are resolved from the `remote-role` header (or proxy-mapped headers) and compared against the mappings defined in `AuthConfig.roles`. 

The `resolve_role` helper translates proxy-provided claims into Frigate role definitions. Endpoints declare required roles via FastAPI dependencies; if the user’s role is absent from the required list, the request is rejected with a 403 error.

## User Model and Lifecycle

Persistent user data is defined in [[`frigate/models.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/models.py)](https://github.com/blakeblackshear/frigate/blob/dev/frigate/models.py#L44-L51) by the **`User`** SQLAlchemy model:

```python
class User(Model):
    username = CharField(unique=True)
    role = CharField()
    password_hash = CharField()

```

When the application starts, [[`frigate/app.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/app.py)](https://github.com/blakeblackshear/frigate/blob/dev/frigate/app.py#L520-L560) checks for the existence of an admin user or the `reset_admin_password` flag. If a new user is created or the admin password is reset, the backend invokes `hash_password` with `iterations=self.config.auth.hash_iterations` and commits the resulting string to the `password_hash` column.

## Summary

- **Configuration:** `AuthConfig` in [`frigate/config/auth.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/config/auth.py) centralizes hash iterations, session timing, and cookie settings.
- **Hashing:** PBKDF2-SHA256 with 600,000 default iterations produces hashes in `algorithm$iterations$salt$base64_hash` format.
- **Security:** Constant-time verification via `secrets.compare_digest` mitigates timing attacks.
- **Sessions:** JWT tokens are signed with secrets from environment variables or filesystem storage and transmitted via configurable cookies.
- **Storage:** User credentials reside in the `User` model ([`frigate/models.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/models.py)), populated during startup logic in [`frigate/app.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/app.py).

## Frequently Asked Questions

### What hashing algorithm does Frigate use for passwords?

Frigate uses **PBKDF2-HMAC-SHA256** with a default of 600,000 iterations, which aligns with current OWASP guidelines. The iteration count is configurable via the `hash_iterations` field in `AuthConfig` ([`frigate/config/auth.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/config/auth.py)).

### How does Frigate store and retrieve the JWT signing secret?

Frigate attempts to load the JWT secret from the `FRIGATE_JWT_SECRET` environment variable first. If undefined, it checks Docker secrets, Home Assistant add-on contexts, or generates a random secret and persists it to a `.jwt_secret` file in the configuration directory (implemented in [`frigate/api/auth.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/api/auth.py)).

### Can I enforce secure cookies and customize session timeouts?

Yes. Set `cookie_secure: True` in your [`config.yml`](https://github.com/blakeblackshear/frigate/blob/main/config.yml) to enforce the `Secure` flag on cookies (required for HTTPS deployments). Adjust `session_length` (default 86400 seconds) and `refresh_time` (default 1800 seconds) within `AuthConfig` to control token lifetimes.

### Where are user passwords stored in the database?

User credentials are stored in the `User` table defined in [`frigate/models.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/models.py), specifically within the `password_hash` column. This column stores the complete PBKDF2 string including algorithm, iterations, salt, and hash, which is generated during user creation in [`frigate/app.py`](https://github.com/blakeblackshear/frigate/blob/main/frigate/app.py).