netbirdio/netbird · error

PKCE authorization flow failed: %v

Error message

PKCE authorization flow failed: %v

What it means

Top-level wrapper emitted by the local callback HTTP handler inside the PKCE Authorization Code flow. It fires when handleRequest fails at any step after the IdP redirects back to the localhost redirect URL: the IdP reported an OAuth error, the state parameter mismatched, the authorization code was missing, or the token exchange at the TokenEndpoint failed. The %v carries the specific underlying cause.

Source

Thrown at client/internal/auth/pkce_flow.go:242

	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		log.Infof("pkce flow: received authorization callback from IdP")
		cert := p.providerConfig.ClientCertPair
		if cert != nil {
			tr := &http.Transport{
				TLSClientConfig: &tls.Config{
					Certificates: []tls.Certificate{*cert},
				},
			}
			sslClient := &http.Client{Transport: tr}
			ctx := context.WithValue(req.Context(), oauth2.HTTPClient, sslClient)
			req = req.WithContext(ctx)
		}

		token, err := p.handleRequest(req)
		if err != nil {
			renderPKCEFlowTmpl(w, err)
			errChan <- fmt.Errorf("PKCE authorization flow failed: %v", err)
			return
		}

		renderPKCEFlowTmpl(w, nil)
		tokenChan <- token
	})

	server.Handler = mux
	if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
		errChan <- err
	}
}

func (p *PKCEAuthorizationFlow) handleRequest(req *http.Request) (*oauth2.Token, error) {
	query := req.URL.Query()

	if authError := query.Get(queryError); authError != "" {
		authErrorDesc := query.Get(queryErrorDesc)

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Read the wrapped text after 'PKCE authorization flow failed:' - it names the exact sub-cause (IdP error description, Invalid state, missing code, or the token exchange error).
  2. Retry the login (netbird up) for a fresh state and code_verifier; close stale browser tabs and do not refresh the callback page.
  3. Verify the IdP application's allowed redirect/callback URLs include the localhost redirect URL logged by the client.
  4. If the wrapped cause is an exchange failure, verify TokenEndpoint reachability from the client and that the system clock is synced (codes live for minutes).
  5. If it persists, have the NetBird administrator re-check the IdP configuration (ClientID, endpoints, scopes) in management.
Defensive patterns

Strategy: try-catch

Try / catch

tokenInfo, err := flow.WaitToken(ctx, info)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        // flow expired before the user finished login; call RequestAuthInfo again
    }
    // return err verbatim: the wrapped text after 'PKCE authorization flow failed:'
    // identifies the failed step (IdP error, state, code, or exchange)
    return TokenInfo{}, err
}

Prevention

When it happens

Trigger: WaitToken's callback server receives a request whose query has error/error_description set; or the state query param fails subtle.ConstantTimeCompare against p.state; or the code query param is empty; or p.oAuthConfig.Exchange against providerConfig.TokenEndpoint returns an error (network failure, wrong client credentials, expired or already-consumed authorization code, code_verifier mismatch).

Common situations: IdP application missing the localhost callback URL registration; a second login started and overwrote p.state/p.codeVerifier while the first browser tab finished; clock skew expiring the short-lived code; management's IdP configuration (TokenEndpoint, AuthorizationEndpoint, ClientID) pointing at wrong endpoints; corporate proxy or TLS interception blocking the token endpoint; user refreshing the callback page replaying a consumed code.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/2e73ca495e134e48. Report an issue: GitHub.