Tencent/WeKnora · error

oauth authorization attempt does not match the current princ

Error message

oauth authorization attempt does not match the current principal or service

What it means

IsAuthorizationAttemptComplete verifies that the stored OAuth authorization attempt belongs to the tenant, service, and principal making the status query. This error is thrown when the attempt record's tenant/service IDs or normalized principal (type + ID) do not match the caller's current identity. It prevents a principal from reading or consuming another principal's OAuth flow state.

Source

Thrown at internal/mcp/oauth_manager.go:245

// IsAuthorizationAttemptComplete reports whether this exact authorization
// attempt completed for the requested principal and service. A pre-existing
// token must never satisfy a newly opened OAuth popup.
func (m *OAuthManager) IsAuthorizationAttemptComplete(
	ctx context.Context,
	tenantID uint64,
	principal types.Principal,
	serviceID, attemptID string,
) (bool, error) {
	attempt, err := m.states.Attempt(ctx, attemptID)
	if err != nil {
		return false, err
	}
	principal = principal.Normalize()
	attemptPrincipal := attempt.Principal.Normalize()
	if attempt.TenantID != tenantID || attempt.ServiceID != serviceID ||
		attemptPrincipal.Type != principal.Type || attemptPrincipal.ID != principal.ID {
		return false, fmt.Errorf("oauth authorization attempt does not match the current principal or service")
	}
	return attempt.Completed, nil
}

// AuthorizationStatus reports whether the stored access token is usable now,
// or is expired but still has a refresh token that runtime use can rotate.
func (m *OAuthManager) AuthorizationStatus(
	ctx context.Context, tenantID uint64, principal types.Principal, serviceID string,
) (OAuthAuthorizationStatus, error) {
	tok, err := m.repo.GetTokenForPrincipal(ctx, tenantID, principal, serviceID)
	if err != nil {
		return OAuthAuthorizationStatus{}, err
	}
	return tokenStatus(tok, time.Now()), nil
}

// IsAuthorized reports whether the given principal has an access token that is
// usable now. An expired row is not authorization success merely because its

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Verify the principal passed to IsAuthorizationAttemptComplete is exactly the one that started the authorization attempt (same type and ID after Normalize()).
  2. Start a fresh authorization attempt for the current principal instead of reusing the old state value.
  3. Check tenant/service routing so the callback/status call lands in the same tenantID/serviceID context that created the attempt.
  4. If identities legitimately change, migrate or delete the stale attempt rather than querying it under the new principal.

Example fix

// before
complete, err := svc.IsAuthorizationAttemptComplete(ctx, tenantA, serviceID, oldPrincipal, state)
// after
complete, err := svc.IsAuthorizationAttemptComplete(ctx, attempt.TenantID, attempt.ServiceID, attempt.Principal, state)
Defensive patterns

Strategy: try-catch

Validate before calling

if attempt.TenantID != tenantID || attempt.ServiceID != serviceID || attempt.Principal.Normalize().ID != principal.Normalize().ID {
    // skip status check; start a new attempt
}

Type guard

func attemptBelongsTo(attempt OAuthAttempt, tenantID, serviceID string, principal Principal) bool {
    p := principal.Normalize()
    ap := attempt.Principal.Normalize()
    return attempt.TenantID == tenantID && attempt.ServiceID == serviceID && ap.Type == p.Type && ap.ID == p.ID
}

Try / catch

complete, err := svc.IsAuthorizationAttemptComplete(ctx, tenantID, serviceID, principal, state)
if err != nil {
    // principal/service mismatch: discard state, start a fresh OAuth attempt
    state = beginNewAuthorizationAttempt(ctx, tenantID, serviceID, principal)
}

Prevention

When it happens

Trigger: Calling Status (or TestAuthorizationAttemptStatusIsScopedToPrincipalAndService) with a state parameter whose stored OAuthAttempt was created by a different principal ID/type, a different tenantID, or a different serviceID.

Common situations: A user re-logs into the app with a different account before completing OAuth; the principal identity rotated (e.g. token refresh assigned a new user ID); the state token is replayed against another tenant/service; multitenant routing sent the callback to the wrong tenant context.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/2f602cf221e67a14. Report an issue: GitHub.