
Research
/Security News
10 npm Typosquatted Packages Deploy Multi-Stage Credential Harvester
Socket researchers found 10 typosquatted npm packages that auto-run on install, show fake CAPTCHAs, fingerprint by IP, and deploy a credential stealer.
Auth made easy and secure via JWT cookies, CSRF tokens, and password hashing helpers.
Auth made easy and secure via JWT cookies, CSRF tokens, and password hashing helpers. ESM and browser friendly.
npm i auth-vir
For the easiest usage, construct and use BackendAuthClient on your server and FrontendAuthClient in your frontend.
Hash a user created password:
import {hashPassword} from 'auth-vir';
/** When a user creates or resets their password, hash it before storing it in your database. */
const hashedPassword = await hashPassword('user input password');
/** Store `hashedPassword` in your database. */
Compare a stored password hash for login checking:
import {doesPasswordMatchHash} from 'auth-vir';
if (
!(await doesPasswordMatchHash({
hash: 'hash from database',
password: 'user input password for login',
}))
) {
throw new Error('Login failure.');
}
Use this on your host / server / backend to authenticate client / frontend requests.
AuthHeaderName.CsrfToken (or just 'csrf-token') header via CORS headers with either of the following options:
customHeaders: [AuthHeaderName.CsrfToken] in implementService from @rest-vir/implement-service.Access-Control-Allow-Headers to (at least) AuthHeaderName.CsrfToken.Access-Control-Allow-Origin header (it cannot be *) and properly implement CORS headers and responses.npx auth-vir: the generated keys will be printed to your console.await generateNewJwtKeys() (imported from this package) in your code.parseJwtKeys: await parseJwtKeys(stringKeys).parseJwtKeys in all auth functionality:
generateSuccessfulLoginHeaders: after a user successfully logs in, run this function and attach the output headers to the Response object.extractUserIdFromRequestHeaders: to verify an authenticated user Request object (make sure to properly attach all auth in the client by following the below Client / frontend side guide).Here's a full example of how to use all host / server / backend side auth functionality:
import {type ClientRequest, type ServerResponse} from 'node:http';
import {
doesPasswordMatchHash,
extractUserIdFromRequestHeaders,
generateNewJwtKeys,
generateSuccessfulLoginHeaders,
hashPassword,
parseJwtKeys,
type CookieParams,
type CreateJwtParams,
} from 'auth-vir';
type MyUserId = string;
/**
* Use this for a /login endpoint.
*
* This verifies a user's login credentials and generate the auth cookie and CSRF token.
*/
export async function handleLogin(
userRequestData: Readonly<{username: string; password: string}>,
response: ServerResponse,
) {
const user = findUserInDatabaseByUsername(userRequestData.username);
if (
!(await doesPasswordMatchHash({
hash: user.hashedPassword,
password: userRequestData.password,
}))
) {
throw new Error('Credentials mismatch.');
}
const authHeaders = await generateSuccessfulLoginHeaders(user.id, cookieParams);
response.setHeaders(new Headers(authHeaders));
}
/**
* Use this for a /sign-up endpoint.
*
* This creates a new user, stores their securely hashed password in the database, and generates the
* auth cookie and CSRF token.
*/
export async function createUser(
userRequestData: Readonly<{username: string; password: string}>,
response: ServerResponse,
) {
const newUser = await createUserInDatabase(userRequestData);
const authHeaders = await generateSuccessfulLoginHeaders(newUser.id, cookieParams);
response.setHeaders(new Headers(authHeaders));
}
/**
* Use this all endpoints that require an authenticated user.
*
* This loads the current user from their auth cookie and CSRF token.
*/
export async function getAuthenticatedUser(request: ClientRequest) {
const userId = (
await extractUserIdFromRequestHeaders<MyUserId>(request.getHeaders(), jwtParams)
)?.userId;
const user = userId ? findUserInDatabaseById(userId) : undefined;
if (!userId || !user) {
throw new Error('Unauthorized.');
}
return user;
}
/**
* # ===========
*
* Helpers
*
* # ===========
*/
async function loadSecretJwtKeys() {
/**
* This should load your saved JWT keys from a non-committed config file or a secrets manager
* (like AWS Secrets Manager).
*/
return await generateNewJwtKeys();
}
const jwtParams: Readonly<CreateJwtParams> = {
audience: 'server context',
jwtDuration: {
hours: 2,
},
issuer: 'server login',
jwtKeys: await parseJwtKeys(await loadSecretJwtKeys()),
};
const cookieParams: CookieParams = {
cookieDuration: {
hours: 2,
},
hostOrigin: 'https://your-backend-origin.example.com',
jwtParams,
};
function findUserInDatabaseByUsername(username: string) {
/** This should connect to your database and find a user matching the given username. */
return {
/** This should be retrieved from your database. */
id: 'some id',
username,
/** This should be retrieved from your database. */
hashedPassword: 'hash retrieved from database',
};
}
function findUserInDatabaseById(userId: MyUserId):
| undefined
| {
id: MyUserId;
username: string;
} {
/** This should connect to your database and find a user matching the given user id. */
return {
id: userId,
/** This should be retrieved from your database. */
username: 'some username',
};
}
async function createUserInDatabase(
userRequestData: Readonly<{username: string; password: string}>,
) {
const hashedPassword = await hashPassword(userRequestData.password);
if (!hashedPassword) {
throw new Error('Password too long.');
}
/**
* Store the new username and hashedPassword in your database and return the new user id.
*
* @example
*
* // using the Prisma ORM:
* return (
* await prismaClient.user.create({
* data: {
* username: userRequestData.username,
* hashedPassword,
* },
* select: {
* id: true,
* },
* })
* ).id;
*/
return {
id: 'some new id',
};
}
Use this on your client / frontend for storing and sending session authorization.
{credentials: 'include'} set on the request.Response from step 1 into handleAuthResponse.{credentials: 'include'} and include {headers: {[AuthHeaderName.CsrfToken]: getCurrentCsrfToken()}}.wipeCurrentCsrfToken()Here's a full example of how to use all the client / frontend side auth functionality:
import {HttpStatus} from '@augment-vir/common';
import {AuthHeaderName} from '../headers.js';
import {getCurrentCsrfToken, handleAuthResponse, wipeCurrentCsrfToken} from 'auth-vir';
/** Call this when the user logs in for the first time this session. */
export async function sendLoginRequest(
userLoginData: {username: string; password: string},
loginUrl: string,
) {
if (getCurrentCsrfToken().csrfToken) {
throw new Error('Already logged in.');
}
const response = await fetch(loginUrl, {
method: 'post',
body: JSON.stringify(userLoginData),
credentials: 'include',
});
handleAuthResponse(response);
return response;
}
/** Call this when the user needs to send any authenticated request after already having logged in. */
export async function sendAuthenticatedRequest(
requestUrl: string,
requestInit: Omit<RequestInit, 'headers'> = {},
headers: Record<string, string> = {},
) {
const {csrfToken} = getCurrentCsrfToken();
if (!csrfToken) {
throw new Error('Not authenticated.');
}
const response = await fetch(requestUrl, {
...requestInit,
credentials: 'include',
headers: {
...headers,
[AuthHeaderName.CsrfToken]: csrfToken.token,
},
});
/**
* This indicates the user is no longer authorized and thus needs to login again. (This likely
* means that their session timed out or they clicked a "log out" button onr your website in
* another tab.)
*/
if (response.status === HttpStatus.Unauthorized) {
wipeCurrentCsrfToken();
throw new Error(`User no longer logged in.`);
} else {
return response;
}
}
/** Call this when the user explicitly clicks a "log out" button. */
export function logout() {
wipeCurrentCsrfToken();
}
All of these configurations must be set for the auth exports in this package to function properly:
AuthHeaderName.CsrfToken (or just 'csrf-token') header via CORS headers with either of the following options:
customHeaders: [AuthHeaderName.CsrfToken] in implementService from @rest-vir/implement-service.Access-Control-Allow-Headers to (at least) AuthHeaderName.CsrfToken.credentials: include in all fetch requests on the client that need to use or set the auth cookie.Access-Control-Allow-Origin (it cannot be *).FAQs
Auth made easy and secure via JWT cookies, CSRF tokens, and password hashing helpers.
We found that auth-vir demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Research
/Security News
Socket researchers found 10 typosquatted npm packages that auto-run on install, show fake CAPTCHAs, fingerprint by IP, and deploy a credential stealer.

Product
Socket Firewall Enterprise is now available with flexible deployment, configurable policies, and expanded language support.

Security News
Open source dashboard CNAPulse tracks CVE Numbering Authorities’ publishing activity, highlighting trends and transparency across the CVE ecosystem.