# How to Set Up OAuth2 Authentication in RomM: Complete Implementation Guide

> Implement OAuth2 authentication in RomM easily. This guide covers password grant, refresh grant, and OIDC support using environment variables for seamless integration.

- Repository: [The RomM Project/romm](https://github.com/rommapp/romm)
- Tags: how-to-guide
- Published: 2026-07-05

---

**RomM implements a full OAuth 2.0 flow with password-grant, refresh-grant, and optional OpenID Connect (OIDC) support through environment variables in [`backend/config.py`](https://github.com/rommapp/romm/blob/main/backend/config.py) and token handlers in [`backend/handler/auth/base_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/auth/base_handler.py).**

Setting up OAuth2 authentication in RomM requires configuring environment variables for token signing and enabling specific endpoints that handle JWT issuance and validation. The implementation supports both traditional password-based authentication and external identity providers via OIDC, with Redis-backed refresh token rotation for enhanced security.

## Prerequisites and Configuration

Before enabling OAuth2 flows, you must set the required environment variables in [`backend/config.py`](https://github.com/rommapp/romm/blob/main/backend/config.py). These values control token lifetimes and cryptographic signing:

- `ROMM_AUTH_SECRET_KEY` – The HMAC-SHA256 key used to sign all JWT tokens
- `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS` – Lifetime of short-lived access tokens
- `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` – Lifetime of longer-lived refresh tokens

RomM loads these configurations at startup and uses them across the authentication stack.

## Token Architecture

The core OAuth2 logic resides in [`backend/handler/auth/base_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/auth/base_handler.py) within the `OAuthHandler` class. This implementation uses JWT tokens with specific claims and Redis storage for one-time-use semantics.

### Access Token Generation

The `OAuthHandler.create_access_token` method generates short-lived JWTs containing:
- `type = "access"` claim
- Configurable expiration based on `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS`

### Refresh Token Management

The `OAuthHandler.create_refresh_token` method issues longer-lived tokens with:
- `type = "refresh"` claim
- A unique JTI (JWT ID) stored in Redis for single-use enforcement

When consuming refresh tokens, `OAuthHandler.consume_refresh_token` validates the JTI, removes it from Redis to prevent reuse, and returns the associated user object.

## API Endpoints

All OAuth2 HTTP routes are defined in [`backend/endpoints/auth.py`](https://github.com/rommapp/romm/blob/main/backend/endpoints/auth.py).

### Password Grant Flow

The `POST /token` endpoint supports `grant_type=password`. It validates credentials via `auth_handler.authenticate_user`, verifies requested scopes against `user.oauth_scopes`, and returns both access and refresh tokens.

```bash
curl -X POST "$BASE_URL/api/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password&username=john&password=secret&scope=roms.read roms.write"

```

### Refresh Token Flow

For `grant_type=refresh_token`, the endpoint calls `consume_refresh_token`, invalidates the old refresh token JTI, and issues a fresh token pair while preserving the original scopes.

```bash
curl -X POST "$BASE_URL/api/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=refresh_token&refresh_token=<REFRESH_TOKEN>"

```

## OpenID Connect Integration

RomM supports OIDC for external authentication providers. When `OIDC_ENABLED` is set, the following endpoints become active:

- `GET /login/openid` – Initiates the authentication flow
- `GET /oauth/openid` – Handles the provider callback, validates tokens via `OpenIDHandler`, and provisions users

The callback endpoint validates the external token, creates a session, and redirects to the UI with RomM tokens.

## Device and Session Management

Upon successful authentication (password or OIDC), RomM automatically manages device sessions through `utils.auth.create_or_find_web_device`. This function creates or reuses browser device records and stores the device ID in the session, enabling multi-device tracking and session management.

## Security Implementation

The OAuth2 implementation in RomM includes several security controls:

**Token Signing**: All tokens are signed using `ROMM_AUTH_SECRET_KEY` loaded as a `jose` `OctKey` in [`base_handler.py`](https://github.com/rommapp/romm/blob/main/base_handler.py).

**One-Time Use Refresh Tokens**: The JTI values for refresh tokens are stored in Redis and immediately deleted upon consumption in `consume_refresh_token`, preventing replay attacks.

**Scope Validation**: The `token` endpoint verifies that requested scopes are a subset of the user's granted scopes (`User.oauth_scopes`) before issuing tokens.

**Bearer Token Validation**: The `get_current_active_user_from_bearer_token` method validates the `iss = "romm:oauth"` claim and token signature before returning the user.

## Summary

- Configure OAuth2 in RomM by setting `ROMM_AUTH_SECRET_KEY`, `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS`, and `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS` environment variables
- Access tokens are short-lived JWTs with `type="access"` claims generated in [`backend/handler/auth/base_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/auth/base_handler.py)
- Refresh tokens use Redis-backed JTI storage for one-time-use semantics via `consume_refresh_token`
- Use `POST /token` with `grant_type=password` for initial authentication or `grant_type=refresh_token` for token rotation
- Enable OIDC by setting `OIDC_ENABLED` and using the `/login/openid` and `/oauth/openid` endpoints
- All authenticated requests require an `Authorization: Bearer <access_token>` header validated against the `iss = "romm:oauth"` claim

## Frequently Asked Questions

### How do I configure the OAuth2 secret key in RomM?

Set the `ROMM_AUTH_SECRET_KEY` environment variable before starting the application. This key is used as the HMAC-SHA256 signing key for all JWT tokens and is loaded as a `jose` `OctKey` in [`backend/handler/auth/base_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/auth/base_handler.py). Without this configuration, token generation and validation will fail.

### What is the difference between access tokens and refresh tokens in RomM?

Access tokens are short-lived JWTs containing a `type="access"` claim that authorize API requests via the `Authorization: Bearer` header. Refresh tokens are longer-lived tokens with `type="refresh"` that include a unique JTI stored in Redis; they can only be used once to obtain new token pairs through the `POST /token` endpoint with `grant_type=refresh_token`.

### How does RomM handle OpenID Connect authentication?

RomM implements OIDC through the `GET /login/openid` and `GET /oauth/openid` endpoints in [`backend/endpoints/auth.py`](https://github.com/rommapp/romm/blob/main/backend/endpoints/auth.py). When enabled via `OIDC_ENABLED`, the login endpoint redirects to the external provider, and the callback endpoint validates the external token using `OpenIDHandler`, provisions the user if necessary, and establishes a session with device tracking via `create_or_find_web_device`.

### Where are refresh tokens stored and how are they secured?

Refresh token JTI values are stored in Redis with the implementation enforcing one-time-use semantics. When `consume_refresh_token` processes a refresh request, it immediately removes the JTI from Redis, ensuring the token cannot be replayed. This prevents token theft and reuse attacks in the RomM authentication flow.