Feature/resolve local jwks keys (#5073)

This commit is contained in:
Misha Bragin
2026-01-09 09:41:27 -05:00
committed by GitHub
parent 684fc0d2a2
commit f7967f9ae3
6 changed files with 217 additions and 3 deletions

View File

@@ -190,6 +190,9 @@ func applyEmbeddedIdPConfig(cfg *nbconfig.Config) error {
// Enable user deletion from IDP by default if EmbeddedIdP is enabled
userDeleteFromIDPEnabled = true
// Set LocalAddress for embedded IdP if enabled, used for internal JWT validation
cfg.EmbeddedIdP.LocalAddress = fmt.Sprintf("localhost:%d", mgmtPort)
// Ensure HttpConfig exists
if cfg.HttpConfig == nil {
cfg.HttpConfig = &nbconfig.HttpServerConfig{}

View File

@@ -68,7 +68,8 @@ func (s *BaseServer) AuthManager() auth.Manager {
if len(audiences) > 0 {
audience = audiences[0] // Use the first client ID as the primary audience
}
keysLocation = oauthProvider.GetKeysLocation()
// Use localhost keys location for internal validation (management has embedded Dex)
keysLocation = oauthProvider.GetLocalKeysLocation()
signingKeyRefreshEnabled = true
issuer = oauthProvider.GetIssuer()
userIDClaim = oauthProvider.GetUserIDClaim()

View File

@@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net/http"
"strings"
"github.com/dexidp/dex/storage"
"github.com/google/uuid"
@@ -27,8 +28,11 @@ const (
type EmbeddedIdPConfig struct {
// Enabled indicates whether the embedded IDP is enabled
Enabled bool
// Issuer is the OIDC issuer URL (e.g., "http://localhost:3002/oauth2")
// Issuer is the OIDC issuer URL (e.g., "https://management.netbird.io/oauth2")
Issuer string
// LocalAddress is the management server's local listen address (e.g., ":8080" or "localhost:8080")
// Used for internal JWT validation to avoid external network calls
LocalAddress string
// Storage configuration for the IdP database
Storage EmbeddedStorageConfig
// DashboardRedirectURIs are the OAuth2 redirect URIs for the dashboard client
@@ -146,7 +150,12 @@ var _ OAuthConfigProvider = (*EmbeddedIdPManager)(nil)
// OAuthConfigProvider defines the interface for OAuth configuration needed by auth flows.
type OAuthConfigProvider interface {
GetIssuer() string
// GetKeysLocation returns the public JWKS endpoint URL (uses external issuer URL)
GetKeysLocation() string
// GetLocalKeysLocation returns the localhost JWKS endpoint URL for internal use.
// Management server has embedded Dex and can validate tokens via localhost,
// avoiding external network calls and DNS resolution issues during startup.
GetLocalKeysLocation() string
GetClientIDs() []string
GetUserIDClaim() string
GetTokenEndpoint() string
@@ -500,6 +509,22 @@ func (m *EmbeddedIdPManager) GetKeysLocation() string {
return m.provider.GetKeysLocation()
}
// GetLocalKeysLocation returns the localhost JWKS endpoint URL for internal token validation.
// Uses the LocalAddress from config (management server's listen address) since embedded Dex
// is served by the management HTTP server, not a standalone Dex server.
func (m *EmbeddedIdPManager) GetLocalKeysLocation() string {
addr := m.config.LocalAddress
if addr == "" {
return ""
}
// Construct localhost URL from listen address
// addr is in format ":port" or "host:port" or "localhost:port"
if strings.HasPrefix(addr, ":") {
return fmt.Sprintf("http://localhost%s/oauth2/keys", addr)
}
return fmt.Sprintf("http://%s/oauth2/keys", addr)
}
// GetClientIDs returns the OAuth2 client IDs configured for this provider.
func (m *EmbeddedIdPManager) GetClientIDs() []string {
return []string{staticClientDashboard, staticClientCLI}

View File

@@ -247,3 +247,61 @@ func TestEmbeddedIdPManager_UserIDFormat_MatchesJWT(t *testing.T) {
t.Logf(" Raw UUID: %s", rawUserID)
t.Logf(" Connector: %s", connectorID)
}
func TestEmbeddedIdPManager_GetLocalKeysLocation(t *testing.T) {
ctx := context.Background()
tmpDir, err := os.MkdirTemp("", "embedded-idp-test-*")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)
tests := []struct {
name string
localAddress string
expected string
}{
{
name: "localhost with port",
localAddress: "localhost:8080",
expected: "http://localhost:8080/oauth2/keys",
},
{
name: "localhost with https port",
localAddress: "localhost:443",
expected: "http://localhost:443/oauth2/keys",
},
{
name: "port only format",
localAddress: ":8080",
expected: "http://localhost:8080/oauth2/keys",
},
{
name: "empty address",
localAddress: "",
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
config := &EmbeddedIdPConfig{
Enabled: true,
Issuer: "http://localhost:5556/dex",
LocalAddress: tt.localAddress,
Storage: EmbeddedStorageConfig{
Type: "sqlite3",
Config: EmbeddedStorageTypeConfig{
File: filepath.Join(tmpDir, "dex-"+tt.name+".db"),
},
},
}
manager, err := NewEmbeddedIdPManager(ctx, config, nil)
require.NoError(t, err)
defer func() { _ = manager.Stop(ctx) }()
result := manager.GetLocalKeysLocation()
assert.Equal(t, tt.expected, result)
})
}
}