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
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.
Next.js's
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
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:
npm add next-iron-session
yarn add next-iron-session
You can find full featured 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.
// 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",
cookieName: "myapp_cookiename",
// 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",
cookieName: "myapp_cookiename",
// 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",
cookieName: "myapp_cookiename",
// if your localhost is served on http:// then disable the secure flag
cookieOptions: {
secure: process.env.NODE_ENV === "production",
},
});
Also see the full TypeScript example.
// pages/api/login.ts
import { NextApiRequest, NextApiResponse } from "next";
import { withIronSession, Session } from "next-iron-session";
type NextIronRequest = NextApiRequest & { session: Session };
async function handler(
req: NextIronRequest,
res: NextApiResponse,
): Promise<void> {
// 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",
cookieName: "myapp_cookiename",
// if your localhost is served on http:// then disable the secure flag
cookieOptions: {
secure: process.env.NODE_ENV === "production",
},
});
When you want to:
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).seal
) is always the first one in the array, so when rotating to put a new password, it must be first in the array liststring
) was given {id:1}
automatically.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.
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;
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 storedttl
, optional: In seconds, default to 14 dayscookieOptions
, 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
}
Connect middleware.
import { ironSession } from "next-iron-session";
app.use(ironSession({ ...options }));
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);
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.
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.
There are some drawbacks to this approach:
next-iron-session
to accept basic auth or bearer token methods too. Open an issue if you're interested.next-iron-session
cookie containing {user: {id: 230, admin: true}}
is 358 bytes signed and encrypted: still plenty of available cookie space in here.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.
Not so much:
Depending on your own needs and preferences, next-iron-session
may or may not fit you.
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.
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.
Thanks goes to these wonderful people (emoji key):
John Vandivier |
This project follows the all-contributors specification. Contributions of any kind welcome!
Iron ,一个用 Rust 编写的可扩展的、并发的 Web 框架。 Iron 是一个快速、灵活的面向中间件的服务器框架,它提供了小巧但健壮的基础以创建复杂的应用和 RESTful APIs。Iron 没有和中间件捆绑在一起,任何东西都是可以拖拽的(drag-and-drop),这允许你天马行空地进行模块设置。 extern crate iron;extern crate time;use iro
描述 (Description) 这不是一个函数,它会导致当前循环迭代跳转到下一个值或控制语句的下一个评估。 不执行当前循环中的进一步语句。 如果指定了LABEL,则执行将跳转到LABEL标识的循环的下一次迭代。 语法 (Syntax) 以下是此函数的简单语法 - next LABEL next 返回值 (Return Value) 此函数不返回任何值。 例子 (Example) 以下是显示其基
++运算符为类String调用此方法。 它会递增给定String中的最后一个字符。 语法 (Syntax) String next() 参数 (Parameters) 没有 返回值 (Return Value) 字符串的新值 例子 (Example) 以下是此方法的使用示例 - class Example { static void main(String[] args) {
Next 是一个面向键盘的、可扩展的 Web 浏览器,专为高级用户设计。该应用程序具有键绑定(Emacs,VI),在 Lisp 中是完全可配置和可扩展的,并且对生产专业人员具有强大的功能。 特性: 选项卡的快速切换 通过模糊搜索,可以轻松地在打开的选项卡之间切换。如果你在找https://www.example.com,则只需输入ele, exa,epl,或任何其他匹配的字母序列。 快速导航 Ne
Perl next语句启动循环的下一次迭代。 您可以为LABEL提供next语句,其中LABEL是循环的标签。 next语句可以在嵌套循环中使用,如果未指定LABEL,它将适用于最近的循环。 如果循环上有一个continue块,它总是在即将评估条件之前执行。 您将在单独的章节中看到continue语句。 语法 (Syntax) Perl中next语句的语法是 - next [ LABEL ];
描述 (Description) next( [selector] )方法获取一组元素,这些元素包含每个给定元素集的唯一下一个兄弟。 语法 (Syntax) 以下是使用此方法的简单语法 - <i>selector</i>.next( [selector] ) 参数 (Parameters) 以下是此方法使用的所有参数的说明 - selector - 可以使用CSS 1-3选择器语法编写可选选择器