argoproj/argo-workflows · error

failed to parse encrypted token: %w

Error message

failed to parse encrypted token: %w

What it means

sso.Authorize parses the incoming Authorization bearer cookie/token as a JWE encrypted token (direct key, A256GCM). If jose.ParseEncrypted fails — the token is malformed, not a JWE, truncated, or uses an unexpected algorithm — the request is rejected with this wrapped error.

Source

Thrown at server/auth/sso/sso.go:429

// It's not sufficient to only refer to RFC3986 for this validation logic
// because modern browsers will convert back slashes (\) to forward slashes (/)
// and will interprete percent-encoded bytes.
//
// We used to use absolute redirect URLs and would validate the scheme and host
// match the request scheme and host, but this led to problems when Argo is
// behind a TLS termination proxy, since the redirect URL would have the scheme
// "https" while the request scheme would be "http"
// (see https://github.com/argoproj/argo-workflows/issues/13031).
func isValidFinalRedirectURL(redirect string) bool {
	// Copied from https://github.com/oauth2-proxy/oauth2-proxy/blob/ab448cf38e7c1f0740b3cc2448284775e39d9661/pkg/app/redirect/validator.go#L47
	return strings.HasPrefix(redirect, "/") && !strings.HasPrefix(redirect, "//") && !invalidRedirectRegex.MatchString(redirect)
}

// authorize verifies a bearer token and pulls user information form the claims.
func (s *sso) Authorize(authorization string) (*types.Claims, error) {
	tok, err := jwt.ParseEncrypted(strings.TrimPrefix(authorization, Prefix), []jose.KeyAlgorithm{jose.DIRECT}, []jose.ContentEncryption{jose.A256GCM})
	if err != nil {
		return nil, fmt.Errorf("failed to parse encrypted token: %w", err)
	}

	c := &types.Claims{}
	if err := tok.Claims(s.encryptionKey, c); err != nil {
		return nil, fmt.Errorf("failed to decrypt token: %w", err)
	}

	if err := c.Validate(jwt.Expected{Issuer: issuer}); err != nil {
		return nil, fmt.Errorf("failed to validate claims: %w", err)
	}
	return c, nil
}

func (s *sso) getRedirectURL(r *http.Request) string {
	if s.config.RedirectURL != "" {
		return s.config.RedirectURL
	}

View on GitHub (pinned to 35bff19146)

Solutions

  1. Log in again via the SSO redirect flow to obtain a fresh encrypted token
  2. Verify the Authorization header contains Argo's encrypted token (not a raw OIDC JWT or API key)
  3. Ensure the token is passed intact (no truncation by proxy, curl quoting, or cookie size limits)
  4. If tokens fail persistently, check that the server's token encryption key (argo-sso secret) has not changed/been deleted

Example fix

// before: sending raw OIDC token
-H "Authorization: <raw-id-token>"
// after: use token from Argo login (JWE), e.g. via CLI
argo auth token   # returns the correct encrypted bearer token
Defensive patterns

Strategy: try-catch

Validate before calling

if !strings.HasPrefix(auth, "Bearer ") { return errors.New("missing bearer token") }

Try / catch

claims, err := sso.Authorize(auth)
if err != nil {
    return status.Error(codes.Unauthenticated, "please re-authenticate via SSO")
}

Prevention

When it happens

Trigger: An HTTP request to the argo-server API carries an Authorization value that is not a valid JWE previously issued by the SSO callback; the token string was truncated/corrupted in transit or storage; a raw OIDC ID token (plain JWT, not encrypted) is presented instead of Argo's encrypted token.

Common situations: Users crafting the Authorization header by hand from their OIDC provider token; proxies or scripts stripping characters from the cookie; mixed versions where token format changed; testing with `argo` against a server whose cookie was issued under a different encryption key.

Understand the failure class

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/e6628a7dc15aed93. Report an issue: GitHub.