AlistGo/alist · error

oidc: malformed jwt, expected 3 parts got %d

Error message

oidc: malformed jwt, expected 3 parts got %d

What it means

Returned by parseJWT (server/handles/ssologin.go:178) during OIDC callback handling when the id_token has fewer than 2 dot-separated segments — i.e. it is not a JWT at all. Note the guard checks len(parts) < 2 while the message says 'expected 3 parts', so a token with zero or one dot triggers it; a normal JWT (header.payload.signature) passes.

Source

Thrown at server/handles/ssologin.go:178

		SsoID:      userID,
	}
	if err = db.CreateUser(user); err != nil {
		if strings.HasPrefix(err.Error(), "UNIQUE constraint failed") && strings.HasSuffix(err.Error(), "username") {
			user.Username = user.Username + "_" + userID
			if err = db.CreateUser(user); err != nil {
				return nil, err
			}
		} else {
			return nil, err
		}
	}
	return user, nil
}

func parseJWT(p string) ([]byte, error) {
	parts := strings.Split(p, ".")
	if len(parts) < 2 {
		return nil, fmt.Errorf("oidc: malformed jwt, expected 3 parts got %d", len(parts))
	}
	payload, err := base64.RawURLEncoding.DecodeString(parts[1])
	if err != nil {
		return nil, fmt.Errorf("oidc: malformed jwt payload: %v", err)
	}
	return payload, nil
}

func OIDCLoginCallback(c *gin.Context) {
	useCompatibility := setting.GetBool(conf.SSOCompatibilityMode)
	method := c.Query("method")
	if useCompatibility {
		method = path.Base(c.Request.URL.Path)
	}
	clientId := setting.GetStr(conf.SSOClientId)
	endpoint := setting.GetStr(conf.SSOEndpointName)
	provider, err := oidc.NewProvider(c, endpoint)
	if err != nil {

View on GitHub (pinned to 843d9dc814)

Solutions

  1. Verify the OIDC provider actually issues JWT id_tokens for your client
  2. Inspect the raw token string at the callback for truncation or embedded whitespace
  3. Log the segment count and the provider response to confirm which of the above applies
  4. Check conf.SSOCompatibilityMode if the method is derived from the URL path

Example fix

// before
idToken := resp.AccessToken // opaque token, no dots
// after
idToken := resp.IDToken // proper JWT: header.payload.signature
Defensive patterns

Strategy: validation

Validate before calling

func looksLikeJWT(s string) bool { return strings.Count(s, ".") >= 2 && len(s) > 0 }

Try / catch

if err != nil && strings.Contains(err.Error(), "malformed jwt") { reFetchTokenAndRetry() }

Prevention

When it happens

Trigger: OIDC provider returns an opaque access token instead of a JWT id_token; the token string got truncated (e.g. by URL parsing on '#' or whitespace); SSO compatibility mode misreads the callback path so the wrong value is parsed as the token.

Common situations: Provider configuration requesting scope/response_type that omits id_token; proxies stripping query fragments; identity providers that return errors in the token field during outages.

Understand the failure class

Related errors


AI-assisted analysis of AlistGo/alist@843d9dc814 (2026-08-15). Data as JSON: /api/errors/3926a2dc907b6637. Report an issue: GitHub.