Use Cases (अपडेट: 2/6/2026)

Claude Code से सुरक्षित JWT Authentication: claims, cookies, rotation और keys

Claude Code से JWT auth बनाएं: claims, cookies, refresh rotation, revocation, key rotation और safe prompts.

Claude Code से सुरक्षित JWT Authentication: claims, cookies, rotation और keys

JWT authentication demo में बहुत आसान लगता है। payload sign करो, token return करो, फिर हर API request में verify करो। Production में यही छोटा pattern जल्दी unsafe बन सकता है। अगर aud verify नहीं हुआ, refresh token बहुत long-lived है, token localStorage में रखा है, या key rotation अचानक की गई है, तो account takeover का रास्ता खुल सकता है।

इस guide में JWT basics beginner-friendly तरीके से समझाए गए हैं और फिर उन्हें Claude Code के लिए implementation brief में बदला गया है। हम claim design, signing और encryption का फर्क, Cookie/session placement, refresh-token rotation, revocation, key rotation, common security failures और safe Claude Code prompts cover करेंगे। Login layer के broader design के लिए authentication implementation, Cookie details के लिए cookie management, और permissions के लिए RBAC guide भी देखें।

Implementation करते समय primary sources देखें: RFC 7519 JWT spec है, RFC 8725 JWT hardening बताता है, RFC 9700 refresh token replay और rotation पर guidance देता है, OWASP JWT Cheat Sheet practical risks दिखाता है, MDN Set-Cookie Cookie attributes समझाता है, jose Node implementation के लिए अच्छा विकल्प है, और Claude Code settings permissions boundary समझने में मदद करता है।

Beginners के लिए JWT basics

JWT तीन parts से बना होता है: header.payload.signature। header में type और signing algorithm होता है। payload में claims होते हैं। claim का मतलब है token के बारे में statement। sub user id है, iss issuer है, aud target API है, exp expiry time है, और jti token id है।

सबसे जरूरी बात: आम JWT signed होता है, encrypted नहीं। Signature tampering detect करती है, लेकिन payload छिपाती नहीं। जिसके पास token है वह payload decode कर सकता है। इसलिए API keys, address, payment data, internal notes या unnecessary personal data payload में न रखें। अगर कोई data secret रहना चाहिए, उसे server-side session या database में रखें और JWT में सिर्फ minimal reference दें।

Claude Code को शुरुआत में ही ये rules दें।

JWT authentication implement करो।
Rules:
- JWT payload signed होता है, encrypted नहीं। इसमें secrets मत डालो।
- Access token 15 minutes या उससे कम।
- Refresh token 7 days या उससे कम।
- iss, aud, sub, exp, iat, jti validate करो।
- alg none और unexpected algorithms reject करो।
- refresh-token rotation और reuse detection implement करो।
- Cookie, CSRF, XSS, revocation और key rotation review करो।

Claims minimal रखें

JWT को user profile cache न बनाएं। access token में सिर्फ वही information रखें जो API entry पर चाहिए: stable user id, session id, tenant id, broad role और token id। Plan, suspension status, detailed permissions, quota और billing जैसी values बदलती रहती हैं। इन्हें server पर DB/cache से recheck करें।

Claimउपयोगसावधानी
subuser idemail के बजाय internal stable id बेहतर
ississuerauth server पर fixed रखें
audaudienceदूसरे API के token reject करें
expexpiryaccess token short-lived रखें
jtitoken idrevocation और audit के लिए
sidsession iddevice logout और token family के लिए
rolebroad rolefine authorization server पर दोबारा check करें

Common mistake है plan: "pro" याdisabled: false token में डाल देना। Plan बदल सकता है, account suspend हो सकता है, लेकिन token expiry तक पुराना claim चलता रहेगा। Authentication पूछता है “यह user कौन है?” Authorization पूछता है “क्या अभी यह action allowed है?” दोनों decisions अलग रखें।

jose के साथ runnable TypeScript example

नीचे demo access token sign और verify करता है, refresh token का सिर्फ hash store करता है, refresh token rotate करता है और reuse detect करता है। Production में Map को Redis या database से बदलें, HTTPS, rate limit, audit logs और CSRF protection जोड़ें।

mkdir jwt-lab
cd jwt-lab
npm init -y
npm install jose
npm install -D tsx typescript @types/node
// auth-demo.ts
import { createHash, createSecretKey, randomUUID } from "node:crypto";
import { SignJWT, jwtVerify } from "jose";

const ISSUER = "https://auth.example.com";
const AUDIENCE = "claudecodelab-api";
const ACCESS_TTL = "15m";
const REFRESH_TTL_SECONDS = 60 * 60 * 24 * 7;

const accessKey = createSecretKey(
  Buffer.from(
    process.env.JWT_ACCESS_SECRET ??
      "dev-only-secret-change-me-32-bytes-minimum"
  )
);

const refreshKey = createSecretKey(
  Buffer.from(
    process.env.JWT_REFRESH_SECRET ??
      "dev-only-refresh-secret-change-me-32-bytes"
  )
);

type Role = "admin" | "user" | "viewer";
type User = { id: string; role: Role; tenantId: string };
type VerifiedAccess = {
  userId: string;
  role: Role;
  tenantId: string;
  sessionId: string;
  tokenId: string;
};

type RefreshRecord = {
  userId: string;
  sessionId: string;
  tokenHash: string;
  expiresAt: number;
  revokedAt?: number;
};

const refreshStore = new Map<string, RefreshRecord>();
const revokedAccessTokenIds = new Set<string>();

function sha256(value: string) {
  return createHash("sha256").update(value).digest("hex");
}

function assertRole(value: unknown): asserts value is Role {
  if (!["admin", "user", "viewer"].includes(String(value))) {
    throw new Error("invalid role claim");
  }
}

async function signAccessToken(user: User, sessionId: string) {
  const tokenId = randomUUID();

  return new SignJWT({ role: user.role, tid: user.tenantId, sid: sessionId })
    .setProtectedHeader({ alg: "HS256", typ: "JWT" })
    .setIssuer(ISSUER)
    .setAudience(AUDIENCE)
    .setSubject(user.id)
    .setIssuedAt()
    .setExpirationTime(ACCESS_TTL)
    .setJti(tokenId)
    .sign(accessKey);
}

async function verifyAccessToken(token: string): Promise<VerifiedAccess> {
  const { payload } = await jwtVerify(token, accessKey, {
    issuer: ISSUER,
    audience: AUDIENCE,
    algorithms: ["HS256"],
  });

  assertRole(payload.role);

  if (
    typeof payload.sub !== "string" ||
    typeof payload.tid !== "string" ||
    typeof payload.sid !== "string" ||
    typeof payload.jti !== "string"
  ) {
    throw new Error("missing required claim");
  }

  if (revokedAccessTokenIds.has(payload.jti)) {
    throw new Error("access token revoked");
  }

  return {
    userId: payload.sub,
    role: payload.role,
    tenantId: payload.tid,
    sessionId: payload.sid,
    tokenId: payload.jti,
  };
}

async function signRefreshToken(user: User, sessionId: string) {
  const tokenId = randomUUID();
  const token = await new SignJWT({ sid: sessionId, kind: "refresh" })
    .setProtectedHeader({ alg: "HS256", typ: "JWT" })
    .setIssuer(ISSUER)
    .setAudience("claudecodelab-refresh")
    .setSubject(user.id)
    .setIssuedAt()
    .setExpirationTime("7d")
    .setJti(tokenId)
    .sign(refreshKey);

  refreshStore.set(tokenId, {
    userId: user.id,
    sessionId,
    tokenHash: sha256(token),
    expiresAt: Date.now() + REFRESH_TTL_SECONDS * 1000,
  });

  return token;
}

async function rotateRefreshToken(refreshToken: string, user: User) {
  const { payload } = await jwtVerify(refreshToken, refreshKey, {
    issuer: ISSUER,
    audience: "claudecodelab-refresh",
    algorithms: ["HS256"],
  });

  if (
    typeof payload.jti !== "string" ||
    typeof payload.sid !== "string" ||
    typeof payload.sub !== "string"
  ) {
    throw new Error("invalid refresh token claims");
  }

  const record = refreshStore.get(payload.jti);
  const presentedHash = sha256(refreshToken);

  if (!record || record.revokedAt || record.tokenHash !== presentedHash) {
    for (const item of refreshStore.values()) {
      if (item.sessionId === payload.sid) item.revokedAt = Date.now();
    }
    throw new Error("refresh token reuse detected");
  }

  if (record.expiresAt < Date.now()) {
    throw new Error("refresh token expired");
  }

  record.revokedAt = Date.now();

  return {
    accessToken: await signAccessToken(user, payload.sid),
    refreshToken: await signRefreshToken(user, payload.sid),
  };
}

async function main() {
  const user: User = {
    id: "user_123",
    role: "admin",
    tenantId: "tenant_a",
  };
  const sessionId = randomUUID();
  const accessToken = await signAccessToken(user, sessionId);
  const refreshToken = await signRefreshToken(user, sessionId);
  const verified = await verifyAccessToken(accessToken);
  const rotated = await rotateRefreshToken(refreshToken, user);

  console.log({ verified, rotatedRefreshLength: rotated.refreshToken.length });
}

main().catch((error) => {
  console.error(error);
  process.exit(1);
});
npx tsx auth-demo.ts

Important detail यह है कि refresh token का raw value store नहीं होता। सिर्फ hash store होता है। Token use होते ही old record revoke होता है और नया pair issue होता है। अगर पुराना token दोबारा आए तो उसी sid की पूरी token family revoke हो जाती है।

Browser apps में refresh token को आम तौर पर HttpOnly, Secure, SameSite Cookie में रखते हैं। HttpOnly JavaScript को Cookie value पढ़ने से रोकता है, जिससे XSS के बाद token string चोरी करना मुश्किल होता है। लेकिन Cookie automatically request के साथ जाती है, इसलिए refresh, logout और state-changing APIs को CSRF protection चाहिए।

const refreshCookieOptions = {
  httpOnly: true,
  secure: true,
  sameSite: "lax" as const,
  path: "/api/auth/refresh",
  maxAge: 60 * 60 * 24 * 7,
};

const clearRefreshCookieOptions = {
  ...refreshCookieOptions,
  maxAge: 0,
};

Access token placement architecture पर depend करता है। Pure SPA memory में access token रख सकती है, लेकिन reload पर token चला जाएगा। BFF या Next.js Route Handler में server API call proxy कर सकता है और browser को access token देने की जरूरत नहीं रहती। localStorage convenient है, पर XSS होने पर bearer token पढ़ा जा सकता है।

Revocation और key rotation

JWT default में exp तक valid रहता है। इसलिए logout, password change, account suspension, lost device और suspected leak के लिए server-side controls जरूरी हैं। Short access tokens, jti revocation list, sid session revocation और refresh token rotation साथ में लगाएं।

Key rotation में overlap window जरूरी है। HS256 आसान है, लेकिन हर verifier को shared secret चाहिए। Services बढ़ें तो RS256 या ES256 with JWKS बेहतर होता है। Verifiers को सिर्फ public keys मिलती हैं। पहले नया public key publish करें, फिर नए kid से sign करें, और old public key तब तक रखें जब तक old access tokens expire न हो जाएं।

import { createRemoteJWKSet, jwtVerify } from "jose";

const JWKS = createRemoteJWKSet(
  new URL("https://auth.example.com/.well-known/jwks.json")
);

export async function verifyWithRotatingKeys(token: string) {
  return jwtVerify(token, JWKS, {
    issuer: "https://auth.example.com",
    audience: "claudecodelab-api",
    algorithms: ["RS256", "ES256"],
  });
}
{
  "rotationPlan": {
    "step1": "नई key बनाएं और JWKS में publish करें",
    "step2": "नए kid से नए tokens sign करें",
    "step3": "पुराने access tokens expire होने तक old public key रखें",
    "step4": "logs check करके old key हटाएं"
  }
}

Use cases और safe Claude Code prompts

flowchart LR
  Login["Login"] --> Access["Short access token"]
  Login --> Refresh["HttpOnly refresh cookie"]
  Access --> API["API validates iss/aud/exp/jti"]
  Refresh --> Rotate["Refresh पर rotation"]
  Rotate --> Store["DB/Redis में hash और sid"]
  Store --> Reuse["Reuse detected: family revoke"]

Use case 1 SaaS admin panel है। tenantId claim context देता है, लेकिन DB query में भी tenant condition चाहिए। Admin action, billing state और account suspension को server पर recheck करें।

Use case 2 paid content या course site है। Access token short रखें और refresh silently करें ताकि reader lesson के बीच logout न हो। अगर site पर ads, Analytics और purchase CTA भी हैं, तो web security headers और cookie consent भी साथ में design करें।

Use case 3 mobile या desktop app है। Browser Cookie के बजाय OS secure storage use होता है। Lost device के लिए sid से session revoke करें और refresh token reuse को security event मानें।

Use case 4 microservices है। Symmetric secret हर service में मत फैलाएं। Public-key verification, gateway या token exchange देखें। हर service aud validate करे।

इस repository में JWT authentication design और implement करो।
Edit करने से पहले table बनाओ:
- framework, user model, session/cookie code, auth middleware
- existing authorization checks, CSRF, CSP, rate limit
- current token storage और XSS/CSRF risk

Rules:
- jose use करो। jsonwebtoken पर वापस मत जाओ।
- access token 15 minutes, refresh token 7 days।
- iss, aud, sub, exp, iat, jti, sid validate करो।
- refresh token का सिर्फ hash DB/Redis में store करो।
- refresh token rotate करो और reuse पर sid family revoke करो।
- secrets, .env या production tokens print मत करो।
- अंत में test या curl verification evidence दो।

Failures, verification और CTA

Common failures हैं algorithm pin न करना, payload में secret या PII डालना, aud/iss skip करना, refresh token reuse allow करना, logout में सिर्फ browser Cookie clear करना, old key तुरंत delete करना और Claude Code में real secret paste करना। बचाव है algorithm allowlist, minimal claims, rotation, sid revocation, JWKS overlap और secret-free prompt।

curl -i -X POST https://example.com/api/auth/login \
  -H "content-type: application/json" \
  -d '{"email":"demo@example.com","password":"correct horse"}'

npm test -- --runInBand auth

Expired token, tampered signature, wrong audience, revoked jti, reused refresh token, logout, password change और account suspension test करें। Refresh Cookie में HttpOnly, Secure, सही SameSite और narrow path भी verify करें।

Individual developers free Claude Code cheatsheet से verification habits शुरू कर सकते हैं। Reusable prompts और templates के लिए ClaudeCodeLab products देखें। Team rollout में JWT, RBAC, cookies, audit logs और CI gates साथ में चाहिए तो Claude Code training and consultation उपयोगी है।

इस article के example को test करते समय सबसे अच्छा result claim table पहले लिखने से मिला। इससे तीन चीजें जल्दी पकड़ी गईं: missing aud, default tsx path में top-level await issue, और refresh token को plain text में store करने का risk। Secure JWT auth सिर्फ token sign करना नहीं है; यह claims, validation, storage, rotation, revocation और key management को reviewable workflow में बांधना है।

#Claude Code #JWT #authentication #security #Node.js
मुफ़्त

मुफ़्त PDF: Claude Code cheatsheet

Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.

हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.

Masa

लेखक के बारे में

Masa

Claude Code workflow और team adoption पर काम करने वाला engineer.