WeKnora Principal Model for API Keys and MCP/Embed Session Isolation
WeKnora separates human and machine authentication through a lightweight principal model that isolates API keys, MCP OAuth tokens, and embed sessions by representing every caller as a typed identifier stored in the request context.
The Tencent/WeKnora repository implements a robust authorization layer that treats API keys, embed visitors, and instant-messaging users as distinct security principals. This principal model for API keys and MCP/embed session isolation ensures that machine credentials cannot impersonate human users and that OAuth tokens remain scoped to their specific caller identity.
What Is the Principal Model?
A principal is a lightweight struct representing the ultimate caller of a request, deliberately separate from the traditional UserID. While human users authenticate via JWT, many callers—such as API keys, IM integrations, and anonymous embed visitors—lack a regular WeKnora account. The principal model provides a unified way to identify these disparate callers through a simple type-plus-identifier tuple.
Principal Types and Structure
The core enumeration lives in internal/types/principal.go:
type Principal struct {
Type string
ID string
}
WeKnora defines eight distinct principal types:
- web_user: Normal logged-in human user (JWT-based).
- api_tenant: Tenant-scoped API-key principal issued per-key.
- api_platform: Platform-wide API-key principal for system keys.
- api_external_user: API-key mapped to an external user identity.
- im_user: Instant-messaging user (e.g., Feishu, WeChat).
- embed_channel: Configuration of a public embed channel.
- embed_session: Concrete chat session for an embed visitor.
- embed_visitor: Anonymous browser visitor that may open multiple sessions.
The principal is attached to a request context via WithPrincipal(ctx, p) and retrieved via PrincipalFromContext. If no principal exists, the system falls back to the JWT UserID and treats it as a web_user.
API Key Isolation via First-Class Principals
API keys are first-class principals of type api_tenant or api_platform. When a request carries the X-API-Key header, middleware extracts a TenantAPIKeyScope and stores it in the context.
The API Key Gate
The API key gate in internal/middleware/api_key_gate.go evaluates every request against declared APIKeyRoutePolicy rules:
func (a *APIKeyRouteAuthorizer) authorize(
scope types.TenantAPIKeyScope, method, fullPath string) error {
policy, ok := a.Lookup(method, fullPath)
if !ok { return errTenantAPIKeyScopeForbidden } // default deny
if policy.PlatformOnly && !scope.IsPlatform() { … } // platform-only routes
if scope.FullAccess { return nil } // full-access keys
for _, cap := range policy.Capabilities {
if cap != "" && scope.HasCapability(cap) { return nil }
}
…
}
This implementation enforces three isolation guarantees:
- Default deny: Routes without an explicit policy are implicitly rejected, eliminating the "forget-to-add-APIKeyDeny" vulnerability.
- Platform-only enforcement: Routes marked
policy.PlatformOnlyreject tenant-scoped keys. - Capability-based access: Fine-grained permissions via
policy.Capabilitiesallow keys to possess specific abilities (e.g.,retrieve,modify) rather than blanket access.
The authorizer attaches as the first handler of the /api/v1 group, ensuring every API key request is validated before business logic executes.
MCP OAuth Token Isolation
MCP services requiring OAuth tokens store credentials per-principal rather than per-user. The token store uses the composite key (tenant_id, principal_type, principal_id, service_id) as defined in migration migrations/versioned/000064_principal_model.up.sql.
For a standard API key call, the principal is api_tenant; for embed flows, it becomes an embed_visitor or embed_session principal derived from the X-Embed-Visitor header. The function MCPOAuthPrincipalFromContext in internal/types/principal.go resolves the correct identity:
func MCPOAuthPrincipalFromContext(ctx context.Context) Principal {
p, ok := PrincipalFromContext(ctx)
if !ok { return Principal{} }
if p.Type != PrincipalEmbedSession { return p }
// embed session → embed visitor if X-Embed-Visitor present
…
}
This design prevents cross-visitor token leakage by ensuring each embed visitor maintains distinct OAuth credentials.
Embed Session Isolation
Embedding operates through a public embed channel (embed_channel). When a visitor opens the widget, the frontend sends an X-Embed-Visitor header containing a random visitor ID. The server then materializes two distinct principals:
- Embed session principal (
embed_session): Identifies a single chat session viatenant:channel:session. - Embed visitor principal (
embed_visitor): Groups all sessions belonging to the same anonymous browser.
Session Ownership and Validation
The SessionOwnerIDFromContext function in internal/types/principal.go constructs ownership identifiers such as api_tenant_key:<tenantID>:<keyID> for API key sessions or embed_session:<...> for embed sessions.
The embed authentication middleware in internal/middleware/embed_auth.go validates the X-Embed-Visitor header, extracts the visitor principal, and enforces rate limits and origin checks before allowing any embed API call.
Summary
- The principal model unifies human and machine authentication through a type-plus-ID struct stored in the request context.
- API keys isolate tenant and platform access via the
APIKeyRouteAuthorizerininternal/middleware/api_key_gate.go, which implements default-deny policies and capability-based authorization. - MCP OAuth tokens are scoped to specific principals using composite database keys, preventing token reuse across API keys and embed visitors.
- Embed sessions use separate principals for channels, sessions, and visitors, ensuring anonymous browser identities cannot interfere with tenant data or other visitors.
- The system stores principal metadata in
mcp_oauth_tokensandapi_principal_configtables as defined in migration000064_principal_model.up.sql.
Frequently Asked Questions
How does WeKnora prevent API keys from impersonating human users?
WeKnora stores API keys as distinct principal types (api_tenant or api_platform) rather than converting them to web_user identities. The APIKeyRouteAuthorizer middleware evaluates these principals against explicit route policies before any handler executes, ensuring API keys can only access capabilities explicitly granted to them and cannot masquerade as JWT-authenticated humans.
What happens if a route lacks an API key policy definition?
Routes without a registered APIKeyRoutePolicy trigger an implicit denial. The authorize function in internal/middleware/api_key_gate.go returns errTenantAPIKeyScopeForbidden when no policy is found, implementing a fail-closed security posture that prevents accidental exposure of endpoints to API key access.
How are MCP OAuth tokens isolated between different embed visitors?
Each embed visitor receives a unique embed_visitor principal derived from the X-Embed-Visitor header. The MCPOAuthPrincipalFromContext function resolves this identity when storing or retrieving tokens, using the composite database key (tenant_id, principal_type, principal_id, service_id). This ensures visitor A's OAuth credentials remain inaccessible to visitor B even when both interact with the same embed channel.
Can a single API key support both tenant-scoped and platform-wide operations?
No, API keys are categorized as either api_tenant or api_platform principals at creation. Platform-only routes explicitly reject tenant-scoped keys by checking policy.PlatformOnly and scope.IsPlatform(). Keys requiring broad access must be issued as api_platform principals, subject to stricter security controls.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →