How OIDC SSO Integration Works with Claims-Based Admin Mapping in TREK

TREK determines administrator privileges during OIDC SSO authentication by inspecting a configurable claim (default groups) for a specific value defined in OIDC_ADMIN_VALUE, automatically granting the admin role when the claim value matches while protecting the bootstrap user and last remaining admin from demotion.

TREK implements OpenID Connect (OIDC) single sign-on to delegate authentication to external identity providers like Google, Keycloak, or Authentik. After verifying the ID token, the system performs claims-based admin mapping to determine whether the authenticated user should receive administrator privileges or standard user access. This mapping relies on environment variables to inspect specific claims in the OIDC userinfo response, making it easy to synchronize your identity provider's group memberships with TREK's role-based access control.

Configuration Environment Variables

Claims-based admin mapping is controlled exclusively through environment variables. These settings cannot be modified from the admin panel and must be configured before starting the application.

  • OIDC_ADMIN_CLAIM: Specifies the claim name to inspect for role information (default: groups). Common alternatives include roles or realm_access.roles.
  • OIDC_ADMIN_VALUE: The exact value that must be present in the claim to grant admin privileges. When unset, claim-based admin mapping is disabled and all users receive the standard user role.
  • OIDC_ISSUER, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET: Standard OIDC configuration for provider discovery and token exchange.

For a complete reference, see the environment variable documentation in wiki/OIDC-SSO.md and wiki/Environment-Variables.md.

Authentication Flow Overview

The OIDC SSO process follows these steps to authenticate and authorize users:

  1. The user initiates login via GET /api/auth/oidc/login, which redirects to the identity provider.
  2. After authentication, the IdP redirects to GET /api/auth/oidc/callback with an authorization code.
  3. TREK exchanges the code for tokens using exchangeCodeForToken() in server/src/services/oidcService.ts.
  4. The ID token is verified via verifyIdToken() using the provider's JWKS.
  5. User information is retrieved via getUserInfo().
  6. The system calls findOrCreateUser() to link the OIDC identity to a local account or create a new one.
  7. Role resolution occurs through resolveOidcRole(), which determines if the user receives admin or user privileges.

Claims-Based Admin Mapping Logic

The core role resolution logic resides in server/src/services/oidcService.ts within the resolveOidcRole function. This function implements a hierarchy of checks to determine the appropriate role.

First User Protection

The bootstrap administrator (the first account ever created in the TREK instance) is automatically granted admin privileges regardless of OIDC claims. This ensures you cannot lock yourself out during initial setup.

Claim Inspection

For subsequent users, if OIDC_ADMIN_VALUE is configured, TREK examines the claim specified by OIDC_ADMIN_CLAIM:

export function resolveOidcRole(userInfo: OidcUserInfo, isFirstUser: boolean): 'admin' | 'user' {
  // First user always becomes admin
  if (isFirstUser) return 'admin';

  const adminValue = process.env.OIDC_ADMIN_VALUE;
  if (!adminValue) return 'user'; // Claim-based admin disabled

  const claimKey = process.env.OIDC_ADMIN_CLAIM || 'groups';
  const claimData = userInfo[claimKey];

  // Handle array claims (e.g., ["admins", "users"])
  if (Array.isArray(claimData)) {
    return claimData.some(v => String(v) === adminValue) ? 'admin' : 'user';
  }
  
  // Handle string claims
  if (typeof claimData === 'string') {
    return claimData === adminValue ? 'admin' : 'user';
  }
  
  return 'user';
}

The function handles both array and string claim formats, converting values to strings for comparison against OIDC_ADMIN_VALUE.

Role Re-evaluation and Safety

TREK stores the resolved role in the users.role column and re-evaluates it on every login. However, the system includes a critical safety check: if the resolved role would downgrade the last remaining admin, TREK logs a warning and preserves the admin role to prevent accidental lock-out.

Configuration Examples

Docker Compose Setup

Configure claims-based admin mapping in your docker-compose.yml:

services:
  trek:
    environment:
      - OIDC_ISSUER=https://auth.example.com
      - OIDC_CLIENT_ID=trek
      - OIDC_CLIENT_SECRET=super-secret
      - OIDC_ADMIN_CLAIM=groups          # Claim to inspect

      - OIDC_ADMIN_VALUE=app-trek-admins # Value granting admin access

Custom Role Claims

For identity providers using roles instead of groups:

export OIDC_ADMIN_CLAIM=roles
export OIDC_ADMIN_VALUE=administrator

Users with "roles": ["administrator", "editor"] in their ID token will receive admin privileges.

Programmatic Role Verification

When building custom middleware or extensions, you can reuse the role resolution logic:

import { resolveOidcRole } from './services/oidcService';

function checkAdminAccess(userInfo: OidcUserInfo): boolean {
  // Pass false for isFirstUser when checking existing users
  return resolveOidcRole(userInfo, false) === 'admin';
}

Key Implementation Files

File Purpose
server/src/services/oidcService.ts Core OIDC implementation including resolveOidcRole(), findOrCreateUser(), and token validation
wiki/OIDC-SSO.md High-level documentation of the OIDC flow and required environment variables
wiki/Environment-Variables.md Complete reference for all OIDC-related configuration
server/tests/unit/services/oidcService.test.ts Unit tests covering claims-based admin mapping scenarios
docker-compose.yml Example production configuration showing OIDC variable placement

Summary

  • Claims-based admin mapping uses OIDC_ADMIN_CLAIM (default: groups) and OIDC_ADMIN_VALUE to determine administrator privileges from OIDC tokens.
  • The first user in the system is automatically granted admin status regardless of claims.
  • The resolveOidcRole() function in server/src/services/oidcService.ts handles both array and string claim formats.
  • Roles are re-evaluated on every login, with safety mechanisms preventing the demotion of the last remaining admin.
  • Configuration is environment-variable only and cannot be changed through the web interface.

Frequently Asked Questions

What happens if OIDC_ADMIN_VALUE is not set?

When OIDC_ADMIN_VALUE is undefined, claims-based admin mapping is disabled. Every user authenticating via SSO receives the standard user role, and no automatic admin promotion occurs based on OIDC claims. You must manually promote users to admin through the database or admin panel.

Can I use nested claims for role mapping?

The current implementation in resolveOidcRole() performs a direct property lookup on the userinfo object using userInfo[claimKey]. Nested claims (e.g., realm_access.roles) require your identity provider to flatten the claim or you must configure a custom claim mapper in your IdP to surface the nested value as a top-level claim.

Does TREK support multiple admin values or regex patterns?

As implemented in server/src/services/oidcService.ts, the comparison uses exact string equality (===). The code checks if any array element equals the OIDC_ADMIN_VALUE or if the string claim equals it exactly. Multiple values or regex patterns are not supported; you must specify a single exact value.

How does TREK handle claim value type coercion?

The function explicitly converts array elements to strings using String(v) before comparison, ensuring consistent matching regardless of whether the IdP sends integers or strings. However, the OIDC_ADMIN_VALUE environment variable is always treated as a string for comparison purposes.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →