From 4755a1815e2d84252ade0e1c27f2ffc937912534 Mon Sep 17 00:00:00 2001 From: bcmmbaga Date: Fri, 12 Jun 2026 19:25:21 +0300 Subject: [PATCH] skip JWT group checks for local Dex user --- management/server/auth/manager.go | 6 +++- management/server/auth/manager_test.go | 38 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/management/server/auth/manager.go b/management/server/auth/manager.go index 27346a604..9498789f2 100644 --- a/management/server/auth/manager.go +++ b/management/server/auth/manager.go @@ -12,6 +12,7 @@ import ( "github.com/netbirdio/netbird/shared/auth" "github.com/netbirdio/netbird/base62" + "github.com/netbirdio/netbird/idp/dex" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/types" nbjwt "github.com/netbirdio/netbird/shared/auth/jwt" @@ -74,7 +75,10 @@ func (m *manager) ValidateAndParseToken(ctx context.Context, value string) (auth } func (m *manager) EnsureUserAccessByJWTGroups(ctx context.Context, userAuth auth.UserAuth, token *jwt.Token) (auth.UserAuth, error) { - if userAuth.IsChild || userAuth.IsPAT { + // Child accounts and PAT-authenticated requests do not use JWT group access checks. + // Embedded-Dex local users also skip them because local password authentication + // does not provide external IdP group claims. + if userAuth.IsChild || userAuth.IsPAT || dex.IsLocalUserID(userAuth.UserId) { return userAuth, nil } diff --git a/management/server/auth/manager_test.go b/management/server/auth/manager_test.go index 469737f47..af8a30ef1 100644 --- a/management/server/auth/manager_test.go +++ b/management/server/auth/manager_test.go @@ -16,6 +16,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/netbirdio/netbird/idp/dex" "github.com/netbirdio/netbird/management/server/auth" "github.com/netbirdio/netbird/management/server/store" "github.com/netbirdio/netbird/management/server/types" @@ -206,6 +207,43 @@ func TestAuthManager_EnsureUserAccessByJWTGroups(t *testing.T) { _, err = manager.EnsureUserAccessByJWTGroups(context.Background(), userAuth, token) require.Error(t, err, "ensure user access is not in allowed groups") }) + + t.Run("Local embedded-Dex user is exempt from JWT allow-groups", func(t *testing.T) { + account.Settings.JWTGroupsEnabled = true + account.Settings.JWTGroupsClaimName = "idp-groups" + account.Settings.JWTAllowGroups = []string{"not-a-group"} + err := store.SaveAccount(context.Background(), account) + require.NoError(t, err, "save account failed") + + // Local Dex users have a "local" connector encoded in their user ID. + localUserAuth := nbauth.UserAuth{ + AccountId: account.Id, + Domain: domain, + UserId: dex.EncodeDexUserID("local-owner", "local"), + } + + localUserAuth, err = manager.EnsureUserAccessByJWTGroups(context.Background(), localUserAuth, token) + require.NoError(t, err, "local user must not be locked out by JWT allow-groups (issue #5337)") + require.Len(t, localUserAuth.Groups, 0, "JWT groups must not be evaluated for local users") + }) + + t.Run("Federated embedded-Dex user is still subject to JWT allow-groups", func(t *testing.T) { + account.Settings.JWTGroupsEnabled = true + account.Settings.JWTGroupsClaimName = "idp-groups" + account.Settings.JWTAllowGroups = []string{"not-a-group"} + err := store.SaveAccount(context.Background(), account) + require.NoError(t, err, "save account failed") + + // A federated user (non-"local" connector) must remain restricted. + fedUserAuth := nbauth.UserAuth{ + AccountId: account.Id, + Domain: domain, + UserId: dex.EncodeDexUserID("entra-user", "entra"), + } + + _, err = manager.EnsureUserAccessByJWTGroups(context.Background(), fedUserAuth, token) + require.Error(t, err, "federated user must still be restricted by JWT allow-groups") + }) } func TestAuthManager_ValidateAndParseToken(t *testing.T) {