---
title: Authentication
sidebar_position: 5
description: JWT authentication flow in Streamlit Admin. How tokens are injected, how to access user info, and how to protect custom pages.
---

# Authentication

Streamlit Admin uses JWT tokens generated by Django and passed via URL query parameters. The token is verified inside Streamlit using the same secret (or `SECRET_KEY`).

---

## How It Works

```
User visits Django admin
       │
       ▼
Django generates JWT token (signed with SECRET_KEY)
       │
       ▼
Django renders iframe src="http://localhost:8501/?token=<JWT>"
       │
       ▼
Streamlit reads token from query params
       │
       ▼
require_auth() validates token → returns User object
       │
       ▼
Page renders with authenticated user
```

The token is **short-lived** and generated fresh on each Django admin page load. Streamlit validates it locally — no round-trip to Django.

---

## `require_auth()`

Call this at the top of every page (or in `app.py`) to enforce authentication:

```python
from api.auth import require_auth

user = require_auth()

# user is now available
st.write(f"Hello, {user.username}")
```

If the token is missing or invalid, `require_auth()` stops execution and shows a login prompt.

### User Object Fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | `int` | Django user ID |
| `username` | `str` | Username |
| `email` | `str` | Email address |
| `first_name` | `str` | First name |
| `last_name` | `str` | Last name |
| `is_staff` | `bool` | Is Django staff |
| `is_superuser` | `bool` | Is Django superuser |

---

## Usage in `app.py`

The scaffold places `require_auth()` at app startup before any page renders:

```python
# streamlit/app.py
import streamlit as st
from api.auth import require_auth, logout

# Authenticate before anything else
user = require_auth()

# Now render the rest of the app
st.sidebar.write(f"👤 {user.username}")

if st.sidebar.button("Logout"):
    logout()
    st.rerun()
```

---

## Per-Page Auth Check

You can also check auth at the page level if you want different behavior per page:

```python
from api.auth import require_auth

@page_registry.register("Admin Only", icon="shield-lock", group="System")
def render_admin_only():
    user = require_auth()

    if not user.is_superuser:
        st.error("Superuser access required.")
        st.stop()

    st.title("Admin Only Page")
    # ...
```

---

## Logout

```python
from api.auth import logout

logout()
st.rerun()  # Redirect to login
```

---

## JWT Secret

By default, the JWT is signed with Django's `SECRET_KEY`. Override in config:

```python
StreamlitAdminConfig(
    app_path="streamlit",
    jwt_secret="your-custom-secret",  # keep in sync between Django and Streamlit
)
```

The Streamlit-side `api/auth.py` reads the secret from its own `api/config.py`, which gets it from the environment or config file generated by `create_streamlit_app`.

---

## Token Injection (Django side)

Django injects the token via `StreamlitProxyView` and template tags:

```django
{% load streamlit_admin %}

{# URL with token injected automatically #}
{% streamlit_admin_iframe_url %}

{# Rendered iframe with token #}
<iframe src="{% streamlit_admin_iframe_url %}" ...></iframe>
```

In `DEBUG` mode, the view redirects to Streamlit directly. In production (with `public_url` set), it redirects to the `public_url` with the token appended.
