JWT Decoder JavaScript — atob(), TextDecoder और jose
मुफ़्त JWT Decoder को सीधे अपने ब्राउज़र में उपयोग करें — इंस्टॉलेशन की ज़रूरत नहीं।
JWT Decoder ऑनलाइन आज़माएं →मैंने जितने भी authentication flow बनाए हैं, वे अंततः एक ही बिंदु पर पहुँचते हैं: आपके पास एक JWT cookie में, header में, या OAuth callback URL में होता है, और आपको उसके अंदर क्या है वह पढ़ना होता है। JavaScript में JWT decoder के लिए किसी npm पैकेज की आवश्यकता नहीं है। टोकन का header और payload केवल Base64url-एन्कोडेड JSON हैं, और ब्राउज़र और Node.js दोनों में इन्हें डीकोड करने के लिए सब कुछ उपलब्ध है। यह गाइड JWTs के लिए पूर्ण JavaScript text decoder pipeline को कवर करती है: टोकन को विभाजित करना, base64url को मानक Base64 में सामान्य करना, atob() और TextDecoder सही UTF-8 हैंडलिंग के लिए, Node.js Buffer.from(),jose के साथ सिग्नेचर सत्यापन, और वे सामान्य गलतियाँ जो डेवलपर्स को रोज़ाना परेशान करती हैं। एक quick check के लिए, इसके बजाय ऑनलाइन JWT Decoder आज़माएं। सभी उदाहरण ES2020+ और Node.js 18+ को लक्षित करते हैं।
- ✓JWT को "." पर विभाजित करें — index 0 header है, index 1 payload है, index 2 signature है।
- ✓atob() Base64 डीकोड करता है लेकिन Latin-1 लौटाता है, UTF-8 नहीं। गैर-ASCII claims के लिए TextDecoder या Buffer.from() उपयोग करें।
- ✓Buffer.from(segment, "base64url") Node.js में base64url को नेटिव रूप से हैंडल करता है — कोई manual character replacement आवश्यक नहीं।
- ✓डीकोडिंग सत्यापन नहीं है। सर्वर-साइड पर सिग्नेचर जांचे बिना decoded JWT के claims पर कभी भरोसा न करें।
- ✓jose लाइब्रेरी दोनों करती है: यह HS256/RS256/ES256 सिग्नेचर सत्यापित करती है और एक कॉल में decoded payload लौटाती है।
JWT Decoding क्या है?
एक JSON Web Token तीन Base64url-एन्कोडेड खंड होते हैं जो डॉट्स से अलग होते हैं। पहला खंड header है, दूसरा payload (वे claims जिनकी आपको वास्तव में परवाह है), और तीसरा cryptographic signature है। Header एक छोटा JSON ऑब्जेक्ट है जो टोकन का वर्णन करता है। इसका सबसे महत्वपूर्ण फ़ील्ड alg है — signing algorithm (जैसे, HS256, RS256, ES256)।typ फ़ील्ड लगभग हमेशा "JWT" होता है, और वैकल्पिक kid फ़ील्ड पहचानता है कि टोकन sign करने के लिए कौन सी key का उपयोग किया गया था — जब कोई identity provider keys rotate करता है और multiple public keys के साथ JWKS endpoint प्रकाशित करता है तो यह महत्वपूर्ण है।
Payload claims वहन करता है। RFC 7519 सात registered claim names परिभाषित करता है: sub (subject — आमतौर पर user ID), iss (issuer — auth server URL), aud (audience — वह API जिसके लिए टोकन intended है), iat (issued-at timestamp), exp (expiration timestamp), nbf (not-before timestamp), और jti (JWT ID — replay attacks रोकने के लिए उपयोग)। सभी timestamps Unix epoch seconds हैं, milliseconds नहीं। Signature खंड raw binary है — एक keyed HMAC digest या एक asymmetric digital signature। यह अन्य खंडों की तरह Base64url-एन्कोडेड है, लेकिन इसके bytes JSON नहीं हैं और कोई human-readable संरचना नहीं है।
व्यवहार में, आप तीन सामान्य कारणों से JavaScript में JWTs डीकोड करते हैं। पहला, debugging: आपके पास OAuth flow या test environment से एक टोकन है और आप confirm करना चाहते हैं कि claims वही हैं जो auth server ने issue किए होने चाहिए थे। दूसरा, क्लाइंट साइड पर display उद्देश्यों के लिए user claims पढ़ना — बिना अतिरिक्त API कॉल के token payload से लॉग-इन यूज़र का नाम, avatar URL, या role badge दिखाना। तीसरा, refresh प्रयास से पहले expiry जांचना: यदि exp अगले 60 सेकंड में है, तो 401 response की प्रतीक्षा करने के बजाय अगले API कॉल से पहले silent refresh trigger करें।
डीकोडिंग यह नहीं बताती कि टोकन वैध है या छेड़छाड़ की गई है। यह एक अलग ऑपरेशन है जिसे verification कहते हैं, जिसके लिए HMAC secret या RSA/ECDSA public key आवश्यक है। कोई भी JWT डीकोड कर सकता है। केवल सही key का धारक ही एक को verify कर सकता है। यह अंतर कई डेवलपर्स को परेशान करता है, विशेष रूप से क्लाइंट-साइड auth flows बनाते समय जहाँ decoded claims प्रदर्शित होते हैं लेकिन verified backend जांच के बिना authorization निर्णयों के लिए कभी भरोसा नहीं किया जाना चाहिए।
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOTIxZiIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTcxMTYxMDAwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
// Header
{ "alg": "HS256" }
// Payload
{
"sub": "usr_921f",
"role": "admin",
"iat": 1711610000
}atob() + TextDecoder — ब्राउज़र-नेटिव JWT Decode
JWT डीकोड करने के लिए ब्राउज़र-नेटिव pipeline में चार चरण हैं। पहला, तीन खंड पाने के लिए टोकन स्ट्रिंग को "." पर विभाजित करें। दूसरा, base64url खंड को सामान्य करें: - को + से और _ को / से बदलें, फिर = कैरेक्टर के साथ पैड करें जब तक लंबाई 4 की गुणज न हो। तीसरा, atob() कॉल करें Base64 को बाइनरी स्ट्रिंग में डीकोड करने के लिए। चौथा, बाइनरी स्ट्रिंग को TextDecoder का उपयोग करके सही UTF-8 में परिवर्तित करें। वह अंतिम चरण महत्वपूर्ण है क्योंकि atob() Latin-1 लौटाता है। मल्टी-बाइट कैरेक्टर — emoji, CJK text, Latin-1 range से परे accented कैरेक्टर — JavaScript text decoder चरण के बिना गड़बड़ा जाते हैं।
const token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOTIxZiIsInJvbGUiOiJhZG1pbiIsImlhdCI6MTcxMTYxMDAwMH0.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk";
function decodeJwtPayload(jwt) {
const base64Url = jwt.split(".")[1];
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
const padded = base64.padEnd(base64.length + (4 - (base64.length % 4)) % 4, "=");
const binary = atob(padded);
const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0));
const json = new TextDecoder("utf-8").decode(bytes);
return JSON.parse(json);
}
console.log(decodeJwtPayload(token));
// { sub: "usr_921f", role: "admin", iat: 1711610000 }Padding चरण को अनदेखा करना आसान है। JWTs अपने Base64url खंडों से trailing = कैरेक्टर हटा देते हैं क्योंकि JWT specification (RFC 7515) बिना padding के base64url परिभाषित करती है। लेकिन कुछ ब्राउज़र engines में atob() एक InvalidCharacterError throw करता है यदि input की लंबाई 4 से विभाज्य नहीं है। padEnd() के साथ defensively padding करने से सभी environments में उस edge case से बचाव होता है। यहाँ एक reusable version है जो header और payload दोनों को अलग-अलग objects में डीकोड करती है:
function decodeBase64Url(segment) {
const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
const padded = base64.padEnd(base64.length + (4 - (base64.length % 4)) % 4, "=");
const binary = atob(padded);
const bytes = Uint8Array.from(binary, ch => ch.charCodeAt(0));
return new TextDecoder("utf-8").decode(bytes);
}
function decodeJwt(token) {
const [headerB64, payloadB64] = token.split(".");
return {
header: JSON.parse(decodeBase64Url(headerB64)),
payload: JSON.parse(decodeBase64Url(payloadB64)),
};
}
const { header, payload } = decodeJwt(token);
console.log("Algorithm:", header.alg); // "HS256"
console.log("Subject:", payload.sub); // "usr_921f"
console.log("Role:", payload.role); // "admin"एक बार जब आपके पास ये दो functions हों, तो इन्हें files में copy-paste करने के बजाय एक shared utility module में रखना उचित है। src/lib/jwt.ts या utils/jwt-decode.ts फ़ाइल एक typed return shape के साथ codebase में intent को स्पष्ट करती है। TypeScript में, आप return को { header: JwtHeader; payload: JwtPayload } के रूप में type कर सकते हैं जहाँ JwtHeader में alg, typ, और optional kid शामिल है, और JwtPayload RFC 7519 registered claims को custom claims के लिए index signature के साथ extend करता है। Decode logic को centralize करने का मतलब है कि जब आप बाद में error handling (malformed segments को catch करना) या telemetry (decode failures को log करना) जोड़ना चाहते हैं, तो आपके पास अपडेट करने के लिए केवल एक जगह है।
TextDecoder चरण वही है जो इस pipeline को गैर-ASCII claims के लिए सुरक्षित बनाता है। इसके बिना, atob() एक Latin-1 स्ट्रिंग लौटाता है जहाँ मल्टी-बाइट UTF-8 सीक्वेंस कैरेक्टर में विभाजित होते हैं। emoji या CJK text की जगह आपको गड़बड़ दिखेगा। हमेशा atob() के बाद new TextDecoder("utf-8") से pipe करें।मल्टी-बाइट कैरेक्टर के साथ UTF-8 JWT Claims डीकोड करना
JWT payloads UTF-8 JSON हैं जो base64url के रूप में एन्कोड हैं। अधिकांश payloads में केवल ASCII fields होते हैं जैसे user IDs और timestamps, इसलिए डेवलपर्स कभी नोटिस नहीं करते कि atob() UTF-8 के बजाय Latin-1 लौटाता है। समस्या तब सामने आती है जब किसी claim में emoji, Japanese characters, Cyrillic, या U+00FF से ऊपर कोई code point होता है। JavaScript decode UTF-8 pattern के लिए पहले बाइनरी स्ट्रिंग को byte array में परिवर्तित करना आवश्यक है, फिर इसे TextDecoder से चलाना।
// emoji और CJK कैरेक्टर के साथ JWT payload का अनुकरण
const payloadObj = {
sub: "usr_e821",
display_name: "田中太郎",
team: "Platform 🚀",
region: "ap-northeast-1"
};
// Encode: object → JSON → UTF-8 bytes → base64url
const jsonStr = JSON.stringify(payloadObj);
const utf8Bytes = new TextEncoder().encode(jsonStr);
const base64 = btoa(String.fromCharCode(...utf8Bytes))
.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
// Decode: base64url → base64 → binary string → bytes → UTF-8 string
const base64Std = base64.replace(/-/g, "+").replace(/_/g, "/");
const binary = atob(base64Std);
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
const decoded = new TextDecoder("utf-8").decode(bytes);
const result = JSON.parse(decoded);
console.log(result.display_name); // "田中太郎" — सही
console.log(result.team); // "Platform 🚀" — सहीपुराने codebases में एक legacy fallback pattern मिलेगा जो decodeURIComponent को percent-encoding trick के साथ उपयोग करता है। यह JavaScript decodeURIComponent वाला तरीका काम करता है क्योंकि यह हर byte को percent-hex pair के रूप में re-encode करता है, फिर decodeURIComponent मल्टी-बाइट UTF-8 सीक्वेंस को पुनः जोड़ता है:
function decodeBase64UrlLegacy(segment) {
const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
const binary = atob(base64);
// प्रत्येक char को %XX hex में बदलें, फिर decodeURIComponent UTF-8 को पुनः जोड़ता है
const utf8 = decodeURIComponent(
binary.split("").map(c =>
"%" + c.charCodeAt(0).toString(16).padStart(2, "0")
).join("")
);
return utf8;
}
// TextDecoder के बिना गैर-ASCII claims के लिए काम करता है
const payload = decodeBase64UrlLegacy(token.split(".")[1]);
console.log(JSON.parse(payload));decodeURIComponent(escape(atob(segment))) pattern मिल सकता है। escape() function deprecated और non-standard है। इसे ऊपर दिखाए गए TextDecoder दृष्टिकोण से बदलें। JavaScript unescape decoder pattern में भी वही समस्या है: unescape() deprecated है। दोनों functions को भविष्य के JavaScript engines से हटाया जा सकता है।JWT Decode Pipeline — चरण संदर्भ
ब्राउज़र-नेटिव JWT decode pipeline में प्रत्येक चरण, उपयोग किया गया JavaScript API और यह क्या उत्पन्न करता है:
Node.js equivalent चरण 2 से 4 को एक single call में सिकोड़ता है: Buffer.from(segment, "base64url").toString("utf-8")।"base64url" encoding option alphabet conversion और padding को internally हैंडल करता है।
Buffer.from() — JWTs के लिए Node.js String Decoder
Node.js का पथ बहुत सरल है। Buffer class सीधे "base64url" encoding स्वीकार करती है, इसलिए आप manual character replacement और padding छोड़ सकते हैं। यह server-side कोड के लिए JavaScript string decoder का सही तरीका है। एक line एक JWT segment को UTF-8 string में बदल देती है, और यह बिना किसी अतिरिक्त चरण के मल्टी-बाइट characters को सही ढंग से हैंडल करती है।
const token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOTIxZiIsIm9yZyI6ImFjbWUtY29ycCIsInJvbGUiOiJiaWxsaW5nIiwiaWF0IjoxNzExNjEwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
function decodeJwt(jwt) {
const segments = jwt.split(".");
return {
header: JSON.parse(Buffer.from(segments[0], "base64url").toString("utf-8")),
payload: JSON.parse(Buffer.from(segments[1], "base64url").toString("utf-8")),
};
}
const { header, payload } = decodeJwt(token);
console.log(header);
// { alg: "HS256" }
console.log(payload);
// { sub: "usr_921f", org: "acme-corp", role: "billing", iat: 1711610000 }यही तरीका है जो मैं हर Node.js project में use करता हूँ। यह shorter, faster है, और पहले से UTF-8 को सही तरह हैंडल करता है। कोई TextDecoder नहीं चाहिए, कोई character replacement नहीं, कोई padding math नहीं। Buffer class एक JavaScript string decoder है जो base64url alphabet को नेटिव रूप से हैंडल करती है, जो character substitution से संबंधित bugs की एक पूरी श्रेणी को समाप्त करती है। यदि आपके कोड को ब्राउज़र और Node.js दोनों में चलाना है, तो एक isomorphic wrapper function के लिए नीचे FAQ देखें जो runtime पर environment detect करती है।
यहाँ एक अधिक complete example है जो दिखाता है कि सामान्य JWT claims कैसे निकालें और timestamps को readable dates में कैसे convert करें, जो middleware और API route handlers में सबसे अधिक उपयोग किया जाने वाला pattern है:
function inspectToken(token) {
const segments = token.split(".");
if (segments.length !== 3) {
throw new Error("Not a valid JWT — expected 3 dot-separated segments");
}
const header = JSON.parse(Buffer.from(segments[0], "base64url").toString("utf-8"));
const payload = JSON.parse(Buffer.from(segments[1], "base64url").toString("utf-8"));
const inspection = {
algorithm: header.alg,
tokenType: header.typ || "JWT",
subject: payload.sub,
issuer: payload.iss || "(not set)",
audience: payload.aud || "(not set)",
issuedAt: payload.iat ? new Date(payload.iat * 1000).toISOString() : "(not set)",
expiresAt: payload.exp ? new Date(payload.exp * 1000).toISOString() : "(never)",
isExpired: payload.exp ? payload.exp < Math.floor(Date.now() / 1000) : false,
customClaims: Object.keys(payload).filter(
k => !["sub", "iss", "aud", "iat", "exp", "nbf", "jti"].includes(k)
),
};
return inspection;
}
console.log(inspectToken(process.env.ACCESS_TOKEN));
// {
// algorithm: "RS256",
// tokenType: "JWT",
// subject: "usr_921f",
// issuer: "https://auth.internal",
// audience: "billing-api",
// issuedAt: "2026-03-10T14:00:00.000Z",
// expiresAt: "2026-03-10T15:00:00.000Z",
// isExpired: true,
// customClaims: ["role", "scope", "org"]
// }Production Node.js services में, Buffer.from() decode pattern तीन recurring स्थानों पर दिखता है। पहला है request logging middleware: आप incoming Authorization header को decode करते हैं userId और org को auth server पर extra network round-trip के बिना हर structured log entry में attach करने के लिए। दूसरा है debugging: आप test assertions लिखने से पहले confirm करने के लिए development के दौरान decoded token claims को console पर print करते हैं कि सही scopes issue किए गए थे। तीसरा है API gateways में proactive token refresh। Token को upstream forward करने और downstream service को 401 return करने देने के बजाय जब token mid-request expire हो जाए, gateway edge पर token decode करता है, exp claim पढ़ता है, और यदि expiry अगले 30 सेकंड में है तो refresh trigger करता है। इससे transient auth failures की एक श्रेणी समाप्त होती है जो reproduce करना कठिन और debug करना frustrating है।
"base64url" encoding Node.js 15.7.0 में जोड़ी गई थी। यदि आप Node.js 14 या उससे पहले पर अटके हैं, तो Buffer.from(segment.replace(/-/g, "+").replace(/_/g, "/"), "base64") पर वापस जाएं जो उसी तरह काम करता है लेकिन manual character swap की आवश्यकता है।फ़ाइल और API Response से JWT Decode करना
दो scenarios लगातार आते हैं। पहला है local file से JWT पढ़ना: development के दौरान एक saved token, एक test fixture, या incident के दौरान dump की गई file post-mortem analysis के लिए। दूसरा है HTTP response से JWT निकालना, आमतौर पर OAuth token response body में access_token field या Authorization header। दोनों को error handling की आवश्यकता है क्योंकि malformed tokens, truncated files, और network errors रोज़ाना की वास्तविकताएं हैं। पिछले सप्ताह valid रहा एक token copy-paste से trailing whitespace या newlines के साथ आ सकता है। Response body HTML हो सकता है JSON के बजाय यदि auth server ने error page return किया।
फ़ाइल से JWT पढ़ना (Node.js)
import { readFileSync } from "node:fs";
function decodeJwtFromFile(filePath) {
const raw = readFileSync(filePath, "utf-8").trim();
const segments = raw.split(".");
if (segments.length !== 3) {
throw new Error(`Invalid JWT: expected 3 segments, got ${segments.length}`);
}
try {
return {
header: JSON.parse(Buffer.from(segments[0], "base64url").toString("utf-8")),
payload: JSON.parse(Buffer.from(segments[1], "base64url").toString("utf-8")),
};
} catch (err) {
throw new Error(`Failed to decode JWT from ${filePath}: ${err.message}`);
}
}
try {
const { header, payload } = decodeJwtFromFile("./test-fixtures/access-token.txt");
console.log("Algorithm:", header.alg);
console.log("Expires:", new Date(payload.exp * 1000).toISOString());
} catch (err) {
console.error(err.message);
}API Response से JWT निकालना (fetch)
async function fetchAndDecodeToken(loginUrl, credentials) {
const response = await fetch(loginUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(credentials),
});
if (!response.ok) {
throw new Error(`Login failed: ${response.status} ${response.statusText}`);
}
const { access_token } = await response.json();
if (!access_token || access_token.split(".").length !== 3) {
throw new Error("Response does not contain a valid JWT");
}
const payload = access_token.split(".")[1];
const json = Buffer.from(payload, "base64url").toString("utf-8");
return JSON.parse(json);
}
// उपयोग
try {
const claims = await fetchAndDecodeToken(
"https://auth.internal/oauth/token",
{ username: "deploy-bot", password: process.env.DEPLOY_TOKEN }
);
console.log("Token subject:", claims.sub);
console.log("Token scopes:", claims.scope);
console.log("Expires at:", new Date(claims.exp * 1000).toISOString());
} catch (err) {
console.error("Token decode error:", err.message);
}Command-Line JWT Decoding
कभी-कभी आप बस script लिखे बिना terminal से एक token पर नज़र डालना चाहते हैं। Node.js अधिकांश developer machines पर उपलब्ध है, इसलिए one-liner अच्छा काम करता है। jq pretty-printing संभालता है।
# Node.js one-liner से JWT payload decode करें
echo "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOTIxZiIsInJvbGUiOiJhZG1pbiJ9.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk" \
| cut -d. -f2 \
| node -e "process.stdin.on('data', d => console.log(JSON.parse(Buffer.from(d.toString().trim(), 'base64url').toString('utf-8'))))"
# pretty output के लिए jq से pipe करें
echo "$JWT_TOKEN" | cut -d. -f2 \
| node -e "process.stdin.on('data', d => process.stdout.write(Buffer.from(d.toString().trim(), 'base64url').toString('utf-8')))" \
| jq .
# header और payload दोनों decode करें
echo "$JWT_TOKEN" | node -e "
process.stdin.on('data', d => {
const parts = d.toString().trim().split('.');
console.log('Header:', JSON.parse(Buffer.from(parts[0], 'base64url').toString()));
console.log('Payload:', JSON.parse(Buffer.from(parts[1], 'base64url').toString()));
});
"यदि आप Node.js के बिना pure bash पसंद करते हैं, तो segment को base64 -d से pipe करें tr के साथ base64url characters fix करने के बाद:
# Pure bash: Node.js के बिना JWT payload decode करें echo "$JWT_TOKEN" | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null | jq . # macOS variant (base64 -D instead of -d) echo "$JWT_TOKEN" | cut -d. -f2 | tr '_-' '/+' | base64 -D 2>/dev/null | jq .
बिना किसी terminal के त्वरित visual inspection के लिए, अपना token ToolDeck JWT Decoder में paste करें रंग-कोडेड claim labels और expiration status के साथ सभी तीन segments के side-by-side breakdown के लिए।
jose — एक Library में Verification और Decoding
Production authentication middleware के लिए, आपको केवल decoding नहीं बल्कि signature verification चाहिए। jose library सबसे अच्छा विकल्प है। यह Node.js और browsers दोनों में काम करती है (Web Crypto API के माध्यम से), HS256, RS256, ES256, EdDSA, और JWE (encrypted tokens) को support करती है, और इसमें zero native dependencies हैं। npm install jose से install करें।
import * as jose from "jose";
const secret = new TextEncoder().encode("k8s-webhook-signing-secret-2026");
const token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOTIxZiIsInNjb3BlIjoiYmlsbGluZzpyZWFkIiwiaWF0IjoxNzExNjEwMDAwLCJleHAiOjE3MTE2MTM2MDB9.abc123";
try {
const { payload, protectedHeader } = await jose.jwtVerify(token, secret);
console.log("Algorithm:", protectedHeader.alg); // "HS256"
console.log("Subject:", payload.sub); // "usr_921f"
console.log("Scope:", payload.scope); // "billing:read"
} catch (err) {
if (err.code === "ERR_JWT_EXPIRED") {
console.error("Token expired at:", err.payload.exp);
} else {
console.error("Verification failed:", err.message);
}
}import * as jose from "jose";
// identity provider से public key set fetch करें
const jwks = jose.createRemoteJWKSet(
new URL("https://auth.internal/.well-known/jwks.json")
);
const token = req.headers.authorization?.split(" ")[1];
if (!token) {
return res.status(401).json({ error: "Missing token" });
}
try {
const { payload } = await jose.jwtVerify(token, jwks, {
issuer: "https://auth.internal",
audience: "billing-api",
});
// payload.sub, payload.scope, etc. अब verified हैं
req.userId = payload.sub;
} catch (err) {
return res.status(401).json({ error: "Invalid token" });
}jose और पुराने jsonwebtoken package के बीच चुनते समय, मुख्य अंतर runtime scope है। jsonwebtoken केवल Node.js है — यह crypto built-in पर निर्भर करता है और ब्राउज़र के लिए bundle नहीं होगा। jose पूरी तरह isomorphic है: यह Web Crypto API उपयोग करता है, जो सभी modern browsers, Node.js 16+, Deno, Bun, और Cloudflare Workers में उपलब्ध है। यदि आपका auth logic Next.js middleware file में है (जो Edge Runtime में चलती है), या Cloudflare Worker में, या एक shared utility में जो server और client दोनों code द्वारा import की जाती है, jose सही choice है क्योंकि इसमें zero native dependencies हैं और बिना build step के install होती है। jsonwebtoken pure Node.js server applications के लिए उचित रहती है जहाँ आपको इसके signing helpers के व्यापक ecosystem की आवश्यकता है और आप code को edge environment में चलाने की योजना नहीं बना रहे हैं। 2026 में एक greenfield project में, default रूप से jose का उपयोग करें जब तक पुराने API को prefer करने का कोई specific कारण न हो।
यदि आपको केवल verification के बिना decode चाहिए, jose प्रदान करता है jose.decodeJwt(token) जो payload लौटाता है और jose.decodeProtectedHeader(token) header के लिए। ये convenience functions हैं जो Base64url decoding internally करते हैं। लेकिन jose तक पहुँचने का पूरा कारण यह है कि आपको शायद ही कभी verification के बिना decode करना चाहिए। यदि आप client side पर हैं और बस user को token claims से उनका display नाम या avatar URL दिखाना चाहते हैं, तो decode-only ठीक है। Server side पर, हमेशा verify करें। मैंने production systems देखे हैं जो signature check किए बिना access control decisions के लिए JWT claims decode करते थे, और वह किसी भी हमलावर के लिए खुला दरवाज़ा है जो JWT format समझता है।
import * as jose from "jose";
// Decode-only: कोई secret नहीं, कोई verification नहीं
const payload = jose.decodeJwt(token);
console.log(payload.sub); // "usr_921f"
console.log(payload.scope); // "billing:read"
const header = jose.decodeProtectedHeader(token);
console.log(header.alg); // "HS256"
console.log(header.typ); // "JWT"
// verification के बिना expiry जांचें (client-side display)
if (payload.exp && payload.exp < Math.floor(Date.now() / 1000)) {
console.log("Token expired हो गया है — login पर redirect करें");
}Syntax Highlighting के साथ Terminal Output
Node.js CLI tool में या incident के दौरान JWT tokens debug करते समय, color-coded output वास्तव में फर्क करता है। chalk library JSON.stringify के साथ काम पूरा कर देती है। npm install chalk से install करें।
import chalk from "chalk";
function printJwt(token) {
const segments = token.split(".");
if (segments.length !== 3) {
console.error(chalk.red("Invalid JWT: expected 3 segments"));
return;
}
const header = JSON.parse(Buffer.from(segments[0], "base64url").toString("utf-8"));
const payload = JSON.parse(Buffer.from(segments[1], "base64url").toString("utf-8"));
console.log(chalk.bold.cyan("\n=== JWT Header ==="));
console.log(chalk.gray(JSON.stringify(header, null, 2)));
console.log(chalk.bold.green("\n=== JWT Payload ==="));
console.log(chalk.gray(JSON.stringify(payload, null, 2)));
// expiration status highlight करें
if (payload.exp) {
const expiresAt = new Date(payload.exp * 1000);
const isExpired = expiresAt < new Date();
console.log(
chalk.bold("\nExpires:"),
isExpired
? chalk.red(`EXPIRED at ${expiresAt.toISOString()}`)
: chalk.green(`Valid until ${expiresAt.toISOString()}`)
);
}
console.log(chalk.dim("\nSignature: " + segments[2].substring(0, 20) + "..."));
}
printJwt(process.argv[2]);
// Run: node jwt-debug.mjs "eyJhbGci..."बड़ी Log Files से JWTs प्रोसेस करना
Modern API infrastructure NDJSON format में structured access logs emit करता है — प्रति line एक JSON object, जिसमें प्रत्येक line request path, response status, latency, और decoded या raw Authorization header होता है। एक busy service में ये files तेज़ी से बढ़ती हैं: 10,000 requests प्रति minute संभालने वाला gateway प्रतिदिन 14 million से अधिक log entries उत्पन्न करता है। Security और compliance use cases को नियमित रूप से इन files को बाद में scan करने की आवश्यकता होती है — एक compromised service account द्वारा किए गए हर request की पहचान करना (post-incident analysis), confirm करना कि एक specific user के tokens data-access window से पहले expire हुए (compliance audit), या एक sensitive endpoint तक पहुँचने वाले सभी subjects का पूरा set निकालना maintenance window के दौरान। क्योंकि एक single log file कई gigabytes से अधिक हो सकती है, readFileSync के साथ इसे memory में load करना व्यावहारिक नहीं है। Node.js readline streams file को एक समय में एक line process करते हैं constant memory overhead के साथ, जिससे एक standard developer laptop पर arbitrarily large logs scan करना practical हो जाता है।
आपको individual JWTs के साथ "file too large for memory" समस्या नहीं होगी, क्योंकि एक single token शायद ही कुछ kilobytes से अधिक होता है। जो scenario सामने आती है वह है एक large access log या audit trail को JWT tokens के लिए scan करना, प्रत्येक को decode करना, और specific claims निकालना। Node.js streams यह पूरी file load किए बिना handle करते हैं।
import { createReadStream } from "node:fs";
import { createInterface } from "node:readline";
async function scanLogsForExpiredTokens(logPath) {
const fileStream = createReadStream(logPath, { encoding: "utf-8" });
const rl = createInterface({ input: fileStream, crlfDelay: Infinity });
let lineCount = 0;
let expiredCount = 0;
const nowSeconds = Math.floor(Date.now() / 1000);
for await (const line of rl) {
lineCount++;
try {
const entry = JSON.parse(line);
if (!entry.authorization_token) continue;
const segments = entry.authorization_token.split(".");
if (segments.length !== 3) continue;
const payload = JSON.parse(
Buffer.from(segments[1], "base64url").toString("utf-8")
);
if (payload.exp && payload.exp < nowSeconds) {
expiredCount++;
const expDate = new Date(payload.exp * 1000).toISOString();
console.log("Line " + lineCount + ": expired token for " + payload.sub + ", exp=" + expDate);
}
} catch {
// Malformed lines skip करें
}
}
console.log(`\nScanned ${lineCount} lines, found ${expiredCount} expired tokens`);
}
scanLogsForExpiredTokens("./logs/api-access-2026-03.ndjson");import { createReadStream } from "node:fs";
import { createInterface } from "node:readline";
async function extractUniqueSubjects(logPath) {
const rl = createInterface({
input: createReadStream(logPath, { encoding: "utf-8" }),
crlfDelay: Infinity,
});
const subjects = new Set();
const jwtRegex = /eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g;
for await (const line of rl) {
const matches = line.match(jwtRegex);
if (!matches) continue;
for (const token of matches) {
try {
const payload = JSON.parse(
Buffer.from(token.split(".")[1], "base64url").toString("utf-8")
);
if (payload.sub) subjects.add(payload.sub);
} catch {
// Valid JWT नहीं है
}
}
}
console.log(`Found ${subjects.size} unique subjects:`);
for (const sub of subjects) console.log(` ${sub}`);
}
extractUniqueSubjects("./logs/gateway-2026-03.log");readFileSync के साथ 500 MB NDJSON file load करने से memory pin होगी और GC pauses trigger होंगे। readline approach constant memory usage के साथ एक time में एक line process करता है।सामान्य गलतियाँ
समस्या: atob() एक Latin-1 स्ट्रिंग लौटाता है। मल्टी-बाइट UTF-8 कैरेक्टर (emoji, CJK, accented characters) कैरेक्टर में विभाजित हो जाते हैं और गड़बड़ा कर बाहर आते हैं।
समाधान: atob() आउटपुट को Uint8Array में परिवर्तित करें, फिर इसे new TextDecoder('utf-8') से पास करें।
// गैर-ASCII payload claims पर टूटता है
const payload = JSON.parse(atob(token.split(".")[1]));
// display_name "ç°ä¸å¤ªé\x83\x8E" के रूप में दिखता है "田中太郎" की जगहconst binary = atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"));
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
const payload = JSON.parse(new TextDecoder("utf-8").decode(bytes));
// display_name सही तरह "田中太郎" दिखाता हैसमस्या: atob() "InvalidCharacterError" throw करता है क्योंकि base64url + और / के बजाय - और _ उपयोग करता है।
समाधान: atob() कॉल करने से पहले - को + से और _ को / से बदलें। Node.js Buffer.from() के साथ 'base64url' यह automatically handle करता है।
// Throws: InvalidCharacterError: String contains an invalid character
const payload = atob(token.split(".")[1]);const segment = token.split(".")[1];
const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
const payload = atob(base64); // अब काम करता हैसमस्या: कोई भी किसी भी payload के साथ JWT बना सकता है। Decoding केवल data पढ़ता है — यह साबित नहीं करता कि token आपके auth server द्वारा issue किया गया था।
समाधान: Server side पर, jose.jwtVerify() या jsonwebtoken.verify() का उपयोग करके हमेशा signature verify करें। Client-side पर user claims प्रदर्शित करने के लिए Decode-only स्वीकार्य है।
// खतरनाक: decoded लेकिन verified नहीं
const claims = JSON.parse(atob(token.split(".")[1]));
if (claims.role === "admin") {
grantAdminAccess(); // हमलावर इसे forge कर सकता है
}import * as jose from "jose";
const { payload } = await jose.jwtVerify(token, secretKey);
if (payload.role === "admin") {
grantAdminAccess(); // सुरक्षित — signature verified है
}समस्या: JWT exp epoch से seconds में है, लेकिन Date.now() milliseconds लौटाता है। तुलना हमेशा कहेगी कि token valid है क्योंकि millisecond timestamp 1000 गुना बड़ा है।
समाधान: exp से तुलना करने से पहले Date.now() को 1000 से विभाजित करें और floor लें।
// Bug: Date.now() milliseconds है, exp seconds है
if (payload.exp > Date.now()) {
console.log("Token is valid"); // हमेशा true — गलत!
}const nowSeconds = Math.floor(Date.now() / 1000);
if (payload.exp > nowSeconds) {
console.log("Token is valid"); // सही तुलना
}JWT Decode Methods — त्वरित तुलना
Browser-side decode के लिए जब आपको बस claims user को display करने हैं तो atob() + TextDecoder उपयोग करें। Node.js scripts और CLI tools में Buffer.from() उपयोग करें। jose तक पहुँचें जब आपको signature verify करने की आवश्यकता हो, जो कि किसी भी server-side auth middleware के लिए है। jwt-decode package एक lightweight alternative है यदि आप ब्राउज़र में decode-only के लिए one-function API चाहते हैं। कोड लिखे बिना त्वरित visual inspection के लिए, अपना token JWT Decoder tool में paste करें।
अक्सर पूछे जाने वाले प्रश्न
बिना किसी लाइब्रेरी के JavaScript में JWT टोकन को कैसे डीकोड करें?
टोकन को "." पर विभाजित करें, दूसरा खंड (payload) लें, - को + से और _ को / से बदलकर base64url एन्कोडिंग को सामान्य करें, = कैरेक्टर से पैड करें। फिर atob() कॉल करें और UTF-8 JSON स्ट्रिंग पाने के लिए TextDecoder का उपयोग करें। परिणाम को JSON.parse() से पास करें और आपके पास claims ऑब्जेक्ट होगा। कोई npm पैकेज आवश्यक नहीं। यह दृष्टिकोण सभी आधुनिक ब्राउज़रों और Node.js 18+ में काम करता है। यदि आपको header भी पढ़ना है, तो पहले खंड पर भी यही डीकोडिंग चरण लागू करें। ध्यान रखें कि यह बिना किसी सिग्नेचर सत्यापन के कच्चा डेटा देता है — सर्वर-साइड पर सिग्नेचर सत्यापित करने तक परिणाम को केवल प्रदर्शन के लिए मानें।
const token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c3JfOTIxZiIsInJvbGUiOiJhZG1pbiJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
const payload = token.split(".")[1];
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
const json = atob(base64);
const claims = JSON.parse(json);
console.log(claims);
// { sub: "usr_921f", role: "admin" }JWT डीकोडिंग के लिए atob() और Buffer.from() में क्या अंतर है?
atob() एक ब्राउज़र API है जो मानक Base64 को Latin-1 बाइनरी स्ट्रिंग में डीकोड करता है। यह base64url एन्कोडिंग को सीधे नहीं समझता, इसलिए पहले - और _ कैरेक्टर बदलने पड़ते हैं। Buffer.from(segment, "base64url") एक Node.js API है जो base64url अल्फाबेट को नेटिव रूप से हैंडल करता है और एक Buffer लौटाता है जिस पर .toString("utf-8") कॉल किया जा सकता है। ब्राउज़र में atob() और Node.js में Buffer.from() का उपयोग करें। एक तीसरा विकल्प — जो धीमा है लेकिन ऐतिहासिक रूप से सामान्य — decodeURIComponent प्रतिशत-एन्कोडिंग ट्रिक है, लेकिन यह पैटर्न कुछ पुराने स्निपेट में deprecated escape() फ़ंक्शन पर निर्भर करता है और नए कोड में इससे बचना चाहिए। ऐसे isomorphic कोड के लिए जो दोनों वातावरणों में चले, typeof Buffer !== "undefined" जांचें और उसके अनुसार शाखा लें।
// ब्राउज़र
const json = atob(payload.replace(/-/g, "+").replace(/_/g, "/"));
// Node.js
const json2 = Buffer.from(payload, "base64url").toString("utf-8");गैर-ASCII JWT claims पर atob() क्यों टूट जाता है?
atob() एक Latin-1 स्ट्रिंग लौटाता है जहाँ प्रत्येक कैरेक्टर एक बाइट से मेल खाता है। मल्टी-बाइट UTF-8 सीक्वेंस (emoji, CJK कैरेक्टर, Latin-1 से परे accented अक्षर) कई कैरेक्टर में विभाजित हो जाते हैं, जिससे गड़बड़ आउटपुट मिलता है। समाधान यह है कि बाइनरी स्ट्रिंग को पहले Uint8Array में परिवर्तित करें, फिर उस array को new TextDecoder("utf-8").decode() में पास करें। TextDecoder API मल्टी-बाइट सीक्वेंस को सही तरह से पुनः जोड़ता है। यह समस्या विकास में आसानी से नज़र नहीं आती क्योंकि अधिकांश JWT payload में केवल ASCII यूज़र IDs, timestamps और role नाम होते हैं — बग तब सामने आता है जब किसी claim में गैर-ASCII display नाम या स्थानीयकृत स्ट्रिंग होती है। नए कोड में हमेशा TextDecoder पथ का उपयोग करें, भले ही आपके वर्तमान payload केवल ASCII हों, क्योंकि जैसे-जैसे एप्लिकेशन विकसित होगी, claims बदल सकते हैं।
// टूटा हुआ: atob Latin-1 लौटाता है, मल्टी-बाइट chars गड़बड़ा जाते हैं
const broken = atob(base64); // "ð\x9F\x8E\x89" emoji की जगह
// ठीक: बाइट array में बदलें, फिर TextDecoder
const bytes = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
const fixed = new TextDecoder("utf-8").decode(bytes);क्या मैं JavaScript में JWT सिग्नेचर सत्यापित कर सकता हूँ?
डीकोडिंग और सत्यापन अलग-अलग ऑपरेशन हैं। डीकोडिंग केवल payload पढ़ता है, जो एन्क्रिप्टेड नहीं है। सत्यापन एक secret (HMAC) या public key (RSA/ECDSA) के विरुद्ध सिग्नेचर की जांच करता है। jose लाइब्रेरी ब्राउज़र में Web Crypto API के माध्यम से और Node.js में दोनों को सपोर्ट करती है। jsonwebtoken पैकेज केवल Node.js में काम करता है। सर्वर साइड पर सिग्नेचर सत्यापित किए बिना कभी decoded claims पर भरोसा न करें। क्लाइंट साइड पर उपयोगकर्ता का display नाम या समाप्ति समय पढ़ने के लिए JWT को डीकोड करना स्वीकार्य है, लेकिन कोई भी एक्सेस कंट्रोल निर्णय — जैसे यह जांचना कि किसी उपयोगकर्ता के पास कोई विशेष भूमिका या अनुमति है — सत्यापन के बाद सर्वर-साइड कोड में होना चाहिए। एक हमलावर जो JWT फॉर्मेट समझता है, मनमाने claims के साथ टोकन बना सकता है और आपकी क्लाइंट-साइड जांच पास हो जाएगी।
import * as jose from "jose";
const secret = new TextEncoder().encode("your-256-bit-secret");
const { payload } = await jose.jwtVerify(token, secret);
console.log(payload.sub); // सत्यापित claimsJavaScript में JWT की समाप्ति कैसे जांचें?
payload को डीकोड करें और exp claim पढ़ें, जो सेकंड में Unix timestamp है। इसे Math.floor(Date.now() / 1000) का उपयोग करके वर्तमान समय से तुलना करें। यदि वर्तमान समय exp से अधिक है, तो टोकन समाप्त हो गया है। याद रखें: exp मान epoch से सेकंड में है, मिलीसेकंड में नहीं, इसलिए Date.now() को 1000 से विभाजित करना आवश्यक है। व्यवहार में, एक छोटा clock-skew buffer बनाएं — यह जांचें कि टोकन अगले 30 सेकंड में समाप्त होने वाला है, न कि सख्ती से अतीत में। इससे वे edge cases रुकते हैं जहाँ आप डीकोड करते समय टोकन वैध है, लेकिन अगले downstream API कॉल तक समाप्त हो जाता है। उस स्थिति को भी संभालें जहाँ exp बिल्कुल अनुपस्थित है, जिसका अर्थ है टोकन कभी समाप्त नहीं होता।
function isTokenExpired(token) {
const payload = JSON.parse(
atob(token.split(".")[1].replace(/-/g, "+").replace(/_/g, "/"))
);
const nowSeconds = Math.floor(Date.now() / 1000);
return payload.exp < nowSeconds;
}
console.log(isTokenExpired(myToken)); // true या falseIsomorphic JWT decode कोड कैसे लिखें जो Node.js और ब्राउज़र दोनों में काम करे?
globalThis.Buffer के अस्तित्व की जांच करें। यदि यह मौजूद है, तो आप Node.js में हैं और Buffer.from(segment, "base64url").toString("utf-8") उपयोग कर सकते हैं। यदि नहीं है, तो आप ब्राउज़र में हैं और atob() को TextDecoder दृष्टिकोण के साथ उपयोग करना चाहिए। इस जांच को एक decodeBase64Url फ़ंक्शन में लपेटें और इसे हर जगह उपयोग करें। यह monorepo में utility पैकेज, design system components और किसी भी shared कोड के लिए विशेष रूप से महत्वपूर्ण है जो Next.js server component और ब्राउज़र React component दोनों द्वारा इम्पोर्ट किया जाता है। एनवायरमेंट detection को एक ही स्थान पर रखने से, runtime बदलने पर केवल एक ही जगह बदलाव करना होगा — उदाहरण के लिए, जब Deno पूर्ण Buffer सपोर्ट जोड़ता है या कोई नई edge runtime अलग code path मांगती है।
function decodeBase64Url(segment) {
if (typeof Buffer !== "undefined") {
return Buffer.from(segment, "base64url").toString("utf-8");
}
const base64 = segment.replace(/-/g, "+").replace(/_/g, "/");
const bytes = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
return new TextDecoder("utf-8").decode(bytes);
}संबंधित टूल्स
Marcus specialises in JavaScript performance, build tooling, and the inner workings of the V8 engine. He has spent years profiling and optimising React applications, working on bundler configurations, and squeezing every millisecond out of critical rendering paths. He writes about Core Web Vitals, JavaScript memory management, and the tools developers reach for when performance really matters.
Sophie is a full-stack developer focused on TypeScript across the entire stack — from React frontends to Express and Fastify backends. She has a particular interest in type-safe API design, runtime validation, and the patterns that make large JavaScript codebases stay manageable. She writes about TypeScript idioms, Node.js internals, and the ever-evolving JavaScript module ecosystem.