DEV Community

Cover image for Without JWT Library Decode and Verify a JWT Token in JavaScript
Nilesh Kumar
Nilesh Kumar

Posted on

Without JWT Library Decode and Verify a JWT Token in JavaScript

Description

JSON Web Tokens (JWT) are widely used for authentication, but just decoding them is not enoughβ€”you need to verify their signature too. In this blog, we’ll break down how to decode a JWT and verify its signature using JavaScript, all in simple terms.


πŸ” What is a JWT?

A JWT (JSON Web Token) is a compact, URL-safe token used for securely transmitting information. It has three parts:

HEADER.PAYLOAD.SIGNATURE
Enter fullscreen mode Exit fullscreen mode

Example JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiIxMjM0NSIsIm5hbWUiOiJKb2huIERvZSIsImV4cCI6MTcxNjAwMDAwMH0.ZXhhbXBsZXNpZ25hdHVyZQ
Enter fullscreen mode Exit fullscreen mode
  • Header β†’ Metadata about the token (e.g., algorithm used: HS256)
  • Payload β†’ Data like user ID, roles, and expiration time (exp)
  • Signature β†’ A secret-key-based hash to verify authenticity

Without verifying the signature, anyone can modify a JWT, which is a security risk! 😱


πŸ“Œ Step 1: Decode the JWT in JavaScript

Before verifying a JWT, let's first decode it to extract the payload (the actual data inside the token).

πŸš€ JavaScript Code to Decode JWT

function jwtDecode(token) {
  try {
    const base64Url = token.split(".")[1];
    const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
    const jsonPayload = decodeURIComponent(
      window
        .atob(base64)
        .split("")
        .map((c) => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)
        .join("")
    );

    const decoded = JSON.parse(jsonPayload);

    // Check if token has expired
    if (decoded.exp) {
      const currentTime = Math.floor(Date.now() / 1000);
      if (decoded.exp < currentTime) {
        console.warn("Token has expired!");
        return null;
      }
    }

    return decoded;
  } catch (error) {
    console.error("Invalid JWT token", error);
    return null;
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή How It Works?

  1. Splits the token into three parts and extracts the payload (middle part).
  2. Converts Base64 URL-safe encoding into a normal Base64 string.
  3. Decodes it into a readable JSON object.
  4. Checks if the token has expired using the exp field.

πŸ”΄ Problem: This does not verify the signature, so anyone could tamper with the payload! 🚨


πŸ“Œ Step 2: Verify the JWT Signature

To ensure the token is authentic and not modified, we need to verify its signature using a secret key (e.g., "test").

βœ… JavaScript Code to Verify JWT Signature

async function jwtVerify(token, secret = "test") {
  try {
    const parts = token.split(".");
    if (parts.length !== 3) throw new Error("Invalid JWT format");

    const [headerB64, payloadB64, signatureB64] = parts;
    const header = JSON.parse(atob(headerB64));
    const payload = JSON.parse(atob(payloadB64));

    const currentTime = Math.floor(Date.now() / 1000);
    if (payload.exp && payload.exp < currentTime) {
      console.warn("Token has expired!");
      return null;
    }

    const encoder = new TextEncoder();
    const keyData = encoder.encode(secret);
    const cryptoKey = await crypto.subtle.importKey("raw", keyData, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);

    const data = encoder.encode(`${headerB64}.${payloadB64}`);
    const newSignature = await crypto.subtle.sign("HMAC", cryptoKey, data);
    const newSignatureB64 = btoa(String.fromCharCode(...new Uint8Array(newSignature)))
      .replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");

    if (newSignatureB64 !== signatureB64) {
      throw new Error("Invalid signature! JWT is not authentic.");
    }

    return payload;
  } catch (error) {
    console.error("JWT Verification Failed:", error.message);
    return null;
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”Ή How It Works?

  1. Splits the JWT into three parts: Header, Payload, Signature.
  2. Decodes Header & Payload to JSON format.
  3. Checks expiration time (exp field).
  4. Recreates the signature using HMAC-SHA256 and compares it with the original signature.
  5. βœ… If valid, returns decoded payload. Otherwise, it rejects the token.

πŸ”¬ Example Usage

const token = "YOUR_JWT_HERE"; // Replace with an actual JWT
jwtVerify(token).then((decoded) => {
  if (decoded) {
    console.log("βœ… Valid Token:", decoded);
  } else {
    console.log("❌ Invalid or Expired Token.");
  }
});
Enter fullscreen mode Exit fullscreen mode

🎯 Summary

Step Action Code
1️⃣ Decode JWT Extracts and reads payload (unsafe on its own)
2️⃣ Verify Signature Ensures token authenticity using a secret key (HMAC-SHA256)
3️⃣ Check Expiration Rejects expired tokens (via exp field)

πŸ”Ή Always verify the signature before trusting a JWT!


πŸ’‘ Final Thoughts

βœ… JWTs are great for authentication, but decoding alone is not safe. Always verify the signature before accepting a token!

πŸ”’ Want even better security? Use jsonwebtoken in Node.js:

const jwt = require("jsonwebtoken");
jwt.verify(token, "test");
Enter fullscreen mode Exit fullscreen mode

Top comments (0)