Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cmd/thv/app/proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ func proxyCmdFunc(cmd *cobra.Command, args []string) error {
return fmt.Errorf("invalid target URI: %w", err)
}

// Validate OAuth callback port availability
if err := networking.ValidateCallbackPort(
remoteAuthFlags.RemoteAuthCallbackPort,
remoteAuthFlags.RemoteAuthClientID,
); err != nil {
return err
}

// Select a port for the HTTP proxy (host port)
port, err := networking.FindOrUsePort(proxyPort)
if err != nil {
Expand Down
12 changes: 12 additions & 0 deletions cmd/thv/app/run_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -475,10 +475,22 @@ func buildRunnerConfig(

if remoteServerMetadata, ok := serverMetadata.(*registry.RemoteServerMetadata); ok {
remoteAuthConfig := getRemoteAuthFromRemoteServerMetadata(remoteServerMetadata)

// Validate OAuth callback port availability upfront for better user experience
if err := networking.ValidateCallbackPort(remoteAuthConfig.CallbackPort, remoteAuthConfig.ClientID); err != nil {
return nil, err
}

opts = append(opts, runner.WithRemoteAuth(remoteAuthConfig), runner.WithRemoteURL(remoteServerMetadata.URL))
}
if runFlags.RemoteURL != "" {
remoteAuthConfig := getRemoteAuthFromRunFlags(runFlags)

// Validate OAuth callback port availability upfront for better user experience
if err := networking.ValidateCallbackPort(remoteAuthConfig.CallbackPort, remoteAuthConfig.ClientID); err != nil {
return nil, err
}

opts = append(opts, runner.WithRemoteAuth(remoteAuthConfig))
}

Expand Down
26 changes: 26 additions & 0 deletions pkg/auth/discovery/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,32 @@ func PerformOAuthFlow(ctx context.Context, issuer string, config *OAuthFlowConfi
return nil, fmt.Errorf("OAuth flow config cannot be nil")
}

// Resolve port availability BEFORE dynamic registration
// This ensures we register the OAuth client with the same port we'll actually use

if shouldDynamicallyRegisterClient(config) {
// For dynamic registration, we can allow fallback to alternative ports
// since we can register the client with the actual port we'll use
port, err := networking.FindOrUsePort(config.CallbackPort)
if err != nil {
return nil, fmt.Errorf("failed to find available port: %w", err)
}

if port != config.CallbackPort {
logger.Warnf("Specified auth callback port %d is unavailable, using port %d instead", config.CallbackPort, port)
}
config.CallbackPort = port
} else {
// For pre-registered clients, use strict port checking
// The user likely configured this port in their IdP/app
if !networking.IsAvailable(config.CallbackPort) {
return nil, fmt.Errorf(
"specified auth callback port %d is not available - please choose a different port or ensure it's not in use",
config.CallbackPort,
)
}
}

// Handle dynamic client registration if needed
if shouldDynamicallyRegisterClient(config) {
if err := handleDynamicRegistration(ctx, issuer, config); err != nil {
Expand Down
Loading
Loading