How WeKnora Embed Widget Secure-Mode Token Exchange and Rate Limiting Works
WeKnora's embed widget secure-mode exchanges long-lived publish tokens (em_…) for short-lived session tokens (ems_…) via your backend, enforcing per-minute and per-day rate limits on the exchange endpoint to prevent abuse.
The Tencent/WeKnora repository provides an embeddable widget that supports a secure operational mode to prevent token exposure. In this configuration, the embed widget secure-mode token exchange and rate limiting mechanisms work together to ensure the long-lived publish token never reaches the client browser while protecting the API from brute-force attacks.
Secure-Mode Token Exchange Workflow
The token exchange follows a six-step delegation pattern where your backend acts as a trusted intermediary:
-
Visitor Browser loads the embed widget iframe configured with
data-token-endpointpointing to your backend. Crucially, nodata-tokenattribute is present in the HTML, ensuring the publish token remains server-side. -
Your Backend receives the GET request and verifies user authentication via session cookies or JWT. Unauthenticated requests return 401 Unauthorized.
-
Your Backend calls the WeKnora API endpoint:
POST /api/v1/embed/<CHANNEL_ID>/exchangewith headers:
Authorization: Embed <publish-token>Origin: https://<your-backend>(must match the channel whitelist)
-
WeKnora API validates the publish token, Origin header, and rate-limit counters. Successful validation returns:
{ "session_token": "ems_...", "expires_in": 1800 } -
Your Backend returns the payload
{ "token": "<ems_…>", "expiresIn": 1800 }to the widget. -
Widget stores the session token (valid for ~30 minutes) and uses it for subsequent API calls, automatically refreshing when expired.
Critical Security Requirements
- Publish Token Isolation: The
em_…publish token must remain in theWEKNORA_PUBLISH_TOKENenvironment variable on your server. It never appears in browser source code. - Explicit Origin Header: When calling the exchange endpoint, you must manually set the
Originheader. The server-sidefetchdoes not add it automatically; omission causes a 403 "origin not allowed" error. - Domain Whitelist: The channel configuration in WeKnora maintains a whitelist of allowed origins including your embed page domain and the backend exchange domain.
Rate Limiting Architecture
According to the WeKnora source code, the exchange endpoint implements two-tier rate limiting stored in the embed_channels database table.
Per-Minute and Daily Quotas
Every embed channel stores two limit fields:
rate_limit_per_minute: Defines the maximum exchange requests allowed in any rolling 60-second window. A token-bucket algorithm tracks usage; exceeding the limit returns 429 Too Many Requests.rate_limit_per_day: Defines the maximum requests allowed per calendar day. The counter resets at midnight UTC, and exceeding it returns 429 until the next day.
Enforcement Logic
These limits apply before token validation in internal/server/embed.go. When either threshold is exceeded, the server immediately returns HTTP 429 without validating the publish token, ensuring abusive clients cannot bypass limits with randomized tokens.
Production Implementation Examples
Node.js Express Token Endpoint
As documented in docs/embed-secure-mode.md (lines 84-108), implement the exchange endpoint:
const WEKNORA_BASE = 'https://<WEKNORA_HOST>';
const CHANNEL_ID = '<CHANNEL_ID>';
const ALLOWED_ORIGIN = 'https://shop.example.com';
app.get('/weknora/embed-token', async (req, res) => {
// 1. Verify visitor authentication
const hasSession = Boolean(req.cookies?.session_id);
const auth = req.headers.authorization || '';
if (!hasSession && !auth.startsWith('Bearer ')) {
return res.status(401).json({ error: 'unauthorized' });
}
// 2. Exchange publish token for session token
const r = await fetch(`${WEKNORA_BASE}/api/v1/embed/${CHANNEL_ID}/exchange`, {
method: 'POST',
headers: {
Authorization: 'Embed ' + process.env.WEKNORA_PUBLISH_TOKEN,
Origin: ALLOWED_ORIGIN,
},
});
const body = await r.json();
// 3. Return session token to widget
if (!body?.data?.session_token) {
return res.status(502).json({ error: 'mint failed' });
}
res.json({
token: body.data.session_token,
expiresIn: body.data.expires_in
});
});
Go HTTP Handler
The equivalent Go implementation from docs/embed-secure-mode.md (lines 111-143):
func embedTokenHandler(w http.ResponseWriter, r *http.Request) {
// 1. Authentication check
if r.Header.Get("Authorization") == "" && r.Header.Get("Cookie") == "" {
http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
return
}
// 2. Call WeKnora exchange endpoint
req, _ := http.NewRequest(http.MethodPost,
"https://<WEKNORA_HOST>/api/v1/embed/<CHANNEL_ID>/exchange", nil)
req.Header.Set("Authorization", "Embed "+os.Getenv("WEKNORA_PUBLISH_TOKEN"))
req.Header.Set("Origin", "https://shop.example.com")
resp, err := http.DefaultClient.Do(req)
if err != nil || resp.StatusCode >= 300 {
http.Error(w, `{"error":"mint failed"}`, http.StatusBadGateway)
return
}
defer resp.Body.Close()
// 3. Extract and return token
var body struct {
Data struct {
SessionToken string `json:"session_token"`
ExpiresIn int `json:"expires_in"`
} `json:"data"`
}
if json.NewDecoder(resp.Body).Decode(&body) != nil || body.Data.SessionToken == "" {
http.Error(w, `{"error":"mint failed"}`, http.StatusBadGateway)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]any{
"token": body.Data.SessionToken,
"expiresIn": body.Data.ExpiresIn,
})
}
Frontend Widget Configuration
In your HTML, reference the token endpoint without exposing credentials:
<iframe
src="https://widget.weknora.cn/embed.html"
data-token-endpoint="https://shop.example.com/weknora/embed-token"
data-channel-id="YOUR_CHANNEL_ID">
</iframe>
The frontend/public/weknora-widget.js SDK reads data-token-endpoint, fetches the JSON payload, and manages token refresh automatically.
Key Source Files
docs/embed-secure-mode.md: Complete workflow documentation, Node.js and Go examples (lines 41-143)docs/embed-subdomain.md: Subdomain configuration affecting origin whitelist validationfrontend/public/weknora-widget.js: Client-side SDK handling token acquisition and refreshinternal/server/embed.go: Server-side exchange handler implementing token validation and rate limitingbackend/api/v1/embed/channel.go: Channel data model includingRateLimitPerMinuteandRateLimitPerDayfieldsclient/src/api/embed/index.ts: TypeScript definitions for the exchange API
Summary
- WeKnora's embed widget secure-mode prevents token leakage by exchanging long-lived
em_…publish tokens for short-livedems_…session tokens through your backend. - The exchange endpoint requires explicit
Originheaders and validates against a domain whitelist defined in the channel configuration. - Rate limiting operates on two tiers: per-minute (rolling window) and per-day (UTC calendar), enforced via token-bucket algorithms before token validation.
- Implementation requires server-side handlers in Node.js, Go, or similar to authenticate users and proxy the exchange request with the
WEKNORA_PUBLISH_TOKENenvironment variable.
Frequently Asked Questions
How long does a session token remain valid?
Session tokens (ems_…) expire after approximately 30 minutes (1800 seconds). The frontend/public/weknora-widget.js SDK automatically detects expiration and re-fetches a fresh token from your configured endpoint without user intervention.
Why do I receive a 403 "origin not allowed" error?
The WeKnora API validates the Origin header against the channel's whitelist configured in the admin UI. You must explicitly set the Origin header in your backend request (it is not automatic), and the value must match an entry in the whitelist. Both your frontend domain and backend exchange domain require whitelist entries.
What happens when rate limits are exceeded?
When the per-minute or per-day thresholds are exceeded, the API returns 429 Too Many Requests. These limits apply before token validation, meaning repeated requests with invalid tokens still count toward your quota. The per-day counter resets at midnight UTC.
Can I use the same publish token for multiple channels?
No. Publish tokens (em_…) are scoped to specific channels identified by CHANNEL_ID. The exchange endpoint URL includes the channel ID (/api/v1/embed/<CHANNEL_ID>/exchange), and the token is validated against that channel's specific configuration, rate limits, and whitelist.
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 →