{"id":16759,"library":"auth-vir","title":"Secure JWT Cookie & CSRF Token Auth","description":"Auth-vir is a JavaScript/TypeScript library designed to simplify and secure authentication mechanisms in web applications. It provides robust features for handling JWT (JSON Web Token) based session cookies, CSRF (Cross-Site Request Forgery) protection through tokens, and secure password hashing. The library is actively maintained, with the current stable version being 5.2.0. Releases typically follow a semantic versioning approach, with minor and patch updates occurring every few weeks to months, and major versions introducing breaking changes less frequently, as observed from recent release history. Its key differentiators include a focus on security best practices out-of-the-box, offering both backend and frontend client implementations, and explicit support for ESM (ECMAScript Modules) and browser environments, ensuring modern application compatibility. It aims to abstract away common auth complexities while maintaining high security standards.","status":"active","version":"5.2.0","language":"javascript","source_language":"en","source_url":"https://github.com/electrovir/auth-vir","tags":["javascript","auth","jwt","csrf","hash","password","secure","cookie","typescript"],"install":[{"cmd":"npm install auth-vir","lang":"bash","label":"npm"},{"cmd":"yarn add auth-vir","lang":"bash","label":"yarn"},{"cmd":"pnpm add auth-vir","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"Auth-vir is ESM-only; CommonJS `require` syntax will not work. Use named import for `hashPassword`.","wrong":"const { hashPassword } = require('auth-vir');","symbol":"hashPassword","correct":"import { hashPassword } from 'auth-vir';"},{"note":"`BackendAuthClient` is a named export, not a default export. Ensure you destructure it correctly.","wrong":"import BackendAuthClient from 'auth-vir';","symbol":"BackendAuthClient","correct":"import { BackendAuthClient } from 'auth-vir';"},{"note":"`AuthHeaderName` is an enum-like object containing constants for auth headers, such as `AuthHeaderName.CsrfToken`.","symbol":"AuthHeaderName","correct":"import { AuthHeaderName } from 'auth-vir';"},{"note":"Use `import type` for importing only types to ensure they are stripped from the JavaScript output, preventing potential runtime issues and optimizing bundles.","wrong":"import { CreateJwtParams } from 'auth-vir';","symbol":"type CreateJwtParams","correct":"import type { CreateJwtParams } from 'auth-vir';"}],"quickstart":{"code":"import {type ClientRequest, type ServerResponse} from 'node:http';\nimport {\n    AuthCookie,\n    doesPasswordMatchHash,\n    extractUserIdFromRequestHeaders,\n    generateNewJwtKeys,\n    generateSuccessfulLoginHeaders,\n    hashPassword,\n    parseJwtKeys,\n    type CookieParams,\n    type CreateJwtParams,\n    type CsrfHeaderNameOption,\n} from 'auth-vir';\n\ntype MyUserId = string;\n\n// Generate JWT keys (do this once and store securely, e.g., in environment variables)\nasync function getJwtKeys() {\n    const generatedKeys = await generateNewJwtKeys();\n    // In a real app, store these securely (e.g., environment variables)\n    // For demo purposes, we'll return them directly.\n    console.log('Generated JWT Keys (store securely!):', generatedKeys);\n    return parseJwtKeys(generatedKeys);\n}\n\nconst jwtKeysPromise = getJwtKeys();\n\nasync function handleLogin(req: ClientRequest, res: ServerResponse, passwordInput: string) {\n    const jwtKeys = await jwtKeysPromise;\n    const userId: MyUserId = 'user-123'; // Replace with actual user ID after DB lookup\n    const storedPasswordHash = await hashPassword('password123'); // From your user database\n\n    if (await doesPasswordMatchHash({ hash: storedPasswordHash, password: passwordInput })) {\n        const authHeaders = await generateSuccessfulLoginHeaders({\n            jwtKeys,\n            userId,\n            maxAgeMs: 1000 * 60 * 60 * 24 * 7, // 7 days\n            csrfHeaderName: 'csrf-token',\n        });\n\n        for (const [headerName, headerValue] of Object.entries(authHeaders)) {\n            res.setHeader(headerName, headerValue);\n        }\n        res.statusCode = 200;\n        res.end('Logged in successfully!');\n    } else {\n        res.statusCode = 401;\n        res.end('Invalid credentials.');\n    }\n}\n\nasync function handleAuthenticatedRequest(req: ClientRequest, res: ServerResponse) {\n    const jwtKeys = await jwtKeysPromise;\n    try {\n        const userId = await extractUserIdFromRequestHeaders({\n            jwtKeys,\n            requestHeaders: req.headers as Record<string, string | string[] | undefined>,\n            csrfHeaderName: 'csrf-token',\n        });\n\n        res.statusCode = 200;\n        res.end(`Access granted for user ID: ${userId}`);\n    } catch (error) {\n        console.error('Authentication error:', error);\n        res.statusCode = 401;\n        res.end('Unauthorized or invalid session.');\n    }\n}\n\n// Example usage (simplified HTTP server)\nimport { createServer } from 'node:http';\n\nconst server = createServer(async (req, res) => {\n    if (req.url === '/login' && req.method === 'POST') {\n        // In a real app, parse body for password\n        await handleLogin(req, res, 'password123');\n    } else if (req.url === '/protected' && req.method === 'GET') {\n        await handleAuthenticatedRequest(req, res);\n    } else {\n        res.statusCode = 404;\n        res.end('Not Found');\n    }\n});\n\nserver.listen(3000, () => {\n    console.log('Server running on http://localhost:3000');\n});\n","lang":"typescript","description":"This quickstart demonstrates setting up a basic Node.js server using auth-vir for user login, generating secure JWT and CSRF tokens, and authenticating subsequent requests. It shows password hashing, token generation, and user ID extraction from request headers."},"warnings":[{"fix":"Ensure your backend extracts the raw CSRF token directly from the 'csrf-token' cookie header (or custom header name) and that your frontend client correctly sends this token. Refer to the updated `extractUserIdFromRequestHeaders` usage.","message":"Auth-vir v5.0.0 introduced a major change where the CSRF token is now stored exclusively in a cookie, rather than as a JSON object within a cookie. This requires consumers to update their frontend and backend logic to correctly handle the CSRF token from the cookie header directly.","severity":"breaking","affected_versions":">=5.0.0"},{"fix":"If upgrading from an earlier v3.x version, be aware that v4.0.0 changed the CSRF token format to raw. If upgrading from v4.x to v5.x, refer to the v5.0.0 warning for cookie-only storage.","message":"Auth-vir v4.0.0 reverted CSRF token storage from JSON back to raw token storage. This means the token itself is the raw value, not a JSON string encapsulating it. This was a reversal of an earlier change.","severity":"breaking","affected_versions":">=4.0.0 <5.0.0"},{"fix":"Store JWT keys in environment variables, a secure vault, or a dedicated secrets management service. Load them at runtime and ensure they are never logged or exposed in client-side code.","message":"JWT signing and encryption keys generated by `generateNewJwtKeys()` must be stored securely and never committed to version control or exposed publicly. Compromised keys lead to severe security vulnerabilities, allowing attackers to forge tokens and impersonate users.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Configure your server's CORS policy to include `AuthHeaderName.CsrfToken` in `Access-Control-Allow-Headers` and specify the exact origin(s) for `Access-Control-Allow-Origin`.","message":"When integrating `auth-vir` with a client-side application, the `AuthHeaderName.CsrfToken` (default 'csrf-token') must be explicitly exposed via CORS headers (`Access-Control-Allow-Headers`) on your server. Additionally, `Access-Control-Allow-Origin` cannot be `*` and must be properly configured for your client's origin.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Ensure your project is configured for ESM (e.g., `\"type\": \"module\"` in `package.json`) or use dynamic `import()` if you must use it within a CommonJS context. Always use `import ... from 'auth-vir'` syntax.","message":"The library explicitly states it is ESM-only. Attempting to `require()` any part of `auth-vir` in a CommonJS module will result in a runtime error.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Refactor your code to use ESM equivalents (e.g., `import.meta.url` for path resolution) or ensure your environment is configured for ESM.","cause":"Attempting to use CommonJS-specific globals (`__dirname`, `__filename`, `require`) in an ESM-only package context.","error":"TypeError: __dirname is not defined in ES module scope"},{"fix":"Verify that the output from `generateNewJwtKeys()` is stored verbatim and loaded without corruption. Ensure no whitespace or malformation occurs during storage/retrieval from environment variables or secret managers.","cause":"The JWT signing and encryption keys were not correctly generated, stored, or parsed, leading to an invalid format being passed to `parseJwtKeys` or related functions.","error":"Error: Invalid JWT keys: Expected 3 parts for signing key, got X"},{"fix":"Ensure your frontend is correctly extracting the CSRF token from the initial login response (e.g., from the `Set-Cookie` header if `AuthCookie.CsrfToken` is used) and sending it back in subsequent requests via the `csrf-token` header. Check CORS configuration.","cause":"The CSRF token sent by the client in the request headers (or expected location) does not match the token stored in the server's session or cookie, or is missing.","error":"Error: Login failure: CSRF token mismatch"},{"fix":"Consult the `auth-vir` documentation for the correct named exports. Check for potential default exports or specific versions where the symbol might have existed under a different name or export style.","cause":"Attempting to import a symbol that is not a named export, or has been renamed/removed in a different version.","error":"SyntaxError: Named export 'X' not found. The requested module 'auth-vir' does not provide an export named 'X'"}],"ecosystem":"npm","meta_description":null}