npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vantis/auth

v0.1.1

Published

Vantis Auth SDK — server-only client for the per-project Auth identity engine

Readme

@vantis/auth

Sign your users up, sign them in, and know who is making each request. No auth server to run, no password storage to get right.

import { auth } from '@vantis/auth';

const { token, user } = await auth.signIn({ email, password });
const current = await auth.user(token); // AuthUser, or null when signed out

That is the whole model. You get a token, you store it, you pass it back.

Quickstart

This picks up from a project you have already created and linked, holding a service to connect the block to. If you do not have those yet, create both first:

vantis projects create --link
vantis service add --type web --name web --repo you/your-app --port 3000

Every command below runs against the linked project. Run them somewhere that is not linked and they fail with not found, which reads like the block is missing when the real problem is that Vantis does not know which project you mean. Pass --project <name> to choose one explicitly.

1. Add an Auth block and connect your service

vantis block add auth --name identity
vantis service update web --uses identity

If the service does not exist yet, create it and connect it in one command instead:

vantis service add --type web --name web --repo you/your-app --port 3000 --uses identity

Your service reaches the block from its next deploy. There is no token to manage: an Auth block gives your service a URL and nothing else.

--uses sets every block a service is connected to, so list them all in one command. If your service already uses a data block, pass both:

vantis service update web --uses main --uses identity

--uses only gets a service the block's address; it is not a network boundary. Every web, worker, and cron service in the same project can reach the Auth block whether or not it declares --uses. Treat the project as the trust boundary: services you do not trust equally belong in their own project.

2. Install

npm install @vantis/auth

Zero dependencies. Node 20.17 or newer.

3. Sign someone up, then read them back

import { auth } from '@vantis/auth';

// In your sign-up handler:
const { token, user } = await auth.signUp({ email: '[email protected]', password });
const cookie = '__Host-session=' + token + '; HttpOnly; Secure; SameSite=Lax; Path=/';

// On a later request, with the token you stored:
const current = await auth.user(token);
if (!current) {
  // signed out: send them to sign in
}

You store the token

There is no ambient session. signIn and signUp hand you a token, and every later call takes that token as an argument. Where you keep it between requests is yours to choose: an http-only cookie, a server-side session store, whatever your app already does.

This is deliberate. One server handles many people at once, so a shared session object would leak one user's identity into another user's request.

const { token } = await auth.signIn({ email, password });   // you get it
const user = await auth.user(token);                        // you pass it back
await auth.signOut(token);                                  // and again

Whatever you choose, if the browser ends up holding a cookie that carries auth state (the token itself, or just a handle to a server-side session that holds it), name that cookie with the __Host- prefix, as in the quickstart above. A browser refuses to set a __Host- cookie that carries a Domain attribute, so the cookie can only ever be read back by the exact host that set it. A cookie without that prefix can be affected by other apps hosted on the platform, so __Host- is the pattern to use for that cookie either way, not an optional hardening step.

This is one part of a larger rule: any route that changes state and trusts that cookie needs its own cross-site request forgery protection, not only sign-in and sign-up. See Protect your sign-in and sign-up routes below.

null means signed out, and nothing else

auth.user(token) returns null for exactly one reason: the token is missing, invalid, or expired. Every other failure throws. So if (!user) always means "signed out", never "something broke", and you can build on that.

When you would rather not branch, auth.requireUser(token) returns the user or throws AuthUnauthenticatedError.

Handle a wrong password

A wrong password is a typed error you can catch. Anything else is a real failure and should not be reported to your user as bad credentials.

import { auth, AuthInvalidCredentialsError } from '@vantis/auth';

try {
  const { token, user } = await auth.signIn({ email, password });
} catch (err) {
  if (err instanceof AuthInvalidCredentialsError) {
    return { message: 'Wrong email or password.' };
  }
  throw err;
}

Every error extends AuthError and carries a stable code you can switch on. The full list is in the reference.

Handle a weak password

Signup refuses a password for two reasons, and both are typed errors rather than service failures, carrying readable text you can show the person signing up:

  • shorter than 8 characters;
  • too similar to the email address being registered. A password built from the address, such as adaexample for [email protected], is refused. This one catches people out when they pick a throwaway password while testing.

Passwords are not checked against known-breach lists, so a password can be accepted here and still be one an attacker already has. If that matters for your app, check it in your own signup form before calling signUp.

import { auth, AuthValidationError } from '@vantis/auth';

try {
  const { token, user } = await auth.signUp({ email, password });
} catch (err) {
  if (err instanceof AuthValidationError) {
    // AuthWeakPasswordError extends AuthValidationError, so this one
    // branch catches a weak password and any other rejected input.
    return { message: err.messages[0] ?? 'That signup could not be completed.' };
  }
  throw err;
}

err.messages is text from the block, never the password itself. Your app decides whether to show it.

What a signed-in email proves, and what it does not

Signing in proves the person knows the password for that account. It does not prove they control that inbox. There is no email verification step today, so never use the email address on its own to grant access to anything. Checking user.email.endsWith('@acme.com') does not prove someone works at Acme.

user.emailVerified is typed as literally false, so if (user.emailVerified) is dead code your editor tells you about before you ship. Verified identity is planned.

Identify a user by id, never by email

Two email addresses can look identical to a person and still be two different accounts, because an address can carry characters nobody can see. Key your own data off user.id, which is stable and unique, and treat user.email as a label you display. This only ever applies within one project. Accounts never cross projects.

You exercise auth on deploy, not on your laptop

AUTH_API_URL is an address on your project's private network and it is injected at deploy time, so it does not resolve from your machine. @vantis/auth is server-only as well. Together those mean signup and signin first run when you deploy. There is no local mode today, and this is the thing most likely to read as broken wiring when it is working as designed.

The quickest loop is to keep the rest of your app running locally and reach the real block by deploying, with vantis logs web streaming what happened. Treat a failed auth call in local development as expected rather than as something to debug.

Keep it on the server

The token is a live credential for one of your users. @vantis/auth is server-only, and importing it in browser code throws, on purpose.

A bundler that resolves the import to the browser build fails immediately at module evaluation with VantisServerOnlyError, before your code runs. The block URL is read when you make a call, not when you import, so server build steps that run before your environment is set work fine.

Protect your sign-in and sign-up routes

Your sign-in and sign-up handlers are state-changing POST requests, so they need the same cross-site request forgery protection you would put on any other form handler in your app: make sure the route is CSRF-protected, using whatever your framework provides for that.

Without it, a page on another site can submit a sign-in request on a visitor's behalf. If your handler stores the token it gets back, that visitor ends up signed into whatever account the attacker chose, and everything they do next happens inside that account.

The same protection belongs on every other route that trusts the cookie once it is set, not only sign-in and sign-up: any route that changes state on a signed-in user's behalf needs its own CSRF or Origin check.

Full docs

The guide, covering the block, the flow end to end, and the failure path, is at vantis.build/docs/blocks/auth. Every method, type, and error code is at vantis.build/docs/reference/auth.

Need full control?

@vantis/auth gives you an opinionated path: email and password, one session token, no configuration. If you need a different identity model, run your own service on Vantis instead and own it end to end.

License

Apache-2.0