[GH-ISSUE #5947] Combined server with embedded Dex never populates DeviceAuthorizationFlow/PKCEAuthorizationFlow — netbird ssh SSO broken #12472

Open
opened 2026-08-05 02:05:58 -04:00 by saavagebueno · 0 comments
Owner

Originally created by @renne on GitHub (Apr 21, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/5947

Describe the problem

On the combined server (netbirdio/netbird-server) in v0.69.0 with the embedded Dex IdP (no external idpManagerConfig), the management server never populates DeviceAuthorizationFlow or PKCEAuthorizationFlow in SyncResponse/LoginResponse to clients. As a result, netbird ssh SSO fails immediately: the client prints an empty verification URL and then errors with:

Error: dial <peer>:22: request JWT token: wait for JWT token: rpc error: code = InvalidArgument desc = invalid device code or no active auth flow

Inspection of the client profile after netbird login confirms:

$ sudo jq '.DeviceAuthorizationFlow' /var/lib/netbird/default.json
null

Dex itself is healthy and exposes a working /oauth2/device/code:

$ curl -sk https://netbird.example.com/oauth2/.well-known/openid-configuration | jq .device_authorization_endpoint
"https://netbird.example.com/oauth2/device/code"
$ curl -sk -X POST https://netbird.example.com/oauth2/device/code -d 'client_id=netbird-cli&scope=openid profile email'
{"device_code":"…","user_code":"…","verification_uri":"…","verification_uri_complete":"…",…}

So the server-side OIDC provider works; the management server just never tells clients about it.

Root cause (source analysis)

In the separate-management deployment, management/cmd/management.go has ApplyDeviceAuthFlowConfig which populates cfg.DeviceAuthorizationFlow from the legacy management.json fields.

In the combined server, combined/cmd/config.go (ToManagementConfig, ~lines 610–700 at v0.69.0) builds nbconfig.Config but never sets DeviceAuthorizationFlow or PKCEAuthorizationFlow. The embedded-Dex wiring (ApplyEmbeddedIdPConfig) only fills HttpConfig fields (issuer, audience, keys location, callback URL) — it does not derive the flow structs.

Downstream, management/internals/shared/grpc/server.go reads s.config.DeviceAuthorizationFlow and s.config.PKCEAuthorizationFlow when building toPeerConfig / ToSyncResponse. With both nil, the client's IsSSOSupported path in client/internal/auth/auth.go receives NotFound/nil responses and falls through to an unusable state — the SSH proxy code path that calls request JWT token has nowhere to redirect the user.

To Reproduce

  1. Deploy the combined server with the embedded Dex IdP (i.e., follow the v0.69.0 "getting-started" flow without configuring an external idpManagerConfig / legacy DeviceAuthorizationFlow).
  2. Enrol a client peer.
  3. Enable SSH on the target peer (netbird up --allow-server-ssh or equivalent) and run:
    netbird ssh --no-cache root@<peer>.<domain>
    
  4. Observe that the printed verification URL line is blank and the command fails with the gRPC InvalidArgument: invalid device code or no active auth flow error.
  5. Verify: sudo jq '.DeviceAuthorizationFlow, .PKCEAuthorizationFlow' /var/lib/netbird/default.json → both null.

Expected behaviour

When embedded Dex is enabled, the combined server should auto-populate DeviceAuthorizationFlow (and/or PKCEAuthorizationFlow) in the management config it builds, using:

  • Provider: hosted
  • ClientID: netbird-cli
  • Audience: netbird-cli
  • Scope: openid profile email offline_access
  • DeviceAuthEndpoint: <issuer>/device/code
  • TokenEndpoint: <issuer>/token
  • AuthorizationEndpoint: <issuer>/auth
  • For PKCE: RedirectURLs from server.auth.cliRedirectURIs

…so that netbird ssh, netbird login, and dashboard CLI flows work out of the box, as they did in the split-deployment with the legacy management.json that infrastructure_files/getting-started-with-dex.sh generates.

Are you using NetBird Cloud?

Self-hosted.

NetBird version

Server: netbirdio/netbird-server:latest reporting management server version 0.69.0.
Client: netbird version0.69.0 on aspire (Linux).

Is any other VPN software installed?

No.

Debug output

Key client state (other fields redacted):

$ sudo jq '{ManagementURL: .ManagementURL.Host, DeviceAuthorizationFlow, PKCEAuthorizationFlow, SSHJWTCacheTTL}' /var/lib/netbird/default.json
{
  "ManagementURL": "netbird.example.com:443",
  "DeviceAuthorizationFlow": null,
  "PKCEAuthorizationFlow": null,
  "SSHJWTCacheTTL": 2592000
}

Server config.yaml (relevant section):

server:
  exposedAddress: "https://netbird.example.com:443"
  auth:
    issuer: "https://netbird.example.com/oauth2"
    signKeyRefreshEnabled: true
    dashboardRedirectURIs:
      - "https://netbird.example.com/nb-auth"
      - "https://netbird.example.com/nb-silent-auth"
    cliRedirectURIs:
      - "http://localhost:53000/"

The combined example at combined/config.yaml.example provides no stanza for DeviceAuthorizationFlow or PKCEAuthorizationFlow, and ManagementConfig in combined/cmd/config.go has no YAML-exposed field for either, so operators cannot work around this by configuring them manually.

Additional context

  • Workaround investigated: there is no user-facing workaround short of patching the binary or reverting to the split management deployment with the legacy management.json (where infrastructure_files/getting-started-with-dex.sh explicitly writes the DeviceAuthorizationFlow JSON block).
  • The matching piece already exists server-side (management/cmd/management.go: ApplyDeviceAuthFlowConfig) — it just isn't invoked from the combined code path.
  • Source references (at tag v0.69.0):
    • combined/cmd/config.goToManagementConfig / ApplyEmbeddedIdPConfig (missing the flow wiring)
    • management/cmd/management.goApplyDeviceAuthFlowConfig (the analogous function for the split deployment)
    • management/internals/shared/grpc/server.go — consumers: toPeerConfig, ToSyncResponse

Have you tried these troubleshooting steps?

  • Reviewed client troubleshooting
  • Checked for newer NetBird versions (on 0.69.0, latest tagged)
  • Searched for similar issues on GitHub (including closed ones)
  • Restarted the NetBird client
  • Disabled other VPN software (none present)
  • Checked firewall settings
Originally created by @renne on GitHub (Apr 21, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/5947 **Describe the problem** On the combined server (`netbirdio/netbird-server`) in v0.69.0 with the **embedded Dex IdP** (no external `idpManagerConfig`), the management server never populates `DeviceAuthorizationFlow` or `PKCEAuthorizationFlow` in `SyncResponse`/`LoginResponse` to clients. As a result, `netbird ssh` SSO fails immediately: the client prints an **empty** verification URL and then errors with: ``` Error: dial <peer>:22: request JWT token: wait for JWT token: rpc error: code = InvalidArgument desc = invalid device code or no active auth flow ``` Inspection of the client profile after `netbird login` confirms: ``` $ sudo jq '.DeviceAuthorizationFlow' /var/lib/netbird/default.json null ``` Dex itself is healthy and exposes a working `/oauth2/device/code`: ``` $ curl -sk https://netbird.example.com/oauth2/.well-known/openid-configuration | jq .device_authorization_endpoint "https://netbird.example.com/oauth2/device/code" $ curl -sk -X POST https://netbird.example.com/oauth2/device/code -d 'client_id=netbird-cli&scope=openid profile email' {"device_code":"…","user_code":"…","verification_uri":"…","verification_uri_complete":"…",…} ``` So the server-side OIDC provider works; the management server just never tells clients about it. **Root cause (source analysis)** In the separate-management deployment, `management/cmd/management.go` has `ApplyDeviceAuthFlowConfig` which populates `cfg.DeviceAuthorizationFlow` from the legacy `management.json` fields. In the combined server, `combined/cmd/config.go` (`ToManagementConfig`, ~lines 610–700 at v0.69.0) builds `nbconfig.Config` but never sets `DeviceAuthorizationFlow` **or** `PKCEAuthorizationFlow`. The embedded-Dex wiring (`ApplyEmbeddedIdPConfig`) only fills `HttpConfig` fields (issuer, audience, keys location, callback URL) — it does not derive the flow structs. Downstream, `management/internals/shared/grpc/server.go` reads `s.config.DeviceAuthorizationFlow` and `s.config.PKCEAuthorizationFlow` when building `toPeerConfig` / `ToSyncResponse`. With both nil, the client's `IsSSOSupported` path in `client/internal/auth/auth.go` receives `NotFound`/nil responses and falls through to an unusable state — the SSH proxy code path that calls `request JWT token` has nowhere to redirect the user. **To Reproduce** 1. Deploy the combined server with the embedded Dex IdP (i.e., follow the v0.69.0 "getting-started" flow without configuring an external `idpManagerConfig` / legacy `DeviceAuthorizationFlow`). 2. Enrol a client peer. 3. Enable SSH on the target peer (`netbird up --allow-server-ssh` or equivalent) and run: ``` netbird ssh --no-cache root@<peer>.<domain> ``` 4. Observe that the printed verification URL line is blank and the command fails with the gRPC `InvalidArgument: invalid device code or no active auth flow` error. 5. Verify: `sudo jq '.DeviceAuthorizationFlow, .PKCEAuthorizationFlow' /var/lib/netbird/default.json` → both `null`. **Expected behaviour** When embedded Dex is enabled, the combined server should auto-populate `DeviceAuthorizationFlow` (and/or `PKCEAuthorizationFlow`) in the management config it builds, using: - `Provider: hosted` - `ClientID: netbird-cli` - `Audience: netbird-cli` - `Scope: openid profile email offline_access` - `DeviceAuthEndpoint: <issuer>/device/code` - `TokenEndpoint: <issuer>/token` - `AuthorizationEndpoint: <issuer>/auth` - For PKCE: `RedirectURLs` from `server.auth.cliRedirectURIs` …so that `netbird ssh`, `netbird login`, and dashboard CLI flows work out of the box, as they did in the split-deployment with the legacy `management.json` that `infrastructure_files/getting-started-with-dex.sh` generates. **Are you using NetBird Cloud?** Self-hosted. **NetBird version** Server: `netbirdio/netbird-server:latest` reporting `management server version 0.69.0`. Client: `netbird version` → `0.69.0` on aspire (Linux). **Is any other VPN software installed?** No. **Debug output** Key client state (other fields redacted): ``` $ sudo jq '{ManagementURL: .ManagementURL.Host, DeviceAuthorizationFlow, PKCEAuthorizationFlow, SSHJWTCacheTTL}' /var/lib/netbird/default.json { "ManagementURL": "netbird.example.com:443", "DeviceAuthorizationFlow": null, "PKCEAuthorizationFlow": null, "SSHJWTCacheTTL": 2592000 } ``` Server `config.yaml` (relevant section): ```yaml server: exposedAddress: "https://netbird.example.com:443" auth: issuer: "https://netbird.example.com/oauth2" signKeyRefreshEnabled: true dashboardRedirectURIs: - "https://netbird.example.com/nb-auth" - "https://netbird.example.com/nb-silent-auth" cliRedirectURIs: - "http://localhost:53000/" ``` The combined example at `combined/config.yaml.example` provides no stanza for DeviceAuthorizationFlow or PKCEAuthorizationFlow, and `ManagementConfig` in `combined/cmd/config.go` has no YAML-exposed field for either, so operators cannot work around this by configuring them manually. **Additional context** - Workaround investigated: there is no user-facing workaround short of patching the binary or reverting to the split management deployment with the legacy `management.json` (where `infrastructure_files/getting-started-with-dex.sh` explicitly writes the `DeviceAuthorizationFlow` JSON block). - The matching piece already exists server-side (`management/cmd/management.go: ApplyDeviceAuthFlowConfig`) — it just isn't invoked from the combined code path. - Source references (at tag `v0.69.0`): - `combined/cmd/config.go` — `ToManagementConfig` / `ApplyEmbeddedIdPConfig` (missing the flow wiring) - `management/cmd/management.go` — `ApplyDeviceAuthFlowConfig` (the analogous function for the split deployment) - `management/internals/shared/grpc/server.go` — consumers: `toPeerConfig`, `ToSyncResponse` **Have you tried these troubleshooting steps?** - [x] Reviewed [client troubleshooting](https://docs.netbird.io/how-to/troubleshooting-client) - [x] Checked for newer NetBird versions (on `0.69.0`, latest tagged) - [x] Searched for similar issues on GitHub (including closed ones) - [x] Restarted the NetBird client - [x] Disabled other VPN software (none present) - [x] Checked firewall settings
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#12472