# How to Set Up OIDC Authentication in RomM: A Complete Guide

> Set up OIDC authentication in RomM easily. Follow our complete guide to configure your OpenID Connect provider and enable secure login for your RomM instance.

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

---

**Enable OIDC in RomM by setting `OIDC_ENABLED=true` and configuring your provider credentials in environment variables, then restart the services to activate the OpenID Connect login button.**

RomM supports OpenID Connect (OIDC) as a first-class authentication method for seamless single sign-on (SSO) integration. When properly configured, the backend validates tokens from your identity provider using the `OpenIDHandler` class in [`backend/handler/auth/base_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/auth/base_handler.py), automatically provisions users, and assigns roles based on OIDC claims. This guide explains how to set up OIDC authentication in RomM using environment variables and the underlying implementation.

## How the OIDC Flow Works in RomM

The authentication process involves coordinated steps between the Vue frontend and Python backend. When a user clicks the OIDC login button in [`frontend/src/v2/components/Auth/OIDCButton.vue`](https://github.com/rommapp/romm/blob/main/frontend/src/v2/components/Auth/OIDCButton.vue), the application redirects to the provider's authorization endpoint with the configured client ID, scopes, and `OIDC_REDIRECT_URI`.

After the provider authenticates the user and redirects back to RomM, the backend exchanges the authorization code for tokens. The `OpenIDHandler.get_current_active_user_from_openid_token` method in [`backend/handler/auth/base_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/auth/base_handler.py) validates the ID token, checks that the email is verified, and extracts the username using the `OIDC_USERNAME_ATTRIBUTE` claim (defaulting to `preferred_username`).

If the email does not exist in the database and `OIDC_ALLOW_REGISTRATION` is `true`, RomM creates a new user record with a randomly generated password. The handler also maps OIDC role claims to RomM roles (**ADMIN** or **USER**) based on your `OIDC_CLAIM_ROLES` configuration. Finally, the backend creates a signed session cookie using `ROMM_AUTH_SECRET_KEY` that the frontend consumes via the `/api/me` endpoint.

## Required Environment Variables

All OIDC configuration is loaded from environment variables via the config manager. Add these entries to your `.env` file or Docker Compose environment:

- **`OIDC_ENABLED`**: Set to `true` to activate the OIDC subsystem
- **`OIDC_PROVIDER`**: Human-readable name displayed in the UI (e.g., "Keycloak")
- **`OIDC_CLIENT_ID`**: Your OIDC client identifier from the provider
- **`OIDC_CLIENT_SECRET`**: The client secret associated with your OIDC client
- **`OIDC_REDIRECT_URI`**: Full callback URL (e.g., `https://my-romm.tld/auth/openid/callback`)
- **`OIDC_USERNAME_ATTRIBUTE`**: Claim field containing the username (default: `preferred_username`)
- **`OIDC_ALLOW_REGISTRATION`**: Set to `true` to auto-create users for unknown email addresses
- **`OIDC_CLAIM_ROLES`**: Optional claim name containing user role strings
- **`OIDC_ROLE_ADMIN`**: Value in the role claim that grants **ADMIN** rights
- **`OIDC_ROLE_EDITOR`** and **`OIDC_ROLE_VIEWER`**: Legacy values that map to the **USER** role
- **`ROMM_AUTH_SECRET_KEY`**: Required secret for signing JWTs and session cookies (already required for standard auth)

These variables are imported at the top of [`backend/handler/auth/base_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/auth/base_handler.py) (lines 15-23) and control the behavior of the `OpenIDHandler` class.

## Step-by-Step Setup Guide

1. **Create an OIDC client** in your Identity Provider (Keycloak, Auth0, Google Workspace, Azure AD, etc.) and set the redirect URI to match your `OIDC_REDIRECT_URI` value exactly.

2. **Configure your RomM `.env` file** with the OIDC variables listed above.

3. **Restart the RomM container** to load the new environment variables.

4. **Verify the login screen** displays the "Login with [Provider]" button, which is conditionally rendered in [`frontend/src/v2/views/Auth/Login.vue`](https://github.com/rommapp/romm/blob/main/frontend/src/v2/views/Auth/Login.vue) when OIDC is enabled.

5. **Test the authentication flow** by clicking the OIDC button, authenticating with your provider, and verifying you are redirected back to RomM as the authenticated user.

6. **Optional: Configure logout redirect** by ensuring your provider supplies an `oidc_logout_url` claim in the token, which RomM stores in the `OIDCLogoutResponse` model and uses when users click logout.

## Code Implementation Details

### Backend Token Validation

The core validation logic resides in [`backend/handler/auth/base_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/auth/base_handler.py). The handler checks if `OIDC_ENABLED` is active, validates the email is verified, extracts the preferred username, and maps roles:

```python

# backend/handler/auth/base_handler.py (excerpt)

class OpenIDHandler:
    async def get_current_active_user_from_openid_token(self, token: Any):
        if not OIDC_ENABLED:
            return None, None

        userinfo = token.get("userinfo")
        if userinfo is None:
            raise HTTPException(status_code=400, detail="Userinfo is missing from token.")

        email = userinfo.get("email")
        if email is None:
            raise HTTPException(status_code=400, detail="Email is missing from token.")

        # Verify email is confirmed

        if not self._email_is_verified(userinfo):
            raise HTTPException(status_code=400, detail="Email is not verified.")

        # Extract username and map roles

        username = userinfo.get(OIDC_USERNAME_ATTRIBUTE)
        role = self._map_roles(userinfo)

        # Create or update user record

        user = db_user_handler.get_user_by_email(email)
        if user is None:
            if not OIDC_ALLOW_REGISTRATION:
                raise HTTPException(status_code=403, detail="Registration disabled.")
            user = db_user_handler.add_user(
                User(username=username, email=email, role=role, hashed_password=str(uuid.uuid4()))
            )
        elif role != user.role:
            user = db_user_handler.update_user(user.id, {"role": role})

        if not user.enabled:
            raise UserDisabledException

        return user, userinfo

```

### Frontend Integration

The login button is implemented in [`frontend/src/v2/components/Auth/OIDCButton.vue`](https://github.com/rommapp/romm/blob/main/frontend/src/v2/components/Auth/OIDCButton.vue), which initiates the flow by calling the authentication store:

```typescript
<!-- frontend/src/v2/components/Auth/OIDCButton.vue -->
<template>
  <v-btn @click="login" :loading="loading">
    {{ $t('login.login-oidc', { oidc: provider || 'OIDC' }) }}
  </v-btn>
</template>

<script setup lang="ts">
import { ref } from 'vue';
import { useAuthStore } from '@/stores/auth';

const provider = import.meta.env.VITE_OIDC_PROVIDER;
const loading = ref(false);
const auth = useAuthStore();

function login() {
  loading.value = true;
  auth.startOidcLogin();
}
</script>

```

### Environment Configuration Example

```dotenv

# Core auth secret (required for all auth methods)

ROMM_AUTH_SECRET_KEY=super-secret-key

# OIDC configuration

OIDC_ENABLED=true
OIDC_PROVIDER=Keycloak
OIDC_CLIENT_ID=romm-client
OIDC_CLIENT_SECRET=xxxxxxxxxxxx
OIDC_REDIRECT_URI=https://my-romm.tld/auth/openid/callback
OIDC_USERNAME_ATTRIBUTE=preferred_username
OIDC_ALLOW_REGISTRATION=true
OIDC_CLAIM_ROLES=roles
OIDC_ROLE_ADMIN=admin
OIDC_ROLE_EDITOR=editor
OIDC_ROLE_VIEWER=viewer

```

## Summary

- RomM supports OIDC through environment variables defined in [`backend/handler/auth/base_handler.py`](https://github.com/rommapp/romm/blob/main/backend/handler/auth/base_handler.py)
- Set `OIDC_ENABLED=true` and configure provider credentials to activate the feature
- The system validates tokens via `OpenIDHandler.get_current_active_user_from_openid_token` and auto-provisions users when `OIDC_ALLOW_REGISTRATION` is enabled
- Roles map from OIDC claims using `OIDC_CLAIM_ROLES` and the specific role environment variables (`OIDC_ROLE_ADMIN`, etc.)
- The frontend button in [`OIDCButton.vue`](https://github.com/rommapp/romm/blob/main/OIDCButton.vue) initiates the flow, while the `OIDCLogoutResponse` model handles optional provider logout redirects

## Frequently Asked Questions

### What identity providers work with RomM OIDC?

Any standards-compliant OpenID Connect provider works, including Keycloak, Auth0, Google Workspace, Azure AD, and Okta. Configure the authorization endpoint and token endpoint in your provider settings, then map the client ID and secret into RomM's environment variables.

### Why does RomM require `ROMM_AUTH_SECRET_KEY` for OIDC?

RomM uses this secret to sign the session cookies and JWT tokens after successful OIDC validation. This is required even when using external authentication to maintain secure session state between the frontend and backend API.

### How do I map admin roles from my OIDC provider?

Set `OIDC_CLAIM_ROLES` to the claim name containing roles (e.g., "groups" or "roles"), then set `OIDC_ROLE_ADMIN` to the specific value that should grant administrator privileges (e.g., "admin" or "romm-admin"). Users with matching claims receive the **ADMIN** role in RomM, while other values map to **USER** or are ignored if not configured.

### Can I disable local authentication when OIDC is enabled?

Currently, RomM supports both authentication methods simultaneously. The OIDC button appears alongside the standard login form when `OIDC_ENABLED` is `true`, allowing users to choose their preferred method. There is no configuration option to disable local authentication while OIDC is active.