mirror of
https://github.com/netbirdio/netbird.git
synced 2026-03-31 06:34:14 -04:00
Feature/resolve local jwks keys (#5073)
This commit is contained in:
@@ -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{}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user