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 🙏

© 2024 – Pkg Stats / Ryan Hefner

resident

v0.5.0

Published

**Resident** is an authentication framework for Node.js applications.

Downloads

95

Readme

Resident is an authentication framework for Node.js applications.

ResidentJS is a spiritual successor to PassportJS, addressing some limitations that Password can't or won't address:

  • 🔠 Resident is typed
  • ⏳ Resident is async
  • 🗂️ Resident is backend agnostic
  • 💡 Resident is opinionated about best practices
  • 🔒 Resident bundles a core set of auth strategies

More details in the Why below.

Table of contents

Getting started

Install it via npm, yarn, pnpm, or bun:

npm add resident
yarn add resident
pnpm add resident
bun add resident

First you'll need to decide what data is going to get serialized into your session payload. This is data which will always be instantly available on both the client and server, without doing any database reads:

type SessionPayload = {
  email: string
  organizationId: string
}

The main Resident instance is typically created once per request:

const resident = new Resident<SessionPayload>({
  secrets: ["secret generated with openssl rand -hex 32"],
  onSession(token) {
    res.cookie("session", token)
  },
})

You won't do much with the Resident instance, because most authentication activities will go through specific "strategies" like a PasswordStrategy. However you will use the Resident instance to authenticate based on the session token:

// Authenticating from a session token stored in the cookies:
const sessionFromToken = resident.authenticateFromToken(req.cookies["session"])

Let's set up our first authentication strategy, the PasswordStrategy:

const passwordStrategy = new PasswordStrategy({
  resident,
  authenticate({ username: email, password }) {
    const user = await db.User.findByEmail(email)
    const hashAttempt = PasswordStrategy.hashPassword(password, user.salt)

    if (hashAttempt !== user.hashedPassword) {
      return {
        email,
        organizationId: user.organizationId,
      }
    } else {
      return null
    }
  },
})

That is assuming you saved your user with a hashed password and its salt. Resident will help you do that:

function createUser({ email, password, organizationId }) {
  const salt = await PasswordStrategy.generateSalt()
  const user = await db.User.create({
    email,
    hashedPassword: await PasswordStrategy.hashPassword(password, salt)
    organizationId,
  })
}

After you did that, you could authenticate the user with the email and password, or you could authenticate them directly:

await resident.authenticate({
  email: "[email protected]",
  organizationId,
})

It's up to you how you organize this kind of configuration code, but an idiomatic way to set it up in Express might be to create a middleware:

app.use((req, res, next) => {
  const resident = new Resident(
    ...
  )
  const passwordStrategy = new PasswordStrategy({
    resident,
    ...
  })
  req.resident = resident
  req.passwordStrategy = passwordStrategy
})

With the passwordStrategy instantiated you can authenticate a user based on their username and password:

app.post("/login", (req, res) => {
  const sessionPayload = req.passwordStrategy.authenticateFromPassword({
    username: req.body.username,
    password: req.body.password,
  })

  if (!sessionPayload) {
    throw new Error("Invalid username or password")
  }

  return sessionPayload
})

Why

Resident is a spiritual successor to Passport, addressing some limitations that Password can't or won't address:

Resident is typed

It's written in TypeScript, and strictly typed. The Passport maintainer has no interest in migrating Passport to TypeScript.

Resident is async

The Resident APIs use async/await rather than callbacks.

Resident is backend agnostic

The Passport API is deeply intertwined with the concept of middleware. Every interaction your application has with Passport must pass via some kind of REST middleware. So for example, if you want some of your application code to authenticate a specific user (say for masquerading) or request new scopes from a specific identity provider, you must run those requests through a REST middleware. Because REST is untyped, this means you have no guarantees at the code level that your application is set up correctly.

Resident, in contrast, is backend agnostic. The APIs are just normal everyday JavaScript, which can be called from anywhere in your Node application, whether you're using REST, or GraphQL, or tRPC or whatever.

On the one hand, this means you will need to maintain a bit more boilerplate to wire up all of the webhooks different identity providers need. On the other hand, this makes your code far more debuggable. There's no ✨magic✨ here, just good old fashioned, strictly typed JavaScript APIs.

Resident is opinionated about best practices

Passport will let you store and manage passwords, cookies, etc however you like. You can use JWTs or not. You can use secure cookies or not. You can salt your passwords... or not. Although Resident is not opinionated about how your application is set up, it does takes a "batteries included" approach to authentication.

Included in the box are:

  • Password salting and hashing
  • Secure cookie management
  • Session serialization

If there's a security-critical feature needed to build a secure, authenticated Node application, we'll build it and test it and package it.

Resident bundles a core set of auth strategies

Passport doesn't ship with any authentication strategies, it lets you install whichever strategies you like, published by whoever you choose. This has allowed an ecosystem of strategies to evolve around it, so that it can support every identity scheme under the sun.

However, it also means that every strategy uses slightly different configuration options, is documented slightly differently, and maintained differently. And there are no guarantees that the maintainers of these different packages are trustworthy.

Resident bundles a core set of auth strategies, so that you can get quickly up and running with the most common identity providers.

Development

Install and run the tests

git clone [email protected]:erikpukinskis/resident-js.git
cd resident-js
yarn install
yarn test

Build for distribution

yarn build

Use yalc to test locally

From the resident-js/...

yalc add

Then in your application folder:

yalc add resident

Todo

Top authentication strategies:

Features

  • [ ] Password reset link

Later