Backend Verification
Every authenticated Tribe session produces a userToken, a standard JWT signed with your site’s secret key. Because the signature is cryptographic, your backend can verify a user’s identity by checking it alone, no database lookup or network call back to Tribe required.
How it works
Section titled “How it works”- User logs in via the SDK on the frontend
- The SDK returns a
userToken(JWT) alongside the session - Your frontend sends the
userTokento your backend (for example, in anAuthorizationheader) - Your backend verifies the JWT with your site’s
TRIBE_KEY
Getting your TRIBE_KEY
Section titled “Getting your TRIBE_KEY”- Go to the Tribe dashboard
- Open your site’s Settings
- Copy the TRIBE_KEY
This is a secret. Store it as an environment variable and keep it out of version control.
Getting the user token
Section titled “Getting the user token”On the frontend:
import { Tribe } from "@tribecloud/sdk";
const token = tribe.getUserToken();// Send this to your backendThe token refreshes automatically when you call getSession(), so it stays current as long as the user has an active session.
JWT payload
Section titled “JWT payload”{ sub: string; // User ID email: string | null; // null for wallet/OAuth/Google users pseudonymousId?: string; // Stable hash for non-email users authMethod?: string; // "email" | "wallet" | "google" | "github" | etc. role: string | null; // User's role (set via tribe.setRole()) siteId: string; // Your site ID iat: number; // Issued at (unix timestamp) exp: number; // Expires at (1 hour after iat)}Verification examples
Section titled “Verification examples”import { createHmac } from "crypto";
function verifyUserToken(userToken: string, tribeKey: string) { const [header, payload, signature] = userToken.split("."); const expected = createHmac("sha256", tribeKey) .update(`${header}.${payload}`) .digest("base64url");
if (signature !== expected) throw new Error("Invalid token");
const data = JSON.parse(Buffer.from(payload, "base64url").toString()); if (data.exp < Math.floor(Date.now() / 1000)) throw new Error("Token expired");
return data; // { sub, email, role, siteId, iat, exp }}
// Usage in an Express routeapp.get("/api/profile", (req, res) => { const token = req.headers.authorization?.replace("Bearer ", ""); try { const user = verifyUserToken(token, process.env.TRIBE_KEY); res.json({ userId: user.sub, email: user.email, role: user.role }); } catch (err) { res.status(401).json({ error: "Unauthorized" }); }});import hmac, hashlib, base64, json, time
def verify_user_token(user_token: str, tribe_key: str) -> dict: header, payload, signature = user_token.split(".") expected = base64.urlsafe_b64encode( hmac.new( tribe_key.encode(), f"{header}.{payload}".encode(), hashlib.sha256 ).digest() ).rstrip(b"=").decode()
if signature != expected: raise ValueError("Invalid token")
data = json.loads(base64.urlsafe_b64decode(payload + "==")) if data["exp"] < int(time.time()): raise ValueError("Token expired")
return data # { sub, email, role, siteId, iat, exp }If you’d rather not write the verification by hand, any standard JWT library that supports HS256 works fine. Popular choices include jose or jsonwebtoken for Node.js and PyJWT for Python.
- Rotating the
TRIBE_KEYin the dashboard invalidates every outstanding user token immediately. Plan for a brief re-authentication window if you rotate keys. - For users who signed in via wallet or social login, the
emailfield will benull. Usesub(the user ID) orpseudonymousIdas a stable identifier instead. - The
userTokenis separate from the session token that the SDK uses internally to talk to Tribe’s servers. They serve different purposes and shouldn’t be confused.