[GH-ISSUE #5102] Deadend problem of "User Approval Pending" when I first times login #10524

Open
opened 2026-08-05 01:26:12 -04:00 by saavagebueno · 3 comments
Owner

Originally created by @itfinally on GitHub (Jan 14, 2026).
Original GitHub issue: https://github.com/netbirdio/netbird/issues/5102

Describe the problem

I am setting up my own NetBird server and using Zitadel as my IPD, and I have configured the relevant parameters according to the documentation.

This is the document I followed, 100% followed:

So, my server is running, and I login use my Zitadel account, then netbird server said "User Approval Pending
"

Here is the problem, I haven't even officially logged into the Netbird service yet, how can I conduct the audit? But if I don't audit, I can't log into the Netbird service.

That is a deadend, Is there any way to solve this?
I use auth0 before, but netbird always told me "invlid JWT token" when device login, so I use Zitadel as my idp, Emmm, the constant setbacks are really frustrating......

Image

This is my setup.env

## example file, you can copy this file to setup.env and update its values
##

# Image tags
# you can force specific tags for each component; will be set to latest if empty
NETBIRD_DASHBOARD_TAG=""
NETBIRD_SIGNAL_TAG=""
NETBIRD_MANAGEMENT_TAG=""
COTURN_TAG=""
NETBIRD_RELAY_TAG=""

# Dashboard domain. e.g. app.mydomain.com
NETBIRD_DOMAIN="<my_domain>"

# TURN server domain. e.g. turn.mydomain.com
# if not specified it will assume NETBIRD_DOMAIN
NETBIRD_TURN_DOMAIN=""

# TURN server public IP address
# required for a connection involving peers in
# the same network as the server and external peers
# usually matches the IP for the domain set in NETBIRD_TURN_DOMAIN
NETBIRD_TURN_EXTERNAL_IP=""

# -------------------------------------------
# OIDC
#  e.g., https://example.eu.auth0.com/.well-known/openid-configuration
# -------------------------------------------
NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="https://netbirdserver-ndz4sp.us1.zitadel.cloud/.well-known/openid-configuration"
# The default setting is to transmit the audience to the IDP during authorization. However,
# if your IDP does not have this capability, you can turn this off by setting it to false.
#NETBIRD_DASH_AUTH_USE_AUDIENCE=false
NETBIRD_AUTH_AUDIENCE="<Client_ID>"
# e.g. netbird-client
NETBIRD_AUTH_CLIENT_ID="<Client_ID>"
# indicates the scopes that will be requested to the IDP
NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api"
# NETBIRD_AUTH_CLIENT_SECRET is required only by Google workspace.
# NETBIRD_AUTH_CLIENT_SECRET=""
# if you want to use a custom claim for the user ID instead of 'sub', set it here
# NETBIRD_AUTH_USER_ID_CLAIM=""
# indicates whether to use Auth0 or not: true or false
NETBIRD_USE_AUTH0="false"
# if your IDP provider doesn't support fragmented URIs, configure custom
# redirect and silent redirect URIs, these will be concatenated into your NETBIRD_DOMAIN domain.
NETBIRD_AUTH_REDIRECT_URI="/auth"
NETBIRD_AUTH_SILENT_REDIRECT_URI="/silent-auth"
# Updates the preference to use id tokens instead of access token on dashboard
# Okta and Gitlab IDPs can benefit from this
# NETBIRD_TOKEN_SOURCE="idToken"
# -------------------------------------------
# OIDC Device Authorization Flow
# -------------------------------------------
NETBIRD_AUTH_DEVICE_AUTH_PROVIDER="hosted"
NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID="<Client_ID>"
# Some IDPs requires different audience, scopes and to use id token for device authorization flow
# you can customize here:
NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE="<Client_ID>"
NETBIRD_AUTH_DEVICE_AUTH_SCOPE="openid"
NETBIRD_AUTH_DEVICE_AUTH_USE_ID_TOKEN=false
# -------------------------------------------
# OIDC PKCE Authorization Flow
# -------------------------------------------
# Comma separated port numbers. if already in use, PKCE flow will choose an available port from the list as an alternative
# eg. 53000,54000
NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS="53000"
# -------------------------------------------
# IDP Management
# -------------------------------------------
# eg. zitadel, auth0, azure, keycloak
NETBIRD_MGMT_IDP="zitadel"
# Some IDPs requires different client id and client secret for management api
NETBIRD_IDP_MGMT_CLIENT_ID="netbird"
NETBIRD_IDP_MGMT_CLIENT_SECRET="<Client_Secret>"
NETBIRD_IDP_MGMT_EXTRA_MANAGEMENT_ENDPOINT="https://netbirdserver-ndz4sp.us1.zitadel.cloud/management/v1"
# Required when setting up with Keycloak "https://<YOUR_KEYCLOAK_HOST_AND_PORT>/admin/realms/netbird"
# NETBIRD_IDP_MGMT_EXTRA_ADMIN_ENDPOINT=
# With some IDPs may be needed enabling automatic refresh of signing keys on expire
NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=true
# NETBIRD_IDP_MGMT_EXTRA_ variables. See https://docs.netbird.io/selfhosted/identity-providers for more information about your IDP of choice.
# -------------------------------------------
# Letsencrypt
# -------------------------------------------
# Disable letsencrypt
#  if disabled, cannot use HTTPS anymore and requires setting up a reverse-proxy to do it instead
NETBIRD_DISABLE_LETSENCRYPT=true
# e.g. hello@mydomain.com
NETBIRD_LETSENCRYPT_EMAIL=""
# -------------------------------------------
# Extra settings
# -------------------------------------------
# Disable anonymous metrics collection, see more information at https://netbird.io/docs/FAQ/metrics-collection
NETBIRD_DISABLE_ANONYMOUS_METRICS=false
# DNS DOMAIN configures the domain name used for peer resolution. By default it is netbird.selfhosted
NETBIRD_MGMT_DNS_DOMAIN=netbird.selfhosted
# Disable default all-to-all policy for new accounts
NETBIRD_MGMT_DISABLE_DEFAULT_POLICY=false
# -------------------------------------------
# Relay settings
# -------------------------------------------
# Relay server domain. e.g. relay.mydomain.com
# if not specified it will assume NETBIRD_DOMAIN
NETBIRD_RELAY_DOMAIN=""

# Relay server connection port. If none is supplied
# it will default to 33080
# should be updated to match TLS-port of reverse proxy when netbird is running behind reverse proxy
NETBIRD_RELAY_PORT="443"

# Management API connecting port. If none is supplied
# it will default to 33073
# should be updated to match TLS-port of reverse proxy when netbird is running behind reverse proxy
NETBIRD_MGMT_API_PORT="443"

# Signal service connecting port. If none is supplied
# it will default to 10000
# should be updated to match TLS-port of reverse proxy when netbird is running behind reverse proxy
NETBIRD_SIGNAL_PORT="443"

To Reproduce

Steps to reproduce the behavior:

  1. create project and do any necessary operation on "zitadel.com"
  2. configure your setup.env file
  3. running netbird server
  4. use your zitadel account to login netbird server

Expected behavior

Are you using NetBird Cloud?

No

NetBird version

v0.62.3

Is any other VPN software installed?

No

Debug output

To help us resolve the problem, please attach the following anonymized status output

netbird status -dA

Create and upload a debug bundle, and share the returned file key:

netbird debug for 1m -AS -U

Uploaded files are automatically deleted after 30 days.

Alternatively, create the file only and attach it here manually:

netbird debug for 1m -AS

Screenshots

If applicable, add screenshots to help explain your problem.

Additional context

Add any other context about the problem here.

Have you tried these troubleshooting steps?

  • Reviewed client troubleshooting (if applicable)
  • Checked for newer NetBird versions
  • Searched for similar issues on GitHub (including closed ones)
  • Restarted the NetBird client
  • Disabled other VPN software
  • Checked firewall settings
Originally created by @itfinally on GitHub (Jan 14, 2026). Original GitHub issue: https://github.com/netbirdio/netbird/issues/5102 **Describe the problem** I am setting up my own NetBird server and using Zitadel as my IPD, and I have configured the relevant parameters according to the documentation. This is the document I followed, 100% followed: - https://docs.netbird.io/selfhosted/identity-providers/advanced/zitadel - https://docs.netbird.io/selfhosted/selfhosted-guide So, my server is running, and I login use my Zitadel account, then netbird server said "User Approval Pending " Here is the problem, I haven't even officially logged into the Netbird service yet, how can I conduct the audit? But if I don't audit, I can't log into the Netbird service. That is a deadend, Is there any way to solve this? I use auth0 before, but netbird always told me "invlid JWT token" when device login, so I use Zitadel as my idp, Emmm, the constant setbacks are really frustrating...... <img width="813" height="508" alt="Image" src="https://github.com/user-attachments/assets/e9df657e-0afb-403c-ada9-e2aee6eb46a4" /> This is my setup.env ``` ## example file, you can copy this file to setup.env and update its values ## # Image tags # you can force specific tags for each component; will be set to latest if empty NETBIRD_DASHBOARD_TAG="" NETBIRD_SIGNAL_TAG="" NETBIRD_MANAGEMENT_TAG="" COTURN_TAG="" NETBIRD_RELAY_TAG="" # Dashboard domain. e.g. app.mydomain.com NETBIRD_DOMAIN="<my_domain>" # TURN server domain. e.g. turn.mydomain.com # if not specified it will assume NETBIRD_DOMAIN NETBIRD_TURN_DOMAIN="" # TURN server public IP address # required for a connection involving peers in # the same network as the server and external peers # usually matches the IP for the domain set in NETBIRD_TURN_DOMAIN NETBIRD_TURN_EXTERNAL_IP="" # ------------------------------------------- # OIDC # e.g., https://example.eu.auth0.com/.well-known/openid-configuration # ------------------------------------------- NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="https://netbirdserver-ndz4sp.us1.zitadel.cloud/.well-known/openid-configuration" # The default setting is to transmit the audience to the IDP during authorization. However, # if your IDP does not have this capability, you can turn this off by setting it to false. #NETBIRD_DASH_AUTH_USE_AUDIENCE=false NETBIRD_AUTH_AUDIENCE="<Client_ID>" # e.g. netbird-client NETBIRD_AUTH_CLIENT_ID="<Client_ID>" # indicates the scopes that will be requested to the IDP NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api" # NETBIRD_AUTH_CLIENT_SECRET is required only by Google workspace. # NETBIRD_AUTH_CLIENT_SECRET="" # if you want to use a custom claim for the user ID instead of 'sub', set it here # NETBIRD_AUTH_USER_ID_CLAIM="" # indicates whether to use Auth0 or not: true or false NETBIRD_USE_AUTH0="false" # if your IDP provider doesn't support fragmented URIs, configure custom # redirect and silent redirect URIs, these will be concatenated into your NETBIRD_DOMAIN domain. NETBIRD_AUTH_REDIRECT_URI="/auth" NETBIRD_AUTH_SILENT_REDIRECT_URI="/silent-auth" # Updates the preference to use id tokens instead of access token on dashboard # Okta and Gitlab IDPs can benefit from this # NETBIRD_TOKEN_SOURCE="idToken" # ------------------------------------------- # OIDC Device Authorization Flow # ------------------------------------------- NETBIRD_AUTH_DEVICE_AUTH_PROVIDER="hosted" NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID="<Client_ID>" # Some IDPs requires different audience, scopes and to use id token for device authorization flow # you can customize here: NETBIRD_AUTH_DEVICE_AUTH_AUDIENCE="<Client_ID>" NETBIRD_AUTH_DEVICE_AUTH_SCOPE="openid" NETBIRD_AUTH_DEVICE_AUTH_USE_ID_TOKEN=false # ------------------------------------------- # OIDC PKCE Authorization Flow # ------------------------------------------- # Comma separated port numbers. if already in use, PKCE flow will choose an available port from the list as an alternative # eg. 53000,54000 NETBIRD_AUTH_PKCE_REDIRECT_URL_PORTS="53000" # ------------------------------------------- # IDP Management # ------------------------------------------- # eg. zitadel, auth0, azure, keycloak NETBIRD_MGMT_IDP="zitadel" # Some IDPs requires different client id and client secret for management api NETBIRD_IDP_MGMT_CLIENT_ID="netbird" NETBIRD_IDP_MGMT_CLIENT_SECRET="<Client_Secret>" NETBIRD_IDP_MGMT_EXTRA_MANAGEMENT_ENDPOINT="https://netbirdserver-ndz4sp.us1.zitadel.cloud/management/v1" # Required when setting up with Keycloak "https://<YOUR_KEYCLOAK_HOST_AND_PORT>/admin/realms/netbird" # NETBIRD_IDP_MGMT_EXTRA_ADMIN_ENDPOINT= # With some IDPs may be needed enabling automatic refresh of signing keys on expire NETBIRD_MGMT_IDP_SIGNKEY_REFRESH=true # NETBIRD_IDP_MGMT_EXTRA_ variables. See https://docs.netbird.io/selfhosted/identity-providers for more information about your IDP of choice. # ------------------------------------------- # Letsencrypt # ------------------------------------------- # Disable letsencrypt # if disabled, cannot use HTTPS anymore and requires setting up a reverse-proxy to do it instead NETBIRD_DISABLE_LETSENCRYPT=true # e.g. hello@mydomain.com NETBIRD_LETSENCRYPT_EMAIL="" # ------------------------------------------- # Extra settings # ------------------------------------------- # Disable anonymous metrics collection, see more information at https://netbird.io/docs/FAQ/metrics-collection NETBIRD_DISABLE_ANONYMOUS_METRICS=false # DNS DOMAIN configures the domain name used for peer resolution. By default it is netbird.selfhosted NETBIRD_MGMT_DNS_DOMAIN=netbird.selfhosted # Disable default all-to-all policy for new accounts NETBIRD_MGMT_DISABLE_DEFAULT_POLICY=false # ------------------------------------------- # Relay settings # ------------------------------------------- # Relay server domain. e.g. relay.mydomain.com # if not specified it will assume NETBIRD_DOMAIN NETBIRD_RELAY_DOMAIN="" # Relay server connection port. If none is supplied # it will default to 33080 # should be updated to match TLS-port of reverse proxy when netbird is running behind reverse proxy NETBIRD_RELAY_PORT="443" # Management API connecting port. If none is supplied # it will default to 33073 # should be updated to match TLS-port of reverse proxy when netbird is running behind reverse proxy NETBIRD_MGMT_API_PORT="443" # Signal service connecting port. If none is supplied # it will default to 10000 # should be updated to match TLS-port of reverse proxy when netbird is running behind reverse proxy NETBIRD_SIGNAL_PORT="443" ``` **To Reproduce** Steps to reproduce the behavior: 1. create project and do any necessary operation on "[zitadel.com](https://zitadel.com/)" 2. configure your setup.env file 3. running netbird server 4. use your zitadel account to login netbird server **Expected behavior** **Are you using NetBird Cloud?** No **NetBird version** v0.62.3 **Is any other VPN software installed?** No **Debug output** To help us resolve the problem, please attach the following anonymized status output netbird status -dA Create and upload a debug bundle, and share the returned file key: netbird debug for 1m -AS -U *Uploaded files are automatically deleted after 30 days.* Alternatively, create the file only and attach it here manually: netbird debug for 1m -AS **Screenshots** If applicable, add screenshots to help explain your problem. **Additional context** Add any other context about the problem here. **Have you tried these troubleshooting steps?** - [x] Reviewed [client troubleshooting](https://docs.netbird.io/how-to/troubleshooting-client) (if applicable) - [x] Checked for newer NetBird versions - [x] Searched for similar issues on GitHub (including closed ones) - [x] Restarted the NetBird client - [x] Disabled other VPN software - [x] Checked firewall settings
saavagebueno added the triage-needed label 2026-08-05 01:26:12 -04:00
Author
Owner

@xpufx commented on GitHub (Apr 19, 2026):

Same issue here. "User Approval Required" setting has no effect. There are a bunch of small bugs in the installer and the software on the latest release. Is there a more stable older release people have had more luck with?

@itfinally You can log in with the initial email based admin account and approve the users after they've tried and failed to log in.

EDIT: my second user was able to be approved automatically. This time I hit the url from the authentik dashboard though. Maybet that's the difference.

<!-- gh-comment-id:4275990538 --> @xpufx commented on GitHub (Apr 19, 2026): Same issue here. "User Approval Required" setting has no effect. There are a bunch of small bugs in the installer and the software on the latest release. Is there a more stable older release people have had more luck with? @itfinally You can log in with the initial email based admin account and approve the users after they've tried and failed to log in. EDIT: my second user was able to be approved automatically. This time I hit the url from the authentik dashboard though. Maybet that's the difference.
Author
Owner

@atomlab commented on GitHub (Apr 23, 2026):

I’m experiencing the same issue.

The last thing I did was migrate domains:

  • NetBird: netbird.old-domain.comnetbird.new-domain.com
  • Keycloak: auth.old-domain.comauth.new-domain.com

OIDC has been fully reconfigured with the new domains (issuer, redirect URLs, etc.).
Existing users can still authenticate and work without any issues.

However, new users, after successfully authenticating via Keycloak, get the following error:

User Approval Pending

At the same time, the User Approval Required option is disabled in NetBird settings.

In the NetBird server logs I see:

failed to validate user permissions: user is pending approval
HTTP response ... GET /api/users status 403

failed to validate user permissions: user is pending approval
HTTP response ... GET /api/users/current status 403

got a handler error: user is blocked

Important details:

  • the user successfully completes OIDC authentication (Keycloak)
  • the user is created in NetBird (there is a userID)
  • but gets pending status and then apparently becomes blocked

Releases:

  • Management: v0.69.0
  • Dashboard: v2.37.0
<!-- gh-comment-id:4305542700 --> @atomlab commented on GitHub (Apr 23, 2026): I’m experiencing the same issue. The last thing I did was migrate domains: * NetBird: `netbird.old-domain.com` → `netbird.new-domain.com` * Keycloak: `auth.old-domain.com` → `auth.new-domain.com` OIDC has been fully reconfigured with the new domains (issuer, redirect URLs, etc.). Existing users can still authenticate and work without any issues. However, new users, after successfully authenticating via Keycloak, get the following error: > **User Approval Pending** At the same time, the *User Approval Required* option is disabled in NetBird settings. In the NetBird server logs I see: ``` failed to validate user permissions: user is pending approval HTTP response ... GET /api/users status 403 failed to validate user permissions: user is pending approval HTTP response ... GET /api/users/current status 403 got a handler error: user is blocked ``` Important details: * the user successfully completes OIDC authentication (Keycloak) * the user is created in NetBird (there is a userID) * but gets `pending` status and then apparently becomes `blocked` --- Releases: - Management: v0.69.0 - Dashboard: v2.37.0
Author
Owner

@atomlab commented on GitHub (Apr 23, 2026):

Update (how I fixed it in my case):

The issue was caused by having two primary accounts with different domains after migration, which broke single-account mode behavior.

You can check this with:

SELECT id, domain, domain_category, is_domain_primary_account FROM accounts;

In my case:

d08gritj... | netbird.old.com | private | 1   ← old account
d7jr1o5j... | netbird.new.com  | private | 1   ← new auto-created account

And each had its own owner:

SELECT id, account_id, role FROM users WHERE role = 'owner';
... | d08gritj... | owner
... | d7jr1o5j... | owner

What happened:

After domain migration, NetBird created a new account for the new domain, and new users were being placed there instead of the original account.


What I did:

  1. Stopped Management service

  2. Removed the extra (new) account (netbird.new.com) and all related records (users, peers, groups, policies, etc.)

sqlite3 /opt/netbird/data/mgmt/store.db

Cascade deletion (order matters — dependencies first)

DELETE FROM policy_rules WHERE policy_id IN (SELECT id FROM policies WHERE account_id = 'd7jr1o5...');
DELETE FROM group_peers WHERE account_id = 'd7jr1o5...';
DELETE FROM policies WHERE account_id = 'd7jr1o5...';
DELETE FROM groups WHERE account_id = 'd7jr1o5...';
DELETE FROM peers WHERE account_id = 'd7jr1o5...';
DELETE FROM users WHERE account_id = 'd7jr1o5...';
DELETE FROM account_onboardings WHERE account_id = 'd7jr1o5...';
DELETE FROM accounts WHERE id = 'd7jr1o5...';
  1. Updated the original account to use the new domain:
UPDATE accounts SET domain = 'netbird.new.com' WHERE id = 'd08gritj...';
  1. Fixed missing onboarding state for the original account:
INSERT INTO account_onboardings (account_id, onboarding_flow_pending, signup_form_pending, created_at, updated_at)
VALUES ('d08gritj...', 0, 0, datetime('now'), datetime('now'));
  1. Started Management service

Result:

  • Only one primary account remains
  • Single-account mode works correctly again
  • New users are added to the existing account
  • No more “User Approval Pending” / unexpected account creation

This seems to happen after domain migration when NetBird creates a new primary account instead of reusing the existing one.

<!-- gh-comment-id:4306329101 --> @atomlab commented on GitHub (Apr 23, 2026): **Update (how I fixed it in my case):** The issue was caused by having **two primary accounts with different domains** after migration, which broke single-account mode behavior. You can check this with: ```sql SELECT id, domain, domain_category, is_domain_primary_account FROM accounts; ``` In my case: ``` d08gritj... | netbird.old.com | private | 1 ← old account d7jr1o5j... | netbird.new.com | private | 1 ← new auto-created account ``` And each had its own owner: ```sql SELECT id, account_id, role FROM users WHERE role = 'owner'; ``` ``` ... | d08gritj... | owner ... | d7jr1o5j... | owner ``` ### What happened: After domain migration, NetBird created a **new account for the new domain**, and new users were being placed there instead of the original account. --- ### What I did: 0. Stopped Management service 1. **Removed the extra (new) account** (`netbird.new.com`) and all related records (`users`, `peers`, `groups`, `policies`, etc.) ``` sqlite3 /opt/netbird/data/mgmt/store.db ``` Cascade deletion (order matters — dependencies first) ```sql DELETE FROM policy_rules WHERE policy_id IN (SELECT id FROM policies WHERE account_id = 'd7jr1o5...'); DELETE FROM group_peers WHERE account_id = 'd7jr1o5...'; DELETE FROM policies WHERE account_id = 'd7jr1o5...'; DELETE FROM groups WHERE account_id = 'd7jr1o5...'; DELETE FROM peers WHERE account_id = 'd7jr1o5...'; DELETE FROM users WHERE account_id = 'd7jr1o5...'; DELETE FROM account_onboardings WHERE account_id = 'd7jr1o5...'; DELETE FROM accounts WHERE id = 'd7jr1o5...'; ``` 2. Updated the original account to use the new domain: ```sql UPDATE accounts SET domain = 'netbird.new.com' WHERE id = 'd08gritj...'; ``` 3. Fixed missing onboarding state for the original account: ```sql INSERT INTO account_onboardings (account_id, onboarding_flow_pending, signup_form_pending, created_at, updated_at) VALUES ('d08gritj...', 0, 0, datetime('now'), datetime('now')); ``` 4. Started Management service --- ### Result: * Only one primary account remains * Single-account mode works correctly again * New users are added to the existing account * No more “User Approval Pending” / unexpected account creation --- This seems to happen after domain migration when NetBird creates a new primary account instead of reusing the existing one.
Sign in to join this conversation.
No Label triage-needed
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: DYNR/netbird#10524