Skip to content

feat(auth): extra Apple OAuth clients in Admin - #311

Open
ChrisPdgn wants to merge 5 commits into
ui-rewritefrom
cursor/apple-extra-oauth-clients-5ded
Open

feat(auth): extra Apple OAuth clients in Admin#311
ChrisPdgn wants to merge 5 commits into
ui-rewritefrom
cursor/apple-extra-oauth-clients-5ded

Conversation

@ChrisPdgn

@ChrisPdgn ChrisPdgn commented Sep 1, 2026

Copy link
Copy Markdown

Overview

This PR adds support for configuring extra Apple OAuth clients in the Conduit Admin UI, matching the backend contract from Conduit #1547.

Also replaces #296

What Operators Can Do

  • Configure multiple Apple OAuth clients from a single Conduit instance
  • Each extra client has a unique nickname (id) and Apple app ID (clientId)
  • Extra clients can inherit the default Apple credentials or provide a second set of Apple team credentials
  • Set optional per-client name and redirect_uri (falls back to default when empty)

UI Design

Polished layout inspired by #296:

  • Collapsible sections for default client and extra clients (collapsed by default)
  • Credential completeness badges showing status: "Not configured", "Incomplete", or "Credentials complete"
  • Compact headers with title + subtitle (nickname / Apple app ID)
  • Responsive grid layout for form fields
  • Add/remove clients within the collapsible layout

Plain-Language Copy (Unchanged)

  • Uses "nickname" (not "Client ID (oauthClientId)")
  • Uses "Apple app ID" (not "Client ID")
  • Helper text: "leave empty to reuse the default key"
  • Admin-focused, non-technical language

Key Changes

Type Updates

  • Added AppleExtraClient type with fields: id, clientId, name, redirect_uri, privateKey, teamId, keyId
  • Extended AppleConfig to include optional clients[] array
  • Added CredentialFields type for badge calculation

UI Implementation

  • Implemented dynamic client management using React Hook Form's useFieldArray
  • Collapsible default client section with credential badge
  • Collapsible extra client rows with:
    • Nickname (required, unique identifier)
    • Apple app ID (required, Services ID or bundle ID)
    • Name (optional, admin label)
    • Redirect URI (optional, inherits default when empty)
    • Private Key, Team ID, Key ID (all three required for second Apple team, or leave all empty to inherit default)
  • Grid layout for responsive two-column forms

Validation

  • Unique nicknames enforced (trimmed, case-insensitive check) with errors shown on the specific duplicate row's id field
  • All-or-nothing validation for extra credentials: either all three (privateKey, teamId, keyId) or none
  • Default Apple configuration requires all four fields when enabled: clientId, privateKey, teamId, and keyId (breaking change from backend)
  • Form prevents submission when validation fails
  • Whitespace-only credentials and redirect_uri normalized to empty string on submit (prevents backend treating " " as set)

Bug Fixes

  • Fixed lodash merge issue: Previously, lodash merge would merge apple.clients[] by array index, causing deleted rows to reappear and fields from different clients to mix. Now the entire apple config is replaced wholesale on save when using the Strategy Settings dialog.
  • Added ScrollArea to Settings dialog to handle overflow when many extra clients are configured
  • General Auth settings page (Authentication → Settings) does NOT use lodash merge and does NOT send strategy configs at all — it only sends a partial config with the fields it controls (tokens, teams, captcha, etc.), so it cannot affect apple.clients.

UX Details

  • Helper text explains when to inherit vs provide second team credentials
  • Clear visual separation between default Apple config and extra clients
  • Trim whitespace from id and clientId on submit (backend uniqueness is trimmed, lookup is exact)
  • PEM privateKey interior whitespace is preserved (only whitespace-only strings normalized to empty)
  • Duplicate nickname errors appear next to the offending Nickname field, not at array level
  • Credential badges provide at-a-glance status of configuration completeness

Backend Contract

This implementation matches the backend contract from Conduit #1547:

  • Default Apple: clientId, privateKey, teamId, keyId, redirect_uri, enabled, accountLinking
  • Extra clients: apple.clients[] with id (nickname), clientId, optional name, redirect_uri, and optional credential override
  • Empty string on extra redirect_uri or credentials = inherit from default
  • Enabling Apple requires all four default fields including keyId (breaking change)
  • Admin API: PATCH /config/authentication with { config: { ...full authentication config } }

Testing

Tested scenarios:

  • Add/remove extra clients
  • Collapsible sections work correctly
  • Credential badges show correct status
  • Unique nickname validation shows on duplicate row's id field
  • All-or-nothing credential validation
  • Inherit default credentials (leave privateKey/teamId/keyId empty)
  • Second Apple team (provide all three credentials)
  • Mixed credentials (1 or 2 of 3) rejected
  • Whitespace-only creds/redirect_uri normalized to empty on submit
  • Enabling Apple without default keyId blocked
  • Deleted rows stay gone after save
  • Dialog scrolls with many extra clients
  • Empty clients[] array is valid
  • General Auth settings page doesn't affect apple.clients
  • Responsive grid layout works on mobile

Out of Scope

  • Product apps passing oauthClientId (consumer-side usage)
  • Other OAuth providers
  • Chat invitation UI (#1512)
  • Generic schema-driven config form

Notes for Reviewers

  • This PR is marked as draft and is ready for review but should not be merged until approved
  • UI copy is kept in plain language for non-technical operators (different from feat(auth): add collapsible Apple multi-client OAuth settings #296's "oauthClientId" terminology)
  • The keyId requirement when enabling Apple is a breaking change from the backend
  • The lodash merge fix is critical: it prevents data corruption when managing the clients array
  • General Auth settings page is safe: it sends only a partial config without strategy fields
  • The collapsible layout with badges provides a cleaner, more scalable UI for managing multiple clients
Open in Web Open in Cursor 

cursor Bot pushed a commit that referenced this pull request Sep 1, 2026
…ntial badges

Steal UI patterns from #296 (kkopanidis feat/apple-multi-oauth-clients):
- Collapsible default-client and extra-client rows (collapsed by default)
- Credential completeness badge (Not configured / Incomplete / Credentials complete)
- Compact header with title + subtitle (nickname / Apple app id)
- Add/remove extra client in that layout
- Grid layout for responsive form fields

Keep all fixes from #311:
- Wholesale replace of apple on save (no lodash-merge of clients[])
- Inherit vs second team validation (all three creds or none)
- Unique trimmed nickname with errors on row
- Whitespace-only extra creds/redirect_uri → '' on submit
- Enabling Apple requires default clientId + privateKey + teamId + keyId
- Plain-language copy: 'nickname', 'Apple app ID', 'leave empty to reuse the default key'
- Extra creds stay optional in type
- Do NOT relabel extra id as 'Client ID (oauthClientId)' - keep the plain language

No changes to Microsoft or generic OAuth forms.
cursor Bot pushed a commit that referenced this pull request Sep 1, 2026
…th errors

1. Extra-client credential badge now ONLY counts the three creds (teamId, keyId,
   privateKey):
   - 0 filled = 'Reuses default' (inherits default credentials)
   - 3 filled = 'Second team' (has its own credentials)
   - 1-2 filled = 'Incomplete' (mixed credentials, blocked by validation)
   Do not include clientId/id/name in extra-client badge count since clientId is
   always required. Default client badge still uses all four fields.

2. After validation failure, auto-open collapsible sections that contain errors:
   - Default client section opens if it has validation errors
   - Extra client rows with errors (nickname/mixed creds) open automatically
   - Prevents hiding validation errors inside collapsed sections
   - Uses controlled Collapsible state with React state + useEffect

Keep all #311 fixes: wholesale apple replace, inherit/mixed/unique/keyId
validation, whitespace normalize.
@ChrisPdgn
ChrisPdgn marked this pull request as ready for review September 10, 2026 12:51
cursoragent and others added 5 commits September 10, 2026 15:53
- Add AppleExtraClient type with id, clientId, name, redirect_uri, privateKey, teamId, keyId
- Implement useFieldArray in AppleConfigForm for dynamic client management
- Add Zod validation for unique nicknames and all-or-nothing extra credentials
- Require default keyId when Apple authentication is enabled
- Fix lodash merge issue by replacing apple config wholesale on save
- Add ScrollArea to StrategySettings dialog for overflow handling
- Trim id and clientId fields on form submission
- Support inheriting default credentials or providing second Apple team credentials

Matches backend contract from Conduit #1547
…rrors on row

- Normalize whitespace-only privateKey/teamId/keyId/redirect_uri to empty string on submit
  to prevent backend treating '   ' as set (which would cause mixed-cred 400)
- Interior PEM whitespace in privateKey is preserved (only trim-empty normalization)
- Replace .refine with .superRefine for unique nickname validation to show errors on
  specific duplicate row's id field instead of at clients[] array level
- Operator now sees 'This nickname is already used' next to the offending Nickname field

Addresses review feedback from Dwight
…ntial badges

Steal UI patterns from #296 (kkopanidis feat/apple-multi-oauth-clients):
- Collapsible default-client and extra-client rows (collapsed by default)
- Credential completeness badge (Not configured / Incomplete / Credentials complete)
- Compact header with title + subtitle (nickname / Apple app id)
- Add/remove extra client in that layout
- Grid layout for responsive form fields

Keep all fixes from #311:
- Wholesale replace of apple on save (no lodash-merge of clients[])
- Inherit vs second team validation (all three creds or none)
- Unique trimmed nickname with errors on row
- Whitespace-only extra creds/redirect_uri → '' on submit
- Enabling Apple requires default clientId + privateKey + teamId + keyId
- Plain-language copy: 'nickname', 'Apple app ID', 'leave empty to reuse the default key'
- Extra creds stay optional in type
- Do NOT relabel extra id as 'Client ID (oauthClientId)' - keep the plain language

No changes to Microsoft or generic OAuth forms.
…th errors

1. Extra-client credential badge now ONLY counts the three creds (teamId, keyId,
   privateKey):
   - 0 filled = 'Reuses default' (inherits default credentials)
   - 3 filled = 'Second team' (has its own credentials)
   - 1-2 filled = 'Incomplete' (mixed credentials, blocked by validation)
   Do not include clientId/id/name in extra-client badge count since clientId is
   always required. Default client badge still uses all four fields.

2. After validation failure, auto-open collapsible sections that contain errors:
   - Default client section opens if it has validation errors
   - Extra client rows with errors (nickname/mixed creds) open automatically
   - Prevents hiding validation errors inside collapsed sections
   - Uses controlled Collapsible state with React state + useEffect

Keep all #311 fixes: wholesale apple replace, inherit/mixed/unique/keyId
validation, whitespace normalize.
Align default Apple clientId label to Apple app ID, scope the wide
dialog to Apple only, and match Forgot password redirect URI label
size to Local switches.
@ChrisPdgn
ChrisPdgn force-pushed the cursor/apple-extra-oauth-clients-5ded branch from 3c9ba18 to 98931f3 Compare September 10, 2026 12:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants