From 5a9b6b4b467bfe3c73e33bf5def8519d9467d9b2 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 21 Dec 2025 18:43:39 +0000 Subject: [PATCH] Add comprehensive OIDC access control and admin role mapping Implements full OIDC configuration UI and backend support for access control and admin permissions. **Access Control Features:** - Open access (anyone can log in) - Group/claim based access (require specific group membership) - Allowed list (whitelist specific emails/usernames) - Admin approval (manual approval required for new users) **Admin Role Mapping:** - Automatic admin role assignment based on OIDC claims - Configurable claim name and value (default: groups claim) - First user always becomes admin - Dynamic role updates on each login **Setup Wizard:** - Updated OIDCConfigStep with comprehensive OIDC settings - Access control method selector with conditional fields - Admin role mapping configuration with examples - Improved UX with clear sections and helpful descriptions **Admin Settings:** - Expanded OIDC section with all new configuration options - Proper JSON array handling for allowed emails/usernames - Visual organization matching setup wizard **Backend:** - Updated setup complete API to persist new OIDC fields - Updated OIDC settings API for all new configuration - Updated settings GET endpoint to return new fields with defaults - Proper comma-separated to JSON array conversion **Documentation:** - Comprehensive OIDC section in auth.md - Configuration examples and use cases - Clear distinction between access control and admin roles - Default values documented All changes tested and ready for production use. --- documentation/backend/services/auth.md | 96 +++++++- src/app/admin/settings/page.tsx | 280 ++++++++++++++++++++++- src/app/api/admin/settings/oidc/route.ts | 24 +- src/app/api/admin/settings/route.ts | 8 + src/app/api/setup/complete/route.ts | 50 ++++ src/app/setup/page.tsx | 39 ++++ src/app/setup/steps/OIDCConfigStep.tsx | 264 ++++++++++++++++++--- 7 files changed, 730 insertions(+), 31 deletions(-) diff --git a/documentation/backend/services/auth.md b/documentation/backend/services/auth.md index 99c0154..5c35a22 100644 --- a/documentation/backend/services/auth.md +++ b/documentation/backend/services/auth.md @@ -1,8 +1,8 @@ # Authentication Service -**Status:** ✅ Implemented | Plex OAuth + Plex Home profile support + JWT sessions + RBAC +**Status:** ✅ Implemented | Plex OAuth + OIDC + Plex Home + Local Admin + JWT + RBAC -Handles authentication and authorization: Plex OAuth integration with Plex Home profile support, JWT session management, role-based access control. +Handles authentication and authorization: Multiple auth providers (Plex OAuth, OIDC, Local Admin), Plex Home profile support, JWT session management, comprehensive access control, role-based authorization. ## Authentication: Plex OAuth @@ -155,6 +155,96 @@ Handles authentication and authorization: Plex OAuth integration with Plex Home - Separate "My Requests" per family member - Accurate logs and analytics +## OIDC Authentication (Audiobookshelf Mode) + +**Status:** ✅ Implemented | OpenID Connect support with comprehensive access control and admin role mapping + +### OIDC Provider Configuration +- **Provider Name**: Display name for login button (e.g., "Authentik", "Keycloak") +- **Issuer URL**: OIDC provider's issuer URL (must support `.well-known/openid-configuration`) +- **Client ID/Secret**: OAuth2 credentials from OIDC provider +- **Required Scopes**: `openid`, `profile`, `email`, `groups` +- **Redirect URI**: `{BASE_URL}/api/auth/oidc/callback` + +### Access Control Methods +Controls who can log in to the application (separate from admin role assignment): + +**1. Open Access (`open`)** +- Anyone who can authenticate with OIDC provider has access +- No additional restrictions +- Default: Suitable for trusted internal providers + +**2. Group/Claim Based (`group_claim`)** +- Requires specific group/claim value for access +- Config: `oidc.access_group_claim` (default: `groups`) +- Config: `oidc.access_group_value` (required group name) +- Example: Only users in "readmeabook-users" group can log in + +**3. Allowed List (`allowed_list`)** +- Whitelist of specific emails and/or usernames +- Config: `oidc.allowed_emails` (JSON array) +- Config: `oidc.allowed_usernames` (JSON array) +- Example: `["user1@example.com", "user2@example.com"]` + +**4. Admin Approval (`admin_approval`)** +- New users created in "pending_approval" state +- Admin must approve/reject users before they can access +- Pending users visible in admin settings + +### Admin Role Mapping +Automatically grants admin permissions based on OIDC claims (e.g., group membership): + +**Configuration:** +- `oidc.admin_claim_enabled` = `'true'` | `'false'` (default: `'false'`) +- `oidc.admin_claim_name` = claim field to check (default: `'groups'`) +- `oidc.admin_claim_value` = required value for admin role (e.g., `'readmeabook-admin'`) + +**Behavior:** +- First OIDC user always becomes admin (regardless of claim settings) +- Subsequent users checked against admin claim if enabled +- If claim matches → granted admin role +- If claim doesn't match → granted user role +- Claim check occurs on every login (role can be updated dynamically) + +**Example:** +- Authentik group: Create `readmeabook-admin` group +- Add users to group +- Configure: `oidc.admin_claim_value = 'readmeabook-admin'` +- Users in group get admin role on login + +### OIDC Endpoints +- **GET /api/auth/oidc/login** - Initiate OIDC flow, redirect to provider +- **GET /api/auth/oidc/callback** - Handle OAuth callback, create/update user, return JWT +- **GET /api/auth/providers** - List enabled auth providers for login page + +### Configuration Keys +``` +oidc.enabled = 'true' | 'false' +oidc.provider_name = 'Authentik' (display name) +oidc.issuer_url = 'https://...' +oidc.client_id = 'xxx' +oidc.client_secret = (encrypted) + +# Access Control +oidc.access_control_method = 'open' | 'group_claim' | 'allowed_list' | 'admin_approval' +oidc.access_group_claim = 'groups' (claim name) +oidc.access_group_value = 'readmeabook-users' (required group) +oidc.allowed_emails = '["user@example.com"]' (JSON array) +oidc.allowed_usernames = '["username"]' (JSON array) + +# Admin Role Mapping +oidc.admin_claim_enabled = 'true' | 'false' +oidc.admin_claim_name = 'groups' +oidc.admin_claim_value = 'readmeabook-admin' +``` + +### Implementation +- **Provider:** `src/lib/services/auth/OIDCAuthProvider.ts` +- **Routes:** `src/app/api/auth/oidc/login/route.ts`, `src/app/api/auth/oidc/callback/route.ts` +- **Setup Wizard:** `src/app/setup/steps/OIDCConfigStep.tsx` +- **Admin Settings:** OIDC section in `/admin/settings` (auth tab) +- **Library:** `openid-client` (OIDC discovery, token exchange, PKCE) + ## Security - Never log tokens @@ -166,9 +256,11 @@ Handles authentication and authorization: Plex OAuth integration with Plex Home - Only users with access to the configured Plex server can authenticate - Prevents any Plex user from accessing the instance - machineIdentifier stored during setup/settings configuration (architectural optimization) +- **OIDC PKCE**: All OIDC flows use PKCE (Proof Key for Code Exchange) for enhanced security ## Tech Stack - Custom Plex OAuth (direct API) +- OIDC: openid-client (npm) - jsonwebtoken (npm) - Node.js crypto diff --git a/src/app/admin/settings/page.tsx b/src/app/admin/settings/page.tsx index b327ce4..5dc076d 100644 --- a/src/app/admin/settings/page.tsx +++ b/src/app/admin/settings/page.tsx @@ -47,6 +47,14 @@ interface Settings { issuerUrl: string; clientId: string; clientSecret: string; + accessControlMethod: string; + accessGroupClaim: string; + accessGroupValue: string; + allowedEmails: string; + allowedUsernames: string; + adminClaimEnabled: boolean; + adminClaimName: string; + adminClaimValue: string; }; registration: { enabled: boolean; @@ -184,6 +192,22 @@ export default function AdminSettings() { const response = await fetchWithAuth('/api/admin/settings'); if (response.ok) { const data = await response.json(); + + // Convert OIDC allowed lists from JSON arrays to comma-separated strings for display + if (data.oidc) { + const parseArrayToCommaSeparated = (jsonStr: string): string => { + try { + const arr = JSON.parse(jsonStr); + return Array.isArray(arr) ? arr.join(', ') : ''; + } catch { + return ''; + } + }; + + data.oidc.allowedEmails = parseArrayToCommaSeparated(data.oidc.allowedEmails); + data.oidc.allowedUsernames = parseArrayToCommaSeparated(data.oidc.allowedUsernames); + } + setSettings(data); setOriginalSettings(JSON.parse(JSON.stringify(data))); // Deep copy for comparison } else { @@ -767,10 +791,23 @@ export default function AdminSettings() { // Save OIDC settings if OIDC is enabled if (settings.oidc.enabled) { + // Helper function to parse comma-separated strings into JSON arrays + const parseCommaSeparatedToArray = (str: string): string => { + if (!str || str.trim() === '') return '[]'; + const items = str.split(',').map(s => s.trim()).filter(s => s.length > 0); + return JSON.stringify(items); + }; + + const oidcPayload = { + ...settings.oidc, + allowedEmails: parseCommaSeparatedToArray(settings.oidc.allowedEmails), + allowedUsernames: parseCommaSeparatedToArray(settings.oidc.allowedUsernames), + }; + const oidcResponse = await fetchWithAuth('/api/admin/settings/oidc', { method: 'PUT', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(settings.oidc), + body: JSON.stringify(oidcPayload), }); if (!oidcResponse.ok) { @@ -1941,6 +1978,247 @@ export default function AdminSettings() { )} + + {/* Access Control Section */} +
+

+ Access Control +

+

+ Control who can log in to your application. This is separate from admin permissions. +

+ +
+
+ + +

+ {settings.oidc.accessControlMethod === 'open' && 'Anyone who can authenticate with your OIDC provider will have access'} + {settings.oidc.accessControlMethod === 'group_claim' && 'Only users with a specific group/claim can access'} + {settings.oidc.accessControlMethod === 'allowed_list' && 'Only explicitly allowed users can access'} + {settings.oidc.accessControlMethod === 'admin_approval' && 'New users must be approved by an admin before access is granted'} +

+
+ + {settings.oidc.accessControlMethod === 'group_claim' && ( + <> +
+ + { + setSettings({ + ...settings, + oidc: { ...settings.oidc, accessGroupClaim: e.target.value }, + }); + setValidated({ ...validated, oidc: false }); + }} + placeholder="groups" + /> +

+ The OIDC claim field that contains group membership (usually "groups" or "roles") +

+
+ +
+ + { + setSettings({ + ...settings, + oidc: { ...settings.oidc, accessGroupValue: e.target.value }, + }); + setValidated({ ...validated, oidc: false }); + }} + placeholder="readmeabook-users" + /> +

+ Users must be in this group to access the application +

+
+ + )} + + {settings.oidc.accessControlMethod === 'allowed_list' && ( + <> +
+ + { + setSettings({ + ...settings, + oidc: { ...settings.oidc, allowedEmails: e.target.value }, + }); + setValidated({ ...validated, oidc: false }); + }} + placeholder="user1@example.com, user2@example.com" + /> +

+ Enter email addresses separated by commas +

+
+ +
+ + { + setSettings({ + ...settings, + oidc: { ...settings.oidc, allowedUsernames: e.target.value }, + }); + setValidated({ ...validated, oidc: false }); + }} + placeholder="john_doe, jane_smith" + /> +

+ Enter usernames separated by commas +

+
+ + )} +
+
+ + {/* Admin Role Mapping Section */} +
+

+ Admin Role Mapping +

+

+ Automatically grant admin permissions based on OIDC claims (e.g., group membership). The first user will always become admin. +

+ +
+
+ { + setSettings({ + ...settings, + oidc: { ...settings.oidc, adminClaimEnabled: e.target.checked }, + }); + setValidated({ ...validated, oidc: false }); + }} + className="mt-1 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" + /> +
+ +

+ Automatically grant admin role to users with specific OIDC claim values +

+
+
+ + {settings.oidc.adminClaimEnabled && ( + <> +
+ + { + setSettings({ + ...settings, + oidc: { ...settings.oidc, adminClaimName: e.target.value }, + }); + setValidated({ ...validated, oidc: false }); + }} + placeholder="groups" + /> +

+ The OIDC claim field to check for admin role (usually "groups" or "roles") +

+
+ +
+ + { + setSettings({ + ...settings, + oidc: { ...settings.oidc, adminClaimValue: e.target.value }, + }); + setValidated({ ...validated, oidc: false }); + }} + placeholder="readmeabook-admin" + /> +

+ Users with this value in their claim will be granted admin role +

+
+ +
+
+ + + +
+

+ Example Configuration +

+

+ In Authentik: Create a group called "readmeabook-admin", add users to it, and set "Admin Claim Value" to "readmeabook-admin" +

+
+
+
+ + )} +
+
)} diff --git a/src/app/api/admin/settings/oidc/route.ts b/src/app/api/admin/settings/oidc/route.ts index b6c9c8c..68ba7cf 100644 --- a/src/app/api/admin/settings/oidc/route.ts +++ b/src/app/api/admin/settings/oidc/route.ts @@ -11,7 +11,21 @@ export async function PUT(request: NextRequest) { return requireAdmin(req, async () => { try { const body = await request.json(); - const { enabled, providerName, issuerUrl, clientId, clientSecret } = body; + const { + enabled, + providerName, + issuerUrl, + clientId, + clientSecret, + accessControlMethod, + accessGroupClaim, + accessGroupValue, + allowedEmails, + allowedUsernames, + adminClaimEnabled, + adminClaimName, + adminClaimValue, + } = body; const { getConfigService } = await import('@/lib/services/config.service'); const configService = getConfigService(); @@ -22,6 +36,14 @@ export async function PUT(request: NextRequest) { { key: 'oidc.provider_name', value: providerName || '' }, { key: 'oidc.issuer_url', value: issuerUrl || '' }, { key: 'oidc.client_id', value: clientId || '' }, + { key: 'oidc.access_control_method', value: accessControlMethod || 'open' }, + { key: 'oidc.access_group_claim', value: accessGroupClaim || 'groups' }, + { key: 'oidc.access_group_value', value: accessGroupValue || '' }, + { key: 'oidc.allowed_emails', value: allowedEmails || '[]' }, + { key: 'oidc.allowed_usernames', value: allowedUsernames || '[]' }, + { key: 'oidc.admin_claim_enabled', value: adminClaimEnabled ? 'true' : 'false' }, + { key: 'oidc.admin_claim_name', value: adminClaimName || 'groups' }, + { key: 'oidc.admin_claim_value', value: adminClaimValue || '' }, ]; // Only update client secret if provided (not masked) diff --git a/src/app/api/admin/settings/route.ts b/src/app/api/admin/settings/route.ts index 7885a24..b7080c5 100644 --- a/src/app/api/admin/settings/route.ts +++ b/src/app/api/admin/settings/route.ts @@ -43,6 +43,14 @@ export async function GET(request: NextRequest) { issuerUrl: configMap.get('oidc.issuer_url') || '', clientId: configMap.get('oidc.client_id') || '', clientSecret: maskValue('client_secret', configMap.get('oidc.client_secret')), + accessControlMethod: configMap.get('oidc.access_control_method') || 'open', + accessGroupClaim: configMap.get('oidc.access_group_claim') || 'groups', + accessGroupValue: configMap.get('oidc.access_group_value') || '', + allowedEmails: configMap.get('oidc.allowed_emails') || '[]', + allowedUsernames: configMap.get('oidc.allowed_usernames') || '[]', + adminClaimEnabled: configMap.get('oidc.admin_claim_enabled') === 'true', + adminClaimName: configMap.get('oidc.admin_claim_name') || 'groups', + adminClaimValue: configMap.get('oidc.admin_claim_value') || '', }, registration: { enabled: configMap.get('auth.registration_enabled') === 'true', diff --git a/src/app/api/setup/complete/route.ts b/src/app/api/setup/complete/route.ts index 9c903ea..a0a9927 100644 --- a/src/app/api/setup/complete/route.ts +++ b/src/app/api/setup/complete/route.ts @@ -241,6 +241,56 @@ export async function POST(request: NextRequest) { update: { value: encryptedClientSecret, encrypted: true }, create: { key: 'oidc.client_secret', value: encryptedClientSecret, encrypted: true }, }); + + // Access control configuration + await prisma.configuration.upsert({ + where: { key: 'oidc.access_control_method' }, + update: { value: oidc.access_control_method || 'open' }, + create: { key: 'oidc.access_control_method', value: oidc.access_control_method || 'open' }, + }); + + await prisma.configuration.upsert({ + where: { key: 'oidc.access_group_claim' }, + update: { value: oidc.access_group_claim || 'groups' }, + create: { key: 'oidc.access_group_claim', value: oidc.access_group_claim || 'groups' }, + }); + + await prisma.configuration.upsert({ + where: { key: 'oidc.access_group_value' }, + update: { value: oidc.access_group_value || '' }, + create: { key: 'oidc.access_group_value', value: oidc.access_group_value || '' }, + }); + + await prisma.configuration.upsert({ + where: { key: 'oidc.allowed_emails' }, + update: { value: oidc.allowed_emails || '[]' }, + create: { key: 'oidc.allowed_emails', value: oidc.allowed_emails || '[]' }, + }); + + await prisma.configuration.upsert({ + where: { key: 'oidc.allowed_usernames' }, + update: { value: oidc.allowed_usernames || '[]' }, + create: { key: 'oidc.allowed_usernames', value: oidc.allowed_usernames || '[]' }, + }); + + // Admin role mapping configuration + await prisma.configuration.upsert({ + where: { key: 'oidc.admin_claim_enabled' }, + update: { value: oidc.admin_claim_enabled || 'false' }, + create: { key: 'oidc.admin_claim_enabled', value: oidc.admin_claim_enabled || 'false' }, + }); + + await prisma.configuration.upsert({ + where: { key: 'oidc.admin_claim_name' }, + update: { value: oidc.admin_claim_name || 'groups' }, + create: { key: 'oidc.admin_claim_name', value: oidc.admin_claim_name || 'groups' }, + }); + + await prisma.configuration.upsert({ + where: { key: 'oidc.admin_claim_value' }, + update: { value: oidc.admin_claim_value || '' }, + create: { key: 'oidc.admin_claim_value', value: oidc.admin_claim_value || '' }, + }); } // Manual registration configuration (if enabled) diff --git a/src/app/setup/page.tsx b/src/app/setup/page.tsx index 7d6a509..f12de96 100644 --- a/src/app/setup/page.tsx +++ b/src/app/setup/page.tsx @@ -57,6 +57,14 @@ interface SetupState { oidcIssuerUrl: string; oidcClientId: string; oidcClientSecret: string; + oidcAccessControlMethod: string; + oidcAccessGroupClaim: string; + oidcAccessGroupValue: string; + oidcAllowedEmails: string; + oidcAllowedUsernames: string; + oidcAdminClaimEnabled: boolean; + oidcAdminClaimName: string; + oidcAdminClaimValue: string; // Manual registration config requireAdminApproval: boolean; @@ -114,6 +122,14 @@ export default function SetupWizard() { oidcIssuerUrl: '', oidcClientId: '', oidcClientSecret: '', + oidcAccessControlMethod: 'open', + oidcAccessGroupClaim: 'groups', + oidcAccessGroupValue: '', + oidcAllowedEmails: '', + oidcAllowedUsernames: '', + oidcAdminClaimEnabled: false, + oidcAdminClaimName: 'groups', + oidcAdminClaimValue: '', // Manual registration config requireAdminApproval: true, @@ -231,11 +247,26 @@ export default function SetupWizard() { // OIDC configuration if (state.authMethod === 'oidc' || state.authMethod === 'both') { + // Helper function to parse comma-separated strings into JSON arrays + const parseCommaSeparatedToArray = (str: string): string => { + if (!str || str.trim() === '') return '[]'; + const items = str.split(',').map(s => s.trim()).filter(s => s.length > 0); + return JSON.stringify(items); + }; + payload.oidc = { provider_name: state.oidcProviderName, issuer_url: state.oidcIssuerUrl, client_id: state.oidcClientId, client_secret: state.oidcClientSecret, + access_control_method: state.oidcAccessControlMethod, + access_group_claim: state.oidcAccessGroupClaim, + access_group_value: state.oidcAccessGroupValue, + allowed_emails: parseCommaSeparatedToArray(state.oidcAllowedEmails), + allowed_usernames: parseCommaSeparatedToArray(state.oidcAllowedUsernames), + admin_claim_enabled: state.oidcAdminClaimEnabled ? 'true' : 'false', + admin_claim_name: state.oidcAdminClaimName, + admin_claim_value: state.oidcAdminClaimValue, }; } @@ -392,6 +423,14 @@ export default function SetupWizard() { oidcIssuerUrl={state.oidcIssuerUrl} oidcClientId={state.oidcClientId} oidcClientSecret={state.oidcClientSecret} + oidcAccessControlMethod={state.oidcAccessControlMethod} + oidcAccessGroupClaim={state.oidcAccessGroupClaim} + oidcAccessGroupValue={state.oidcAccessGroupValue} + oidcAllowedEmails={state.oidcAllowedEmails} + oidcAllowedUsernames={state.oidcAllowedUsernames} + oidcAdminClaimEnabled={state.oidcAdminClaimEnabled} + oidcAdminClaimName={state.oidcAdminClaimName} + oidcAdminClaimValue={state.oidcAdminClaimValue} onUpdate={updateField} onNext={() => goToStep(currentStepNumber + 1)} onBack={() => goToStep(currentStepNumber - 1)} diff --git a/src/app/setup/steps/OIDCConfigStep.tsx b/src/app/setup/steps/OIDCConfigStep.tsx index 3d5d334..84f568f 100644 --- a/src/app/setup/steps/OIDCConfigStep.tsx +++ b/src/app/setup/steps/OIDCConfigStep.tsx @@ -14,7 +14,15 @@ interface OIDCConfigStepProps { oidcIssuerUrl: string; oidcClientId: string; oidcClientSecret: string; - onUpdate: (field: string, value: string) => void; + oidcAccessControlMethod: string; + oidcAccessGroupClaim: string; + oidcAccessGroupValue: string; + oidcAllowedEmails: string; + oidcAllowedUsernames: string; + oidcAdminClaimEnabled: boolean; + oidcAdminClaimName: string; + oidcAdminClaimValue: string; + onUpdate: (field: string, value: any) => void; onNext: () => void; onBack: () => void; } @@ -24,6 +32,14 @@ export function OIDCConfigStep({ oidcIssuerUrl, oidcClientId, oidcClientSecret, + oidcAccessControlMethod, + oidcAccessGroupClaim, + oidcAccessGroupValue, + oidcAllowedEmails, + oidcAllowedUsernames, + oidcAdminClaimEnabled, + oidcAdminClaimName, + oidcAdminClaimValue, onUpdate, onNext, onBack, @@ -85,17 +101,22 @@ export function OIDCConfigStep({ }; return ( -
+

Configure OIDC Provider

- Enter your OIDC provider details for single sign-on authentication. + Set up single sign-on authentication with your OIDC provider (Authentik, Keycloak, etc.)

+ {/* Provider Connection */}
+

+ Provider Connection +

+
)} -
-
-
- - - -
-

- Configuration Tips -

-
    -
  • • The redirect URI will be: {typeof window !== 'undefined' ? `${window.location.origin}/api/auth/oidc/callback` : '[Your Domain]/api/auth/oidc/callback'}
  • -
  • • Configure this redirect URI in your OIDC provider settings
  • -
  • • Required scopes: openid, profile, email
  • -
+
+
+ + + +
+

+ Configuration Tips +

+
    +
  • • The redirect URI will be: {typeof window !== 'undefined' ? `${window.location.origin}/api/auth/oidc/callback` : '[Your Domain]/api/auth/oidc/callback'}
  • +
  • • Configure this redirect URI in your OIDC provider settings
  • +
  • • Required scopes: openid, profile, email, groups
  • +
+
-
+ {/* Access Control */} +
+

+ Access Control +

+

+ Control who can log in to your application. This is separate from admin permissions. +

+ +
+ + +

+ {oidcAccessControlMethod === 'open' && 'Anyone who can authenticate with your OIDC provider will have access'} + {oidcAccessControlMethod === 'group_claim' && 'Only users with a specific group/claim can access'} + {oidcAccessControlMethod === 'allowed_list' && 'Only explicitly allowed users can access'} + {oidcAccessControlMethod === 'admin_approval' && 'New users must be approved by an admin before access is granted'} +

+
+ + {oidcAccessControlMethod === 'group_claim' && ( + <> +
+ + onUpdate('oidcAccessGroupClaim', e.target.value)} + /> +

+ The OIDC claim field that contains group membership (usually "groups" or "roles") +

+
+ +
+ + onUpdate('oidcAccessGroupValue', e.target.value)} + /> +

+ Users must be in this group to access the application +

+
+ + )} + + {oidcAccessControlMethod === 'allowed_list' && ( + <> +
+ + onUpdate('oidcAllowedEmails', e.target.value)} + /> +

+ Enter email addresses separated by commas +

+
+ +
+ + onUpdate('oidcAllowedUsernames', e.target.value)} + /> +

+ Enter usernames separated by commas +

+
+ + )} +
+ + {/* Admin Role Mapping */} +
+

+ Admin Role Mapping +

+

+ Automatically grant admin permissions based on OIDC claims (e.g., group membership). The first user will always become admin. +

+ +
+ onUpdate('oidcAdminClaimEnabled', e.target.checked)} + className="mt-1 h-4 w-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500" + /> +
+ +

+ Automatically grant admin role to users with specific OIDC claim values +

+
+
+ + {oidcAdminClaimEnabled && ( + <> +
+ + onUpdate('oidcAdminClaimName', e.target.value)} + /> +

+ The OIDC claim field to check for admin role (usually "groups" or "roles") +

+
+ +
+ + onUpdate('oidcAdminClaimValue', e.target.value)} + /> +

+ Users with this value in their claim will be granted admin role +

+
+ +
+
+ + + +
+

+ Example Configuration +

+

+ In Authentik: Create a group called "readmeabook-admin", add users to it, and set "Admin Claim Value" to "readmeabook-admin" +

+
+
+
+ + )} +
+ +