TL;DR

A JSON Web Token, or JWT, is a compact credential used to pass identity and authorization claims between systems. It contains a header, payload, and signature. The signature detects tampering, but it does not hide the payload.
Use short-lived access tokens, validate every relevant claim, keep signing keys on trusted servers, and handle refresh tokens carefully. You can inspect a test token with JWT.io, but never paste production tokens containing sensitive information into an external tool.
Understanding JSON Web Tokens
A JSON Web Token is a compact, URL-safe format for transmitting claims between two parties. Applications commonly use JSON Web Tokens to identify users, authorize API requests, and exchange trusted information between services.
JWT is standardized by RFC 7519. A typical authentication flow works like this:
- A user signs in with valid credentials.
- The authentication server issues a JWT.
- The client presents that token when accessing a protected resource.
- The receiving server verifies the token before processing the request.
Unlike a traditional session identifier, a JWT can carry information such as the user ID, intended audience, issuer, permissions, and expiration time. Because the server can verify these claims from the signed token, it may not need to retrieve a session record for every request.
That does not make JWT authentication completely “state-free.” Production systems may still need databases or caches for refresh-token rotation, account status, permission changes, logout, and emergency revocation.
JWTs are especially useful in distributed applications. For example, a multiplayer game or live video platform may have separate services for accounts, rooms, media access, and billing. A verified token can give those services a consistent identity context without sharing one centralized browser session.
The key security distinction in any JWT.io introduction is this: a normal signed JWT is signed, not encrypted. Anyone who obtains the token can usually decode its header and payload. The signature proves that the protected content has not been altered by an unauthorized party; it does not make that content private.
Never place passwords, private keys, payment details, or unnecessary personal information in a JWT payload.
How a JWT Works
A JWT usually consists of three Base64URL-encoded segments separated by periods:
header.payload.signatureA simplified token looks like this:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9
.eyJzdWIiOiJ1c2VyLTEyMyIsImF1ZCI6Im15LWFwaSJ9
.signature-bytesHeader
The header identifies the token type and cryptographic algorithm:
{
"alg": "RS256",
"typ": "JWT"
}typ commonly indicates JWT. alg tells the verifier which signing algorithm was used. The verifier must enforce an approved algorithm rather than blindly trusting this value.
Payload
The payload contains claims. Registered claims commonly include:
iss: the token issuersub: the subject, often the user IDaud: the intended audienceexp: the expiration timenbf: the time before which the token is invalidiat: the issuance timejti: a unique token identifier
Applications can also add private claims, such as a role or tenant ID:
{
"iss": "https://auth.example.com",
"sub": "user-123",
"aud": "video-api",
"role": "host",
"exp": 1760000000
}Keep the payload minimal. Tokens are sent repeatedly, and larger payloads increase request size. More importantly, encoded claims remain readable to anyone holding the token.
Signature
For a signed JWT, the issuer signs the encoded header and payload. Conceptually, the operation is:
signature = Sign(
base64Url(header) + "." + base64Url(payload),
signingKey
)With a shared-key algorithm such as HS256, the issuer and verifier use the same secret. With an asymmetric algorithm such as RS256 or ES256, the issuer signs with a private key and services verify with the corresponding public key.
If an attacker changes the payload from "role": "viewer" to "role": "admin", signature verification fails—provided the server validates the token correctly.
JWT.io can decode a token into its header, payload, and signature components. Decoding alone does not prove authenticity. An application must verify the signature and required claims before trusting any value.
Implementing JWT Authentication

A secure JWT flow includes more than calling a signing function. The application must control issuance, storage, verification, renewal, and revocation.
Step 1: Choose a Library and Configure the Issuer
Use a maintained library rather than implementing JWT cryptography yourself. Node.js applications commonly use jsonwebtoken, while Python applications may use PyJWT.
Define an issuer, audience, approved algorithm, access-token lifetime, and key-management process. Store private keys and shared secrets in a secret manager or protected environment variable—not in source code.
Create the sign-in endpoint separately from protected application routes. It should validate credentials before issuing any token.
Step 2: Issue a Short-Lived Access Token
After authentication succeeds, construct a minimal payload and sign it on the server:
import jwt from "jsonwebtoken";
const accessToken = jwt.sign(
{
sub: user.id,
role: user.role
},
process.env.JWT_PRIVATE_KEY,
{
algorithm: "RS256",
issuer: "https://auth.example.com",
audience: "video-api",
expiresIn: "10m"
}
);Do not include confidential profile data merely to avoid another database query. Claims can also become stale if the user’s role or account status changes.
If users need persistent sessions, issue a separate refresh token. Treat it as a high-value credential, store only a protected or hashed representation server-side where appropriate, and rotate it after use.
Step 3: Store Tokens According to the Client
For browser applications, a common design is:
- Keep the short-lived access token in memory.
- Store the refresh token in a
Secure,HttpOnly, and appropriately configuredSameSitecookie. - Protect cookie-based endpoints against cross-site request forgery.
An HttpOnly cookie prevents JavaScript from reading the token, reducing exposure during an XSS incident. It does not eliminate XSS or CSRF risk, so content security policies and request protections still matter.
On iOS, use Keychain. On Android, use Keystore-backed encrypted storage rather than plain preferences. Avoid local storage for long-lived credentials because injected scripts can read it.
Step 4: Attach the Access Token
For APIs using bearer authentication, send the access token in the Authorization header:
GET /api/rooms/842
Authorization: Bearer eyJhbGciOiJSUzI1NiIs...Always use HTTPS. A signed token can still be stolen if transmitted over an insecure connection.
Step 5: Verify Before Authorizing
The API must verify the signature, algorithm, issuer, audience, and time-based claims. It must then apply authorization rules rather than assuming every authenticated user can perform every action.
const claims = jwt.verify(
token,
process.env.JWT_PUBLIC_KEY,
{
algorithms: ["RS256"],
issuer: "https://auth.example.com",
audience: "video-api"
}
);
if (claims.role !== "host") {
return response.status(403).json({ error: "Insufficient permission" });
}Authentication answers “Who is this?” Authorization answers “May this user perform this operation?” JWT validation must support both decisions without confusing them.
Step 6: Refresh the Session Safely
When an access token expires, the client can call a dedicated endpoint with its refresh token. The server validates that credential, checks whether the session remains active, rotates the refresh token, and returns a new access token.
Rotation helps detect replay: if an already-used refresh token appears again, the server can revoke the affected token family and require a new sign-in.
This is particularly important for gaming, conferencing, and live-streaming applications where users may remain connected for long periods.
Step 7: Support Logout and Revocation
Logout should remove client-side credentials and invalidate the refresh session on the server. Waiting for a short-lived access token to expire may be acceptable for low-risk operations, but sensitive applications often need faster revocation.
Useful strategies include:
- Short access-token expiration times
- Refresh-token records with revocation status
- Token or session version numbers
- A denylist for high-risk incidents
- Key rotation for broader compromise response
Revocation adds state, but that is usually a reasonable security tradeoff. Stateless verification should not prevent an application from responding to stolen credentials or disabled accounts.
Why Applications Use JWTs
JWTs offer several practical advantages when they are used for the right problems.
- Distributed verification: Services can verify asymmetric signatures with a public key without sharing the issuer’s private key.
- Scalability: APIs do not need to load a centralized session object for every request.
- Interoperability: JWT is supported across common languages, frameworks, API gateways, and identity platforms.
- Portable claims: A token can carry the minimum identity and authorization context required by another service.
- Clear expiration: Time-based claims provide a standard mechanism for limiting credential lifetime.
There are also tradeoffs. JWTs are harder to revoke immediately than centralized sessions, claims can become outdated, and careless payload design can expose information. For a simple server-rendered website, an opaque session cookie may be easier to secure and operate.
Choose JWT because multiple APIs or services need verifiable claims—not because token-based authentication is automatically better for every application.
JWT Security Best Practices
Use the following controls when moving from a JWT.io introduction to a production implementation:
- Allowlist algorithms. Configure the verifier to accept only the algorithm your system actually uses. Never accept
none. - Protect signing keys. Keep secrets and private keys server-side, use a managed secret store, restrict access, and rotate keys.
- Prefer short-lived access tokens. Minutes are generally safer than days. Use controlled refresh sessions for longer sign-ins.
- Validate required claims. Check
iss,aud,exp, andnbf, not only the signature. - Keep claims minimal. JWT payloads are readable unless a separate encryption standard is deliberately used.
- Do not generate trusted tokens in clients. Browsers and mobile apps cannot safely hold server signing secrets.
- Plan for revocation. Support password changes, account suspension, device removal, and suspected token theft.
- Use TLS everywhere. Signing prevents modification; it does not prevent interception.
- Separate credential purposes. Access tokens, refresh tokens, and third-party service credentials should have distinct audiences and lifecycles.
- Avoid exposing tokens in URLs or logs. Query strings can leak through browser history, analytics, proxies, and referrer headers.
How to Build an App Like This with Tencent RTC
In a real-time calling, conferencing, or live-streaming application, JWT authentication should establish the application session. Media-room access should then use a separate, server-issued Tencent RTC credential.
A practical architecture is:
- The client signs in to your authentication server.
- Your server issues a short-lived application JWT.
- The client presents that JWT to a protected
/rtc/credentialendpoint. - The server verifies the JWT and checks whether the user may join the requested room.
- The trusted backend generates the Tencent RTC
userSig. - The client uses
trtc-sdk-v5to enter the room and capture, publish, and subscribe to media.
This separation prevents a valid website account from automatically gaining unrestricted room access. The backend can enforce room membership, host roles, bans, and session limits before returning an RTC credential.
Use the Tencent RTC Engine as the cross-platform real-time media layer. Its weak-network resilience, global low-latency infrastructure, monitoring, and media processing support custom calling and interactive experiences without making JWTs responsible for media transport.
A protected backend endpoint can bridge application identity to RTC authorization:
app.post("/rtc/credential", authenticateJwt, async (req, res) => {
const { roomId } = req.body;
const userId = req.auth.sub;
const allowed = await canJoinRoom(userId, roomId);
if (!allowed) {
return res.status(403).json({ error: "Room access denied" });
}
const userSig = generateTencentRTCUserSig({
sdkAppId: process.env.TENCENT_RTC_SDK_APP_ID,
secretKey: process.env.TENCENT_RTC_SECRET_KEY,
userId,
expiresIn: 600
});
res.json({
sdkAppId: Number(process.env.TENCENT_RTC_SDK_APP_ID),
roomId,
userId,
userSig
});
});generateTencentRTCUserSig represents server-side credential generation. Never embed the corresponding secret key in browser or mobile code.
After receiving those values, the web client can enter the room and publish local media:
import TencentRTC from "trtc-sdk-v5";
const client = TencentRTC.create();
await client.enterRoom({
sdkAppId,
userId,
userSig,
roomId,
role: "anchor"
});
await client.startLocalAudio();
await client.startLocalVideo({ view: "local-video" });
client.on(
TencentRTC.EVENT.REMOTE_VIDEO_AVAILABLE,
async ({ userId, streamType }) => {
await client.startRemoteVideo({
userId,
streamType,
view: `remote-${userId}`
});
}
);Review the official Tencent RTC Engine documentation when selecting SDKs and planning room, publishing, subscription, and platform integration. Keep application JWT expiration and Tencent RTC credential expiration independent so each can be rotated according to its own risk.
FAQ
Is a JWT encrypted?
Usually not. A standard signed JWT has a readable header and payload. Its signature detects unauthorized changes but does not conceal the claims.
Is JWT.io safe for production tokens?
Use JWT.io for learning and non-sensitive test tokens. Avoid pasting active production credentials into any external debugging service.
Should a JWT replace every server session?
No. JWTs suit distributed APIs that need portable, verifiable claims. Traditional opaque sessions may be simpler when immediate revocation and centralized control are more important.

