All Projects β†’ vvo β†’ Next Iron Session

vvo / Next Iron Session

Licence: mit
πŸ›  Next.js stateless session utility using signed and encrypted cookies to store data

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Next Iron Session

Auth Boss
πŸ”’ Become an Auth Boss. Learn about different authentication methodologies on the web.
Stars: ✭ 2,879 (+465.62%)
Mutual labels:  cookies, authentication, sessions
Guardian auth
The Guardian Authentication Implementation Using Ecto/Postgresql Elixir Phoenix [ User Authentication ]
Stars: ✭ 15 (-97.05%)
Mutual labels:  authentication, sessions
Mern Login Signup Component
Minimalistic Sessions based Authentication app πŸ”’ using Reactjs, Nodejs, Express, MongoDB and Bootstrap. Uses Cookies πŸͺ
Stars: ✭ 74 (-85.46%)
Mutual labels:  cookies, sessions
Nest User Auth
A starter build for a back end which implements managing users with MongoDB, Mongoose, NestJS, Passport-JWT, and GraphQL.
Stars: ✭ 145 (-71.51%)
Mutual labels:  authentication, backend
Authenticationintro
Stars: ✭ 82 (-83.89%)
Mutual labels:  cookies, authentication
Buji Pac4j
pac4j security library for Shiro: OAuth, CAS, SAML, OpenID Connect, LDAP, JWT...
Stars: ✭ 444 (-12.77%)
Mutual labels:  authentication
Cloudfront Auth
An AWS CloudFront [emailΒ protected] function to authenticate requests using Google Apps, Microsoft, Auth0, OKTA, and GitHub login
Stars: ✭ 471 (-7.47%)
Mutual labels:  authentication
Hoodie
🐢 The Offline First JavaScript Backend
Stars: ✭ 4,240 (+733.01%)
Mutual labels:  backend
Product Is
Welcome to the WSO2 Identity Server source code! For info on working with the WSO2 Identity Server repository and contributing code, click the link below.
Stars: ✭ 435 (-14.54%)
Mutual labels:  authentication
Nextjs Firebase Authentication
Next.js + Firebase Starter
Stars: ✭ 502 (-1.38%)
Mutual labels:  authentication
Steam
☁️ Python package for interacting with Steam
Stars: ✭ 489 (-3.93%)
Mutual labels:  authentication
Pycookiecheat
Borrow cookies from your browser's authenticated session for use in Python scripts.
Stars: ✭ 465 (-8.64%)
Mutual labels:  cookies
Wsify
Just a tiny, simple and real-time self-hosted pub/sub messaging service
Stars: ✭ 452 (-11.2%)
Mutual labels:  backend
Wp Discourse
WordPress plugin that lets you use Discourse as the community engine for a WordPress blog
Stars: ✭ 474 (-6.88%)
Mutual labels:  authentication
Lucene Solr
Apache Lucene and Solr open-source search software
Stars: ✭ 4,217 (+728.49%)
Mutual labels:  backend
Dj Rest Auth
Authentication for Django Rest Framework
Stars: ✭ 491 (-3.54%)
Mutual labels:  authentication
React Native Login
πŸ“± An example React Native project for client login authentication
Stars: ✭ 438 (-13.95%)
Mutual labels:  cookies
Micro Auth
A microservice that makes adding authentication with Google and Github to your application easy.
Stars: ✭ 466 (-8.45%)
Mutual labels:  authentication
Cerberus
A demonstration of a completely stateless and RESTful token-based authorization system using JSON Web Tokens (JWT) and Spring Security.
Stars: ✭ 482 (-5.3%)
Mutual labels:  authentication
Polish Ads Filter
CertyficateIT - Oficjalne polskie filtry do Adblock, uBlock Origin, Adguard
Stars: ✭ 462 (-9.23%)
Mutual labels:  cookies

next-iron-session GitHub license Tests codecov npm Downloads

πŸ›  Next.js and Express (connect middleware) stateless session utility using signed and encrypted cookies to store data


This Next.js, Express and Connect backend utility allows you to create a session to then be stored in browser cookies via a signed and encrypted seal. This provides client sessions that are βš’οΈ iron-strong.

The seal stored on the client contains the session data, not your server, making it a "stateless" session from the server point of view. This is a different take than next-session where the cookie contains a session ID to then be used to map data on the server-side.

Online demo at https://next-iron-session.now.sh/ πŸ‘€


The seal is signed and encrypted using @hapi/iron, iron-store is used behind the scenes. This method of storing session data is the same technique used by frameworks like Ruby On Rails.

♻️ Password rotation is supported. It allows you to change the password used to sign and encrypt sessions while still being able to decrypt sessions that were created with a previous password.

Next.js's πŸ—Ώ Static generation (SG) and βš™οΈ Server-side Rendering (SSR) are both supported.

There's a Connect middleware available so you can use this library in any Connect compatible framework like Express.

By default the cookie has an ⏰ expiration time of 15 days, set via maxAge. After that, even if someone tries to reuse the cookie, next-iron-session will not accept the underlying seal because the expiration is part of the seal value. See https://hapi.dev/family/iron for more information on @hapi/iron mechanisms.

Table of contents:

Installation

npm add next-iron-session

yarn add next-iron-session

Usage

You can find real-world examples (Next.js, Express) in the examples folder.

The password is a private key you must pass at runtime, it has to be at least 32 characters long. Use https://1password.com/password-generator/ to generate strong passwords.

⚠️ Always store passwords in secret environment variables on your platform.

pages/api/login.js:

import { withIronSession } from "next-iron-session";

async function handler(req, res) {
  // get user from database then:
  req.session.set("user", {
    id: 230,
    admin: true,
  });
  await req.session.save();
  res.send("Logged in");
}

export default withIronSession(handler, {
  password: "complex_password_at_least_32_characters_long",
  // if your localhost is served on http:// then disable the secure flag
  cookieOptions: {
    secure: process.env.NODE_ENV === "production",
  },
});

pages/api/user.js:

import { withIronSession } from "next-iron-session";

function handler(req, res, session) {
  const user = req.session.get("user");
  res.send({ user });
}

export default withIronSession(handler, {
  password: "complex_password_at_least_32_characters_long",
  // if your localhost is served on http:// then disable the secure flag
  cookieOptions: {
    secure: process.env.NODE_ENV === "production",
  },
});

pages/api/logout.js:

import { withIronSession } from "next-iron-session";

function handler(req, res, session) {
  req.session.destroy();
  res.send("Logged out");
}

export default withIronSession(handler, {
  password: "complex_password_at_least_32_characters_long",
  // if your localhost is served on http:// then disable the secure flag
  cookieOptions: {
    secure: process.env.NODE_ENV === "production",
  },
});

⚠️ Sessions are automatically recreated (empty session though) when:

  • they expire
  • a wrong password was used
  • we can't find back the password id in the current list

Examples

Handle password rotation/update the password

When you want to:

  • rotate passwords for better security every two (or more, or less) weeks
  • change the password you previously used because it leaked somewhere (😱)

Then you can use multiple passwords:

Week 1:

export default withIronSession(handler, {
  password: [
    {
      id: 1,
      password: "complex_password_at_least_32_characters_long",
    },
  ],
});

Week 2:

export default withIronSession(handler, {
  password: [
    {
      id: 2,
      password: "another_password_at_least_32_characters_long",
    },
    {
      id: 1,
      password: "complex_password_at_least_32_characters_long",
    },
  ],
});

Notes:

  • id is required so that we do not have to try every password in the list when decrypting (the id is part of the cookie value).
  • The password used to encrypt session data (to seal) is always the first one in the array, so when rotating to put a new password, it must be first in the array list
  • Even if you do not provide an array at first, you can always move to array based passwords afterwards, knowing that your first password (string) was given {id:1} automatically.

Express / Connect middleware: ironSession

You can import and use ironSession if you want to use next-iron-session in Express and Connect.

import { ironSession } from "next-iron-session";

const session = ironSession({
  cookieName: "next-iron-session/examples/express",
  password: process.env.SECRET_COOKIE_PASSWORD,
  // if your localhost is served on http:// then disable the secure flag
  cookieOptions: {
    secure: process.env.NODE_ENV === "production",
  },
});

router.get("/profile", session, async function (req, res) {
  // now you can access all of the req.session.* utilities
  if (req.session.get("user") === undefined) {
    res.redirect("/restricted");
    return;
  }

  res.render("profile", {
    title: "Profile",
    userId: req.session.get("user").id,
  });
});

A more complete example using Express can be found in the examples folder.

Usage with next-connect

Since ironSession is an Express / Connect middleware, it means you can use it with next-connect:

import { ironSession } from "next-iron-session";

const session = ironSession({
  cookieName: "next-iron-session/examples/express",
  password: process.env.SECRET_COOKIE_PASSWORD,
  // if your localhost is served on http:// then disable the secure flag
  cookieOptions: {
    secure: process.env.NODE_ENV === "production",
  },
});
import nextConnect from "next-connect";

const handler = nextConnect();

handler.use(session).get((req, res) => {
  const user = req.session.get("user");
  res.send(`Hello user ${user.id}`);
});

export default handler;

API

withIronSession(handler, { password, cookieName, [ttl], [cookieOptions] })

This can be used to wrap Next.js getServerSideProps or API Routes so you can then access all req.session.* methods.

  • password, required: Private key used to encrypt the cookie. It has to be at least 32 characters long. Use https://1password.com/password-generator/ to generate strong passwords. password can be either a string or an array of objects like this: [{id: 2, password: "..."}, {id: 1, password: "..."}] to allow for password rotation.
  • cookieName, required: Name of the cookie to be stored
  • ttl, optional: In seconds, default to 14 days
  • cookieOptions, optional: Any option available from jshttp/cookie#serialize. Default to:
{
  httpOnly: true,
  secure: true,
  sameSite: "lax",
  // The next line makes sure browser will expire cookies before seals are considered expired by the server. It also allows for clock difference of 60 seconds maximum between server and clients.
  maxAge: (ttl === 0 ? 2147483647 : ttl) - 60,
  path: "/",
  // other options:
  // domain, if you want the cookie to be valid for the whole domain and subdomains, use domain: example.com
  // encode, there should be no need to use this option, encoding is done by next-iron-session already
  // expires, there should be no need to use this option, maxAge takes precedence
}

ironSession({ password, cookieName, [ttl], [cookieOptions] })

Connect middleware.

import { ironSession } from "next-iron-session";

app.use(ironSession({ ...options }));

async applySession(req, res, { password, cookieName, [ttl], [cookieOptions] })

Allows you to use this module the way you want as long as you have access to req and res.

import { applySession } from "next-iron-session";

await applySession(req, res, options);

req.session.set(name, value)

req.session.get(name)

req.session.unset(name)

req.session.save() => promise

req.session.destroy() => promise

Note: If you use req.session.destroy() in an API route, you need to make sure this route will not be cached. To do so, either call this route via a POST request fetch("/api/logout", { method: "POST" }) or add cache-control: no-store, max-age=0 to its response.

See https://github.com/vvo/next-iron-session/issues/274 for more details.

FAQ

Why use pure πŸͺ cookies for sessions?

This makes your sessions stateless: you do not have to store session data on your server. You do not need another server or service to store session data. This is particularly useful in serverless architectures where you're trying to reduce your backend dependencies.

What are the drawbacks?

There are some drawbacks to this approach:

  • you cannot invalidate a seal when needed because there's no state stored on the server-side about them. We consider that the way the cookie is stored reduces the possibility for this eventuality to happen. Also, in most applications the first thing you do when receiving an authenticated request is to validate the user and their rights in your database, which defeats the case where someone would try to use a token while their account was deactivated/deleted. Now if someone steals a user token you should have a process in place to mitigate that: deactivate the user and force a re-login with a flag in your database for example.
  • application not supporting cookies won't work, but you can use iron-store to implement something similar. In the future, we could allow next-iron-session to accept basic auth or bearer token methods too. Open an issue if you're interested.
  • on most browsers, you're limited to 4,096 bytes per cookie. To give you an idea, a next-iron-session cookie containing {user: {id: 230, admin: true}} is 358 bytes signed and encrypted: still plenty of available cookie space in here.
  • performance: crypto on the server-side could be slow, if that's the case let me know. Also, cookies are sent to every request to your website, even images, so this could be an issue

Now that you know the drawbacks, you can decide if they are an issue for your application or not. More information can also be found on the Ruby On Rails website which uses the same technique.

How is this different from JWT?

Not so much:

  • JWT is a standard, it stores metadata in the JWT token themselves to ensure communication between different systems is flawless.
  • JWT tokens are not encrypted, the payload is visible by customers if they manage to inspect the seal. You would have to use JWE to achieve the same.
  • @hapi/iron mechanism is not a standard, it's a way to sign and encrypt data into seals

Depending on your own needs and preferences, next-iron-session may or may not fit you.

Project status

This is a recent library I authored because I needed it. While @hapi/iron is battle-tested and used in production on a lot of websites, this library is not (yet!). Please use it at your own risk.

If you find bugs or have API ideas, create an issue.

Credits

Thanks to Hoang Vo for advice and guidance while building this module. Hoang built next-connect and next-session.

Thanks to hapi team for creating iron.

πŸ€“ References

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].