oauth2-proxy/oauth2-proxy · error

malformed access token, expected 3 parts got %d

Error message

malformed access token, expected 3 parts got %d

What it means

getAccessClaims splits the session's access token to read its JWT payload and count the dot-separated parts. If fewer than 2 parts are found, the token is not a usable JWT and this error is thrown. Despite the message mentioning 3 parts, the check rejects tokens with fewer than 2 parts.

Source

Thrown at providers/keycloak_oidc.go:116

	for _, role := range roles {
		s.Groups = append(s.Groups, formatRole(role))
	}
	return nil
}

type realmAccess struct {
	Roles []string `json:"roles"`
}

type accessClaims struct {
	RealmAccess    realmAccess            `json:"realm_access"`
	ResourceAccess map[string]interface{} `json:"resource_access"`
}

func (p *KeycloakOIDCProvider) getAccessClaims(s *sessions.SessionState) (*accessClaims, error) {
	parts := strings.Split(s.AccessToken, ".")
	if len(parts) < 2 {
		return nil, fmt.Errorf("malformed access token, expected 3 parts got %d", len(parts))
	}

	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return nil, fmt.Errorf("malformed access token, couldn't extract jwt payload: %v", err)
	}

	var claims accessClaims
	if err := json.Unmarshal(payload, &claims); err != nil {
		return nil, err
	}
	return &claims, nil
}

// getClientRoles extracts client roles from the `resource_access` claim with
// the format `client:role`.
//
// ResourceAccess format:

View on GitHub (pinned to 33c2eb92de)

Solutions

  1. Ensure the Keycloak client issues JWT access tokens (check 'Access Token' format settings in the Keycloak client config)
  2. Verify the session has a non-empty AccessToken before the refresh/extraction flow
  3. Re-authenticate to get a fresh token set rather than relying on a partially populated session
  4. If tokens are opaque by design, disable Keycloak-specific role extraction from the access token
Defensive patterns

Strategy: validation

Validate before calling

parts := strings.Split(s.AccessToken, ".")
if len(parts) < 3 {
    return errors.New("access token is not a JWT; skipping role extraction")
}

Type guard

func isJWTLike(token string) bool {
    return strings.Count(token, ".") >= 2
}

Prevention

When it happens

Trigger: extractRoles calls getAccessClaims with a session whose AccessToken is empty, opaque, or otherwise lacks at least two dot-separated segments.

Common situations: Keycloak client configured without 'Accept OIDC ID tokens' so only opaque access tokens are issued; access token empty because the session was created from a refresh flow without tokens; a non-JWT bearer token supplied to CreateSessionFromToken.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of oauth2-proxy/oauth2-proxy@33c2eb92de (2026-09-06). Data as JSON: /api/errors/c7e87cf7920bc0ae. Report an issue: GitHub.