Skip to content

Disable local users for a smooth single-idp mode#5226

Merged
braginini merged 8 commits intomainfrom
feature/disable-local-users
Feb 1, 2026
Merged

Disable local users for a smooth single-idp mode#5226
braginini merged 8 commits intomainfrom
feature/disable-local-users

Conversation

@braginini
Copy link
Copy Markdown
Collaborator

@braginini braginini commented Jan 31, 2026

Describe your changes

Add LocalAuthDisabled option to embedded IdP configuration

This adds the ability to disable local (email/password) authentication when using the embedded Dex identity provider. When disabled, users can only authenticate via external
identity providers (Google, OIDC, etc.).

This simplifies user login when there is only one external IdP configured. The login page will redirect directly to the IdP login page.

Key changes:

  • Added LocalAuthDisabled field to EmbeddedIdPConfig
  • Added methods to check and toggle local auth: IsLocalAuthEnabled, HasNonLocalConnectors, DisableLocalAuth, EnableLocalAuth
  • Validation prevents disabling local auth if no external connectors are configured
  • Existing local users are preserved when disabled and can login again when re-enabled
  • Operations are idempotent (disabling already disabled is a no-op)

Issue ticket number and link

Stack

Checklist

  • Is it a bug fix
  • Is a typo/documentation fix
  • Is a feature enhancement
  • It is a refactor
  • Created tests that fail without the change (if possible)

By submitting this pull request, you confirm that you have read and agree to the terms of the Contributor License Agreement.

Documentation

Select exactly one:

Docs PR URL (required if "docs added" is checked)

Paste the PR link from https://github.com/netbirdio/docs here:

https://github.com/netbirdio/docs/pull/__

Summary by CodeRabbit

  • New Features

    • Runtime/config option to disable local (email/password) authentication; surfaces local_auth_disabled in account settings/API and UI.
  • Behavior / Safety

    • Prevent disabling local auth unless another identity connector exists; startup and account flows block local user creation/invite when disabled.
    • Explicit enable/disable workflows for local auth and settings propagation to runtime configuration.
  • Tests

    • New tests covering startup, disabled-state behavior, API responses, and edge cases.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 31, 2026

📝 Walkthrough

Walkthrough

Adds runtime control to disable/enable local (email/password) auth: Provider gains HasNonLocalConnectors/DisableLocalAuth/EnableLocalAuth (IsLocalAuthEnabled removed); EmbeddedIdPConfig gains LocalAuthDisabled; settings, API, handlers, and user flows are updated to propagate and enforce this flag.

Changes

Cohort / File(s) Summary
IDP provider — connector control
idp/dex/connector.go
Removed IsLocalAuthEnabled; added HasNonLocalConnectors(ctx) (bool, error), DisableLocalAuth(ctx) error, EnableLocalAuth(ctx) error. New methods list connectors, validate preconditions, and create/delete the local connector via existing helpers.
Embedded IdP manager & tests
management/server/idp/embedded.go, management/server/idp/embedded_test.go
Added LocalAuthDisabled to EmbeddedIdPConfig; manager validates startup (requires another connector before disabling local auth), calls provider to disable local auth, exposes IsLocalAuthDisabled() and HasNonLocalConnectors(ctx). Tests cover startup and user-creation behavior.
Settings plumbing & runtime flags
management/internals/server/modules.go, management/server/settings/manager.go, management/server/types/settings.go, management/server/http/testing/testing_tools/channel/channel.go
Introduced settings.IdpConfig{EmbeddedIdpEnabled, LocalAuthDisabled}; threaded into settings.NewManager and stored on manager; GetSettings now surfaces both flags. Call sites updated to pass the new param.
Accounts endpoints & API model
management/server/http/handler.go, management/server/http/handlers/accounts/..., management/server/http/handlers/accounts/accounts_handler_test.go, shared/management/http/api/openapi.yml, shared/management/http/api/types.gen.go
Dropped embeddedIdpEnabled parameter from handler wiring; toAccountResponse now reads EmbeddedIdpEnabled and LocalAuthDisabled from settings. OpenAPI and API types add local_auth_disabled / LocalAuthDisabled. Tests adjusted.
User creation & invites guards
management/server/user.go, management/server/http/handlers/users/invites_handler_test.go
Added precondition checks to block embedded IdP user creation, invite creation, and invite acceptance when LocalAuthDisabled is set (returns HTTP 412). Tests added/updated.
Call-site and helper adjustments
management/server/account.go, management/internals/server/modules.go, management/server/http/testing/testing_tools/channel/channel.go
Added IsLocalAuthDisabled(ctx, idp.Manager) bool helper and updated managers to pass the new settings.IdpConfig{} to NewManager. Minor import/wiring changes.
Minor logging/test tweaks
management/server/http/handlers/instance/instance_handler.go, various tests
Small logging additions and test updates aligning with new flags and behaviors.

Sequence Diagram(s)

sequenceDiagram
    actor Operator
    participant HTTP as "HTTP Start"
    participant Settings as "Settings.Manager"
    participant IdP as "EmbeddedIdPManager"
    participant Provider as "Provider (dex)"

    Operator->>HTTP: Start with LocalAuthDisabled=true
    HTTP->>Settings: NewManager(..., idpConfig{LocalAuthDisabled:true})
    HTTP->>IdP: NewEmbeddedIdPManager(config)
    IdP->>Provider: HasNonLocalConnectors(ctx)?
    Provider-->>IdP: (true / false)
    alt no non-local connectors
        IdP-->>HTTP: error — cannot disable local auth without other connectors
    else non-local connectors exist
        IdP->>Provider: DisableLocalAuth(ctx)
        Provider->>Provider: list connectors, validate, delete local connector
        Provider-->>IdP: success
        IdP-->>HTTP: started with local auth disabled
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • mlsmaycon

Poem

🐰
I nibble code at break of dawn,
Toggles set and connectors drawn.
Disable here, enable there,
Count your connectors with care.
Hop, ship, and watch the meadow yawn.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.44% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main feature: disabling local users to enable single-IdP mode simplification. It directly reflects the core functionality added in the changeset.
Description check ✅ Passed The description includes detailed change summary, feature enhancement checkbox marked, and documentation PR link provided. All major template requirements are satisfied.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/disable-local-users

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@braginini braginini requested review from mlsmaycon, pascal-fischer and shuuri-labs and removed request for pascal-fischer January 31, 2026 13:31
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@idp/dex/connector.go`:
- Around line 349-385: The GetConnector call in IsLocalAuthEnabled and
DisableLocalAuth must verify the returned connector's Type to avoid an ID
collision with non-local providers: after calling p.storage.GetConnector(ctx,
"local") inspect the returned connector (e.g., conn.Type or connector.Type) and
ensure it equals the local connector type (use the project’s canonical local
type string/const, e.g., "local" or ConnectorTypeLocal); in IsLocalAuthEnabled
return false if the connector exists but is not of local type, and in
DisableLocalAuth refuse to delete and return a clear error if the found
connector is not of local type (so you don’t remove a non-local
provider)—alternatively enforce the same check at CreateConnector to prevent
creating non-local connectors with ID "local".
🧹 Nitpick comments (1)
idp/dex/connector.go (1)

330-345: Consider downgrading per-connector logs to Debug.

HasNonLocalConnectors logs each connector at Info level, which can be noisy and may leak connector names into standard logs. Consider Debug level or logging only counts.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@management/server/idp/embedded.go`:
- Around line 593-604: The runtime enable/disable functions DisableLocalAuth and
EnableLocalAuth on EmbeddedIdPManager call through to m.provider but do not
update the stored flag m.config.LocalAuthDisabled, causing IsLocalAuthDisabled
to report stale state; after a successful call to
m.provider.DisableLocalAuth(ctx) set m.config.LocalAuthDisabled = true, and
after a successful call to m.provider.EnableLocalAuth(ctx) set
m.config.LocalAuthDisabled = false (ensure you only update the config when the
provider call returns nil error and preserve existing error return behavior).

@netbirdio netbirdio deleted a comment from coderabbitai bot Jan 31, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@management/server/http/handlers/instance/instance_handler.go`:
- Line 49: The log message uses a debug marker "->>>>>>:"; update the call to
log.WithContext(r.Context()).Infof that references setupRequired to remove the
debug markers and follow project style (e.g., change the format string to
"instance setup status: %v") or remove the log statement entirely if redundant;
locate the usage of log.WithContext(r.Context()).Infof and replace the message
accordingly.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@management/server/idp/embedded.go`:
- Line 202: The current debug log in embedded.go uses
log.WithContext(ctx).Debugf("initializing embedded Dex IDP with config: %+v",
config) which will print sensitive fields like Owner.Hash; replace this with a
safe log that omits or redacts secrets—e.g., build and log a sanitized view of
the config (only safe fields or a copy with Owner.Hash and other secret fields
set to "<redacted>") or explicitly list non-sensitive fields to include; update
the call site using the log.WithContext(ctx).Debugf invocation and the config
variable so no raw credential material is ever formatted into logs.

In `@management/server/instance/manager.go`:
- Around line 106-123: In DefaultManager.loadSetupRequired, detect when local
auth is disabled and avoid marking setupRequired true when there are no store
accounts and no local users; specifically, after calling
m.store.GetAccountsCounter and m.embeddedIdpManager.GetAllAccounts,
short‑circuit and set m.setupRequired = false if the manager/config flag for
LocalAuthDisabled is enabled (e.g., m.LocalAuthDisabled or
m.options.LocalAuthDisabled), so CreateOwnerUser (which relies on local user
creation) won't be required; keep locking via m.setupMu around writes to
m.setupRequired and ensure the method still returns errors from the store or
embeddedIdpManager calls.
🧹 Nitpick comments (1)
management/server/idp/embedded.go (1)

312-324: Consider de-duplicating the debug messages.

Both logs use the same wording; the second could be clarified to avoid confusion.

♻️ Suggested wording tweak
-	log.WithContext(ctx).Debugf("retrieved %d users from embedded IdP", len(indexedUsers[UnsetAccountID]))
+	log.WithContext(ctx).Debugf("indexed %d users under UnsetAccountID in embedded IdP", len(indexedUsers[UnsetAccountID]))

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Feb 1, 2026

@braginini braginini merged commit 3a0cf23 into main Feb 1, 2026
41 checks passed
@braginini braginini deleted the feature/disable-local-users branch February 1, 2026 13:26
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.

1 participant