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 itsView on GitHub (pinned to 988cbb0330)
Solutions
- Verify the principal passed to IsAuthorizationAttemptComplete is exactly the one that started the authorization attempt (same type and ID after Normalize()).
- Start a fresh authorization attempt for the current principal instead of reusing the old state value.
- Check tenant/service routing so the callback/status call lands in the same tenantID/serviceID context that created the attempt.
- 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
- Always pass the exact principal that initiated the attempt; re-read it from the session, not from request input.
- Normalize principals on both write and read paths before comparison.
- Scope state cookies to tenant+principal so a mismatched identity cannot even submit the state.
- Rotate session identity carefully: invalidate outstanding OAuth attempts on login/logout.
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
- MCP OAuth metadata URL failed SSRF validation: %w
- join request not found
- failed to retrieve: %s
- opensearch: index not found
- 2201
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/2f602cf221e67a14.
Report an issue: GitHub.