Skip to content

Commit

Permalink
feat(cookieName): enforce cookieName option
Browse files Browse the repository at this point in the history
BREAKING CHANGE: cookieName is now mandatory, to avoid issues of shared
cookieNames in examples etc..

fixes #54
  • Loading branch information
vvo committed May 1, 2020
1 parent 3f4292b commit c016fec
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 29 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ _Table of contents:_
- [Examples](#examples)
- [Handle password rotation/update the password](#handle-password-rotationupdate-the-password)
- [API](#api)
- [withIronSession(handler, { password, ttl, cookieName, cookieOptions })](#withironsessionhandler--password-ttl-cookiename-cookieoptions-)
- [withIronSession(handler, { password, cookieName, [ttl], [cookieOptions] })](#withironsessionhandler--password-cookiename-ttl-cookieoptions-)
- [req.session.set(name, value)](#reqsessionsetname-value)
- [req.session.get(name)](#reqsessiongetname)
- [req.session.setFlash(name, value)](#reqsessionsetflashname-value)
Expand Down Expand Up @@ -155,11 +155,11 @@ Notes:

## API

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

- `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
- `cookieName`, _optional_: Default to `__ironSession`
- `cookieOptions`, _optional_: Any option available from [jshttp/cookie#serialize](https://github.com/jshttp/cookie#cookieserializename-value-options). Default to:

```js
Expand Down
8 changes: 6 additions & 2 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ function throwOnNoPassword() {
throw new Error("next-iron-session: Missing parameter `password`");
}

function throwOnNoCookieName() {
throw new Error("next-iron-session: Missing parameter `cookieName`");
}

function computeCookieMaxAge(ttl) {
// 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.
Expand All @@ -26,10 +30,10 @@ export default function withIronSession(
withIronSessionWrapperHandler,
{
ttl = 15 * 24 * 3600,
cookieName = "__ironSession",
cookieName = throwOnNoCookieName(),
password = throwOnNoPassword(),
cookieOptions: userCookieOptions = {},
} = {},
},
) {
const cookieOptions = {
...defaultCookieOptions,
Expand Down
81 changes: 57 additions & 24 deletions lib/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,32 @@ import ironStore from "iron-store";
import withIronSession from "./index.js";

const password = "Gbm49ATjnqnkCCCdhV4uDBhbfnPqsCW0";
const cookieName = "test";

test("withSession(handler) without a password", () => {
test("without a password", () => {
return new Promise((done) => {
const handler = () => {};
expect(() => {
withIronSession(handler);
withIronSession(handler, { cookieName });
}).toThrowErrorMatchingInlineSnapshot(
`"next-iron-session: Missing parameter \`password\`"`,
);
done();
});
});

test("without a cookieName", () => {
return new Promise((done) => {
const handler = () => {};
expect(() => {
withIronSession(handler, { password });
}).toThrowErrorMatchingInlineSnapshot(
`"next-iron-session: Missing parameter \`cookieName\`"`,
);
done();
});
});

test("withSession((req, res) => {}, {password})", () => {
return new Promise((done) => {
const handler = (req, res) => {
Expand All @@ -42,7 +55,7 @@ test("withSession((req, res) => {}, {password})", () => {
`);
done();
};
const wrappedHandler = withIronSession(handler, { password });
const wrappedHandler = withIronSession(handler, { password, cookieName });
wrappedHandler(
{
headers: { cookie: "sg=1" },
Expand Down Expand Up @@ -77,7 +90,7 @@ test("withSession(({req, res}) => {}, {password})", () => {
`);
done();
};
const wrappedHandler = withIronSession(handler, { password });
const wrappedHandler = withIronSession(handler, { password, cookieName });
wrappedHandler({
req: { headers: { cookie: "ssr=1" } },
res: { json: function () {} },
Expand All @@ -95,7 +108,7 @@ test("req.session.set", () => {
`);
done();
};
const wrappedHandler = withIronSession(handler, { password });
const wrappedHandler = withIronSession(handler, { password, cookieName });
wrappedHandler(
{
headers: { cookie: "" },
Expand All @@ -115,7 +128,7 @@ test("req.session.setFlash", () => {
expect(req.session.get("state")).toMatchInlineSnapshot(`undefined`);
done();
};
const wrappedHandler = withIronSession(handler, { password });
const wrappedHandler = withIronSession(handler, { password, cookieName });
wrappedHandler(
{
headers: { cookie: "" },
Expand All @@ -138,7 +151,7 @@ test("req.session.unset", () => {
expect(req.session.get("state")).toMatchInlineSnapshot(`undefined`);
done();
};
const wrappedHandler = withIronSession(handler, { password });
const wrappedHandler = withIronSession(handler, { password, cookieName });
wrappedHandler(
{
headers: { cookie: "" },
Expand Down Expand Up @@ -169,7 +182,7 @@ test("req.session.save creates a seal and stores it in a cookie", () => {
);
done();
};
const wrappedHandler = withIronSession(handler, { password });
const wrappedHandler = withIronSession(handler, { password, cookieName });
wrappedHandler(
{
headers: { cookie: "" },
Expand All @@ -193,12 +206,12 @@ test("withSession((req, res) => {}, {password}) with existing session (SG)", asy
`);
done();
};
const wrappedHandler = withIronSession(handler, { password });
const wrappedHandler = withIronSession(handler, { password, cookieName });
wrappedHandler(
{
headers: {
cookie:
"__ironSession=Fe26.2**4e769b9b7b921621ed5658cfc0d7d8e267dc8ee93663c2803c257b31111394e3*jRXOJHmt_BDG9nNTXcVRXQ*UHpK9GYp7SXTiEsxTzTUq_tQD_-ZUp7PguEXy-bRFuBE4fW74-9wm9UtlWO2rlwB**d504d6d197d183efec0ae6d3c2378c43048c8752d6c3c591c92289ed01142b3c*3NG2fCo8A53CXPU8rEAMnDB7X9UkwzTaHieumPBqyTw",
"test=Fe26.2**4e769b9b7b921621ed5658cfc0d7d8e267dc8ee93663c2803c257b31111394e3*jRXOJHmt_BDG9nNTXcVRXQ*UHpK9GYp7SXTiEsxTzTUq_tQD_-ZUp7PguEXy-bRFuBE4fW74-9wm9UtlWO2rlwB**d504d6d197d183efec0ae6d3c2378c43048c8752d6c3c591c92289ed01142b3c*3NG2fCo8A53CXPU8rEAMnDB7X9UkwzTaHieumPBqyTw",
},
},
{},
Expand All @@ -218,12 +231,12 @@ test("withSession(({req, res}) => {}, {password}) with existing session (SSR)",
`);
done();
};
const wrappedHandler = withIronSession(handler, { password });
const wrappedHandler = withIronSession(handler, { password, cookieName });
wrappedHandler({
req: {
headers: {
cookie:
"__ironSession=Fe26.2**4e769b9b7b921621ed5658cfc0d7d8e267dc8ee93663c2803c257b31111394e3*jRXOJHmt_BDG9nNTXcVRXQ*UHpK9GYp7SXTiEsxTzTUq_tQD_-ZUp7PguEXy-bRFuBE4fW74-9wm9UtlWO2rlwB**d504d6d197d183efec0ae6d3c2378c43048c8752d6c3c591c92289ed01142b3c*3NG2fCo8A53CXPU8rEAMnDB7X9UkwzTaHieumPBqyTw",
"test=Fe26.2**4e769b9b7b921621ed5658cfc0d7d8e267dc8ee93663c2803c257b31111394e3*jRXOJHmt_BDG9nNTXcVRXQ*UHpK9GYp7SXTiEsxTzTUq_tQD_-ZUp7PguEXy-bRFuBE4fW74-9wm9UtlWO2rlwB**d504d6d197d183efec0ae6d3c2378c43048c8752d6c3c591c92289ed01142b3c*3NG2fCo8A53CXPU8rEAMnDB7X9UkwzTaHieumPBqyTw",
},
},
});
Expand All @@ -240,7 +253,11 @@ test("When ttl is 0, maxAge have a specific value", () => {
expect(maxAgeParam).toMatchInlineSnapshot(`" Max-Age=2147483587"`);
done();
};
const wrappedHandler = withIronSession(handler, { password, ttl: 0 });
const wrappedHandler = withIronSession(handler, {
password,
cookieName,
ttl: 0,
});
wrappedHandler(
{
headers: { cookie: "" },
Expand All @@ -262,13 +279,17 @@ test("req.session.destroy", () => {
Array [
"set-cookie",
Array [
"__ironSession=; Max-Age=0; Path=/; HttpOnly; Secure; SameSite=Lax",
"test=; Max-Age=0; Path=/; HttpOnly; Secure; SameSite=Lax",
],
]
`);
done();
};
const wrappedHandler = withIronSession(handler, { password, ttl: 0 });
const wrappedHandler = withIronSession(handler, {
password,
cookieName,
ttl: 0,
});
wrappedHandler(
{
headers: { cookie: "coucou=true" },
Expand Down Expand Up @@ -298,11 +319,15 @@ test("When trying to use an expired seal", async () => {
`);
done();
};
const wrappedHandler = withIronSession(handler, { password, ttl });
const wrappedHandler = withIronSession(handler, {
password,
cookieName,
ttl,
});

wrappedHandler(
{
headers: { cookie: `__ironSession=${seal}` },
headers: { cookie: `test=${seal}` },
},
{
setHeader: jest.fn(),
Expand All @@ -319,10 +344,14 @@ test("When trying to use an expired seal", async () => {
expect(req.session.get("user")).toMatchInlineSnapshot(`undefined`);
done();
};
const wrappedHandler = withIronSession(handler, { password, ttl });
const wrappedHandler = withIronSession(handler, {
password,
cookieName,
ttl,
});
wrappedHandler(
{
headers: { cookie: `__ironSession=${seal}` },
headers: { cookie: `test=${seal}` },
},
{
setHeader: jest.fn(),
Expand All @@ -344,14 +373,15 @@ test("It throws Iron errors when passing a wrong password (password length must
};
const wrappedHandler = withIronSession(handler, {
password: "dsadsadsadsadsadadsa",
cookieName,
ttl: 0,
});
await expect(
wrappedHandler(
{
headers: {
cookie:
"__ironSession=Fe26.2**4e769b9b7b921621ed5658cfc0d7d8e267dc8ee93663c2803c257b31111394e3*jRXOJHmt_BDG9nNTXcVRXQ*UHpK9GYp7SXTiEsxTzTUq_tQD_-ZUp7PguEXy-bRFuBE4fW74-9wm9UtlWO2rlwB**d504d6d197d183efec0ae6d3c2378c43048c8752d6c3c591c92289ed01142b3c*3NG2fCo8A53CXPU8rEAMnDB7X9UkwzTaHieumPBqyTw",
"test=Fe26.2**4e769b9b7b921621ed5658cfc0d7d8e267dc8ee93663c2803c257b31111394e3*jRXOJHmt_BDG9nNTXcVRXQ*UHpK9GYp7SXTiEsxTzTUq_tQD_-ZUp7PguEXy-bRFuBE4fW74-9wm9UtlWO2rlwB**d504d6d197d183efec0ae6d3c2378c43048c8752d6c3c591c92289ed01142b3c*3NG2fCo8A53CXPU8rEAMnDB7X9UkwzTaHieumPBqyTw",
},
},
{
Expand All @@ -373,7 +403,7 @@ test("when no cookies at all", () => {
`);
done();
};
const wrappedHandler = withIronSession(handler, { password });
const wrappedHandler = withIronSession(handler, { password, cookieName });
wrappedHandler(
{
headers: {},
Expand All @@ -398,10 +428,11 @@ test("When trying to use a wrong seal (example: password was updated server-side
};
const wrappedHandler = withIronSession(handler, {
password: secondPassword,
cookieName,
});
wrappedHandler(
{
headers: { cookie: `__ironSession=${seal}` },
headers: { cookie: `test=${seal}` },
},
{},
);
Expand All @@ -416,12 +447,13 @@ test("moving from <=3.1.2 seals to multi passwords creates a new session", async
};
const wrappedHandler = withIronSession(handler, {
password: [{ id: 1, password }],
cookieName,
});
wrappedHandler(
{
headers: {
cookie:
"__ironSession=Fe26.2**4e769b9b7b921621ed5658cfc0d7d8e267dc8ee93663c2803c257b31111394e3*jRXOJHmt_BDG9nNTXcVRXQ*UHpK9GYp7SXTiEsxTzTUq_tQD_-ZUp7PguEXy-bRFuBE4fW74-9wm9UtlWO2rlwB**d504d6d197d183efec0ae6d3c2378c43048c8752d6c3c591c92289ed01142b3c*3NG2fCo8A53CXPU8rEAMnDB7X9UkwzTaHieumPBqyTw",
"test=Fe26.2**4e769b9b7b921621ed5658cfc0d7d8e267dc8ee93663c2803c257b31111394e3*jRXOJHmt_BDG9nNTXcVRXQ*UHpK9GYp7SXTiEsxTzTUq_tQD_-ZUp7PguEXy-bRFuBE4fW74-9wm9UtlWO2rlwB**d504d6d197d183efec0ae6d3c2378c43048c8752d6c3c591c92289ed01142b3c*3NG2fCo8A53CXPU8rEAMnDB7X9UkwzTaHieumPBqyTw",
},
},
{},
Expand Down Expand Up @@ -453,10 +485,11 @@ test("Password rotation", async () => {
};
const wrappedHandler = withIronSession(handler, {
password: secondPassword,
cookieName,
});
wrappedHandler(
{
headers: { cookie: `__ironSession=${seal}` },
headers: { cookie: `test=${seal}` },
},
{},
);
Expand Down

0 comments on commit c016fec

Please sign in to comment.