Backend integration
The widget never holds your tenant key. Instead, your authenticated panel backend exchanges the long-term key for a short-lived JWT (15-minute TTL) that the widget uses for the WebSocket connection. This guarantees the tenant key only lives on servers you control.
The flow
- Logged-in user opens your admin panel.
- Widget loads, calls
/api/ask2do/tokenon your backend. - Your backend confirms the user's session and POSTs to
cloud.ask2do.com/auth/tokenwith{ tenantKey, panelUser: { id, role } }. - Cloud returns a 15-minute JWT.
- Your backend forwards the JWT to the widget.
- Widget opens its WebSocket using the JWT.
Endpoint contract
Your backend exposes one endpoint, e.g. POST /api/ask2do/token. It must:
- Require a logged-in user (cookie/session — your existing auth).
- Read your tenant key from a server-only env var (never embed in HTML).
- POST to
https://cloud.ask2do.com/auth/tokenwith the user's panel id + role. - Return the JWT in JSON:
{ token, expiresAt }.
Sample response from cloud
{
"token": "eyJhbGciOiJFZERTQSIsImtpZCI6IjEifQ.eyJzdWIiOiI0MiIsInJvbGUiOiJhZG1pbiIs...",
"expiresAt": "2026-05-02T13:55:00.000Z"
}On success, cloud returns 200 with the JWT (~340 chars) and an absolute ISO expiration timestamp. On failure, you get 4xx with { error }.
Code samples
Pick your stack — same logic in every language. The widget calls whichever endpoint you mount.
app.post("/api/ask2do/token", requireLogin, async (req, res) => {
const r = await fetch("https://cloud.ask2do.com/auth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
tenantKey: process.env.ASK2DO_TENANT_KEY, // server-only env
panelUser: { id: String(req.user.id), role: req.user.role },
}),
signal: AbortSignal.timeout(5000),
});
if (!r.ok) return res.status(502).json({ error: "ask2do auth failed" });
res.json(await r.json());
});Security checklist
- Never include the tenant key in HTML, JavaScript, or any page served to a browser.
- The endpoint must require a logged-in user — anyone who can call it can mint JWTs as themselves.
- Use HTTPS for the cloud call (already the default in our code samples).
- The cloud rejects roles outside
admin / ownerat mint time, so passing a user's real role is safe. - Rotate tenant keys periodically. See security docs.
Where it goes wrong
| Symptom | Cause |
|---|---|
Cloud returns 401: invalid tenant key | Tenant key is wrong, revoked, or paused. Verify in your portal. |
Cloud returns 403: role not allowed | You sent panelUser.role outside admin/owner. Use only those values; junior users should not reach this code path. |
| Widget shows "invalid token" on connect | Endpoint returned a malformed JWT, or the token expired before use. Confirm the response shape matches the sample above. |
Next: widget
With the token endpoint live, drop the widget script tag into your panel HTML. Widget script tag →