Giter Site home page Giter Site logo

vvo / iron-session Goto Github PK

View Code? Open in Web Editor NEW
3.3K 14.0 248.0 7.62 MB

๐Ÿ›  Secure, stateless, and cookie-based session library for JavaScript

Home Page: https://get-iron-session.vercel.app

License: MIT License

TypeScript 100.00%
nextjs authentication session stateless cookies expressjs nodejs serverless

iron-session's Introduction

iron-session GitHub Workflow Status (with event) GitHub license npm npm npm package minimized gzipped size (select exports)

iron-session is a secure, stateless, and cookie-based session library for JavaScript.

our sponsor:

API-first authentication, authorization, and fraud prevention

Website โ€ข Documentation


The session data is stored in signed and encrypted cookies which are decoded by your server code in a stateless fashion (= no network involved). This is the same technique used by frameworks like Ruby On Rails.

Online demo and examples: https://get-iron-session.vercel.app ๐Ÿ‘€
Featured in the Next.js documentation โญ๏ธ

Table of Contents

Installation

pnpm add iron-session

Usage

To get a session, there's a single method to know: getIronSession.

// Next.js API Routes and Node.js/Express/Connect.
import { getIronSession } from 'iron-session';

export function get(req, res) {
  const session = getIronSession(req, res, { password: "...", cookieName: "..." });
}

export function post(req, res) {
  const session = getIronSession(req, res, { password: "...", cookieName: "..." });
  session.username = "Alison";
  await session.save();
}
// Next.js Route Handlers (App Router)
import { cookies } from 'next/headers';
import { getIronSession } from 'iron-session';

export function GET() {
  const session = getIronSession(cookies(), { password: "...", cookieName: "..." });
}

export function POST() {
  const session = getIronSession(cookies(), { password: "...", cookieName: "..." });
  session.username = "Alison";
  await session.save();
}
// Next.js Server Components and Server Actions (App Router)
import { cookies } from 'next/headers';
import { getIronSession } from 'iron-session';

async function getIronSessionData() {
  const session = await getIronSession(cookies(), { password: "...", cookieName: "..." });
}

function Profile() {
  const session = await getIronSessionData();

  return <div>{session.username}</div>;
}

Examples

We have many different patterns and examples on the online demo, have a look: https://get-iron-session.vercel.app/.

Project status

โœ… Production ready and maintained.

Session options

Two options are required: password and cookieName. Everything else is automatically computed and usually doesn't need to be changed.****

  • 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 the equivalent of 14 days. You can set this to 0 and iron-session will compute the maximum allowed value by cookies.

  • cookieOptions, optional: Any option available from jshttp/cookie#serialize except for encode which is not a Set-Cookie Attribute. See Mozilla Set-Cookie Attributes and Chrome Cookie Fields. Default to:

    {
      httpOnly: true,
      secure: true, // set this to false in local (non-HTTPS) development
      sameSite: "lax",// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#lax
      maxAge: (ttl === 0 ? 2147483647 : ttl) - 60, // Expire cookie before the session expires.
      path: "/",
    }

API

getIronSession<T>(req, res, sessionOptions): Promise<IronSession<T>>

const session = getIronSession<SessionData>(req, res, sessionOptions);

getIronSession<T>(cookieStore, sessionOptions): Promise<IronSession<T>>

const session = getIronSession<SessionData>(cookies(), sessionOptions);

session.save(): Promise<void>

Saves the session. This is an asynchronous operation. It must be done and awaited before headers are sent to the client.

await session.save()

session.destroy(): void

Destroys the session. This is a synchronous operation as it only removes the cookie. It must be done before headers are sent to the client.

session.destroy()

session.updateConfig(sessionOptions: SessionOptions): void

Updates the configuration of the session with new session options. You still need to call save() if you want them to be applied.

sealData(data: unknown, { password, ttl }): Promise<string>

This is the underlying method and seal mechanism that powers iron-session. You can use it to seal any data you want and pass it around. One usecase are magic links: you generate a seal that contains a user id to login and send it to a route on your website (like /magic-login). Once received, you can safely decode the seal with unsealData and log the user in.

unsealData<T>(seal: string, { password, ttl }): Promise<T>

This is the opposite of sealData and allow you to decode a seal to get the original data back.

FAQ

Why use pure cookies for sessions?

This makes your sessions stateless: since the data is passed around in cookies, you do not need any server or service to store session data.

More information can also be found on the Ruby On Rails website which uses the same technique.

How to invalidate sessions?

Sessions cannot be instantly invalidated (or "disconnect this customer") as there is typically no state stored about sessions on the server by default. However, in most applications, the first step upon receiving an authenticated request is to validate the user and their permissions in the database. So, to easily disconnect customers (or invalidate sessions), you can add an `isBlocked`` state in the database and create a UI to block customers.

Then, every time a request is received that involves reading or altering sensitive data, make sure to check this flag.

Can I use something else than cookies?

Yes, we expose sealData and unsealData which are not tied to cookies. This way you can seal and unseal any object in your application and move seals around to login users.

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, iron-session may or may not fit you.

Credits

Good Reads

iron-session's People

Contributors

allcontributors[bot] avatar borales avatar brc-dd avatar dashue avatar dependabot[bot] avatar dharnarh avatar isaackwok avatar izhaki avatar jameshfisher avatar joswayski avatar keon avatar kerumen avatar kimizuy avatar lachlanjc avatar marvinkome avatar mcansh avatar mirshko avatar muntasirsyed avatar oxicode avatar phocks avatar pluby avatar rarkins avatar renchris avatar renovate-bot avatar renovate[bot] avatar searchableguy avatar snax4a avatar vaguue avatar virginus01 avatar vvo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

iron-session's Issues

Firebase requires Node v10

@hapi/[email protected]: The engine "node" is incompatible with this module. Expected version ">=12.0.0". Got "10.22.0"

Due to firebase requiring Node version 10 when installing next-iron-session i get this incompatible module.

any recommendations?

Testing with api routes?

I'm testing some Next.js API routes using the following.

describe('/api/logout handler', () => {
  let server: http.Server;
  let url: RequestInfo;

  beforeAll(async (done) => {
    server = http.createServer((req, res) => apiResolver(req, res, undefined, handler, undefined));
    url = await listen(server);
    done();
  });

  afterAll((done) => {
    server.close(done);
  });

My API route handler is wrapped in a withIronSession. And I would like to do something like:

withIronSession(async (req) => {
      expect(req.session.get('user')).toMatchObject(expectedValue);
}, sessionOptions);
  1. I can't seem to get the expectation inside the handler to fire
  2. I don't think the session values are persisting in a testing environment

My ultimate goal is to have my tests confirm that my login and logout are properly storing/destroying credentials. Maybe the best thing to do is mock next-iron-session (though I had problems with that too: TypeError: (0 , _nextIronSession.withIronSession) is not a function)

What would you recommend?

Unset vs. destroy in terms of security

Hi! Thanks for authoring this useful library. Quick question -- In usage destroy() is called when signing out, but is it okay to use unset('user') instead? Are there any security concerns I should be aware of?

FYI, I want to store other data via set('someKeyOtherThanUser') but it seems that calling destroy() would clear out both 'user' and 'someKeyOtherThanUser' I want to clear only the 'user' data when signing out.

Sharing cookie across subdomains

Is there any way to access the domain property on the underlying cookie that gets created? I have a use case where a user should stay authenticated across subdomains. Example is that a subdomain = a storefront, but if a user is auth'd in a given storefront, that should hold across all subdomains.

Thanks in advance!

Make cookieName mandatory?

When developing multiple websites on the same host (localhost:3000), then the cookieName and value can be shared between applications. While this is not a security issue (localhost), it's still annoying because it will lead to errors like "Error: Bad hmac value" because we're trying to decode appx cookie using the password of appy.

By making cookieName mandatory, we could avoid that and recommend to always use __appx __appy cookie names

Location of 'await' in examples/next.js/pages/login.jsx

Should lines 24/25 of examples/next.js/pages/login.jsx read :-
...
24 mutateUser(
25 await fetchJson("/api/login", {
...

and not :-
...
24 await mutateUser(
25 fetchJson("/api/login", {
...

I was getting intermittent login problems on a slower connection. Making the above change resolved this.

Thanks...

Thoughts on global SSR components?

This is more of a general question, not an issue.

I'm currently working on implementing next-iron-session in my project. (thanks for the work btw it came at a perfect time for me) I see there is an SSR example for a single user-profile page, which I think works great, but I'm wondering what anyone's thoughts are on more global or site-wide SSR solutions.

We have an application layout being rendered in our custom _app.js page that includes some need for data stored in the session. Simple things like the application header showing "Welcome, XYZ" vs "Sign in"

Right now in order to accomplish this, I'm using something similar to the useUser hook. But on hard-refreshes the initial page is loaded as if there is no user logged in, then once the data is returned from the API it updates the page to reflect a logged-in user's view.

One approach (though I have no idea if it's a good one) that I've used in the past to take advantage of next-redux-wrapper and basically initialize the redux app state with data from req. Then read all the session data from redux. I had various issues with this approach around keeping the redux store and the actual session itself in-sync for changes.

I'm wondering if that is a "valid" approach, or if there is a better solution that you've found to avoid the redux stuff?

Need example for additional authenticated API calls

Hi, in the example project I want to make additional requests only if a user is logged in.

Would you recommend adding another hook, for example useMovies() and pass in the user object?

const { user } = useUser({ redirectTo: '/login' })
const { movies } = useMovies()

As far as I understand, useMovies would still be called in the beginning, even if there is no user.
How would you write the hook then, to only fetch when user logged in?

Or is there a better way?

Allow multiple passwords for key rotation

Hi there, thanks for this module. ๐Ÿ‘‹

A small feature that I think will make it even better: please allow for multiple password keys to be set via an array (similar to how cookie-session does it). This would allow for passwords to be rotated without impacting existing sessions.

Missing parameter password

I used the nextjs example of next-iron-session, and set the SECRET_COOKIE_PASSWORD in the .env but whenever I try to do a login I get the Missing parameter password error.

I checked in the /lib/session.js and it is using process.env.SECRET_COOKIE_PASSWORD as well.
I don't have an idea of where else again I have to pass a password parameter.

this is the /lib/session.js file.

// this file is a wrapper with defaults to be used in both API routes and getServerSideProps functions


export default function withSession(handler) {
  return withIronSession(handler, {
    password: process.env.SECRET_COOKIE_PASSWORD,
    cookieName: "next-iron-session/session/next.js",
    cookieOptions: {
      // the next line allows to use the session in non-https environements like
      // Next.js dev mode (http://localhost:3000)
      secure: process.env.NODE_ENV === "production",
    },
  });
}

Remove flash session data API

Unless someone is using it, even me I did not use it. It's too magical and I prefer to explicitly remove it when necessary.

How to use with jwt login?

Generated jwt token using username password from my backend(java, python, php) api. Then i need store jwt in session cookie instead of custom password. Is it possible for this library? How to do that?

Next.js - Getting Error [ERR_HTTP_HEADERS_SENT]

This is based off of the example for next.js


export const getServerSideProps = withSession(
  async ({ req, res, query }) => {
    const user = req.session.get('user');

    if (!user) {
      res.setHeader('location', '/login');
      res.statusCode = 302;
      res.end();
      return { props: {} };
    }

//... rest of code ...//

})

Full Error:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:518:11)
    at DevServer.sendHTML (/Users/zay/Code/node_modules/next/dist/server/next-dev-server.js:35:5)
    at DevServer.render (/Users/zay/Code/node_modules/next/dist/next-server/server/next-server.js:56:37)
    at async Object.fn (/Users/zay/Code/node_modules/next/dist/next-server/server/next-server.js:31:107)
    at async Router.execute (/Users/zay/Code/node_modules/next/dist/next-server/server/router.js:23:67)
    at async DevServer.run (/Users/zay/Code/node_modules/next/dist/next-server/server/next-server.js:49:494)
    at async DevServer.handleRequest (/Users/zay/Code/node_modules/next/dist/next-server/server/next-server.js:17:101) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

Is there something I may be doing wrong or an issue withSession?

example should use local library

Right now the example of the repository uses the direct dependency from npm.

It would be better if it used the locally built library. To ease development but also provide PR deployments that are actually working since based on the updated code instead of the one in npm.

But then it will break the zeit/now deployment (which defines a basedir of example/), since then the local library is no more available.

I am sure there's a way to setup that well but I tried many different ways without success.

Typescript: withIronSession does not extend `req` type to include `session`

I may be missing something, but as far as I can tell, withIronSession does not extend the req type to include req.session.

export const getServerSideProps: GetServerSideProps = withIronSession(
  async ({ req }: GetServerSidePropsContext) => {
    console.log('session', req.session.get('token'))
...

Here, Typescript will complain:

Property 'session' does not exist on type 'IncomingMessage & { cookies?: { [key: string]: any; }; }'.ts(2339)

I believe an approach similar to next-connect to allow passing the req type as a generic to extend the original type might work: https://github.com/hoangvvo/next-connect/blob/efd72ef77878fd0cc0c546c395d3cdbf20ede9fd/lib/index.d.ts#L34

The slightly tricky part is that withIronSession is designed to work with both API handlers and getServerSideProps so the arguments will be different. I'm not very familiar with adding types to JS, but I'll attempt it. Any hints would be appreciated!

Is it secure to detect whether user is signed in based on whether session could be retrieved?

I'm using Next.js with API routes to handle authentication. I have a /api/signin endpoint that is called from the frontend with email and password. When the user password & email are correct, a session is set & saved on the request with req.session.set and req.session.save. I can't store all of the user data in the cookie because it would be too large, so instead I send back a JSON response with all the user data and just set the user ID in the cookie.

When a user wants to update their information, the frontend calls /api/update with the updated information. The API route will save the changes to the database, but will need to verify that the user is authenticated first.

Is it secure to just check whether req.session.get("user") returns an object that contains an ID?

I thought about this, but I'm not sure. The session is encrypted and decrypted using a secret password so in theory nobody could set a "fake" session on the request with any user ID. Only users that have signed in using our application ( submitted the right email and password to /api/signin ) should have a session that is decryptable by req.session.get. Am I right? I would tremendously appreciate to hear from you, I can't find any resources on this.

getServerSideProps duplicate code in each page

@vvo , Bonjour! Thanks for the next-iron-session example .

Any ideas on removing below duplicate code in each getServerSideProps for SSR pages ?

const user = req.session.get('user')
if (user === undefined) {
res.setHeader('location', '/login')
res.statusCode = 302
res.end()
return { props: {} }
}

something like this-
//profile-ssr.js
export const getServerSideProps = withSession(async function (ctx) {
const { req } = ctx;
const user = RedirectIfNotAuthenticated(ctx);
if (!user) {
return { props: {} };
}
return {
props: { user: req.session.get("user") },
};
});

//session.js
export function RedirectIfNotAuthenticated({ req, res }) {
const user = req.session.get("user");
if (user === undefined) {
res.setHeader("location", "/login");
res.statusCode = 302;
res.end();
return false;
}
return true;
}

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Location: package.json
Error type: The renovate configuration file contains some invalid settings
Message: Configuration option 'packageRules[2].node' should be a json object, Invalid configuration option: author, Invalid configuration option: babel, Invalid configuration option: engineStrict, Invalid configuration option: eslintConfig, Invalid configuration option: files, Invalid configuration option: jest, Invalid configuration option: license, Invalid configuration option: main, Invalid configuration option: name, Invalid configuration option: packageRules[0].@types/cookie, Invalid configuration option: packageRules[0].array.prototype.flat, Invalid configuration option: packageRules[0].clone, Invalid configuration option: packageRules[0].cookie, Invalid configuration option: packageRules[0].debug, Invalid configuration option: packageRules[0].iron-store, Invalid configuration option: packageRules[1].@babel/cli, Invalid configuration option: packageRules[1].@babel/core, Invalid configuration option: packageRules[1].@babel/preset-env, Invalid configuration option: packageRules[1].@types/jest, Invalid configuration option: packageRules[1].babel-eslint, Invalid configuration option: packageRules[1].babel-jest, Invalid configuration option: packageRules[1].eslint, Invalid configuration option: packageRules[1].eslint-plugin-import, Invalid configuration option: packageRules[1].eslint-plugin-jest, Invalid configuration option: packageRules[1].eslint-plugin-react, Invalid configuration option: packageRules[1].eslint-plugin-react-hooks, Invalid configuration option: packageRules[1].jest, Invalid configuration option: packageRules[1].jest-date-mock, Invalid configuration option: packageRules[1].prettier, Invalid configuration option: packageRules[1].prettier-plugin-packagejson, Invalid configuration option: packageRules[1].semantic-release, Invalid configuration option: prettier, Invalid configuration option: private, Invalid configuration option: renovate, Invalid configuration option: scripts, Invalid configuration option: types, Invalid configuration option: version, The "node" object can only be configured at the top level of a config but was found inside "packageRules[2]"

Examples of multiple session cookies

Awesome project, nicely done!

For those that work with auth tokens, the 4k size limit on a cookie is a challenge. After storing a token and refresh token, there isn't sufficient room left over for storing much else. Given that the session options includes a required parameter of cookieName, I assume that there is a way to channel session storage to different cookies.

I'm using an Express site, registering the middleware in the www.js. When I try to employ a second middleware registration with a different cookieName, Express doesn't seem to like this. In my routes file, creating another reference to next-iron-session to register a different cookie name doesn't seem to create a second cookie in the response.

Do you have any examples of how to use multiple cookies to overcome the 4k size limit? Is this even something that is supported?

Many thanks!

Unable to fetch session if I call API using fetch in Next.js

I have created a Next.js project and, I'm using following code to call api,

export const getServerSideProps = withIronSession(
	async ({req, res}) => {
		const { client } = await connectToDatabase()
		const isConnected = await client.isConnected()
		const r = await fetch("http://localhost:3000/api/s", {
				method: "GET",
				headers: { "Content-Type": "application/json" },
			});
		return {
			props: { isConnected: isConnected, user:r.email?r.email:"" },
		}
	},
	{
		cookieName: process.env.MYSITECOOKIE,
		cookieOptions: {
			secure: process.env.NODE_ENV === "production" ? true : false
		},
		password: process.env.SESSION_PASSWORD
	}
);

If I directly call this api (http://localhost:3000/api/s) then it shows user, but when I call using fetch it doesn't return the result.

Cookie does not get removed after logout

I played with the demo a little bit and I have noticed a strange behavior. After login, the cookie next-iron-session/example is set correctly. After I clicked logout, it did redirect me to login when I tried to access profile page. However I could still see the cookie from network panel. I supposed the cookie should be removed right?

Trying an undocumented scenario

Hey @vvo and thanks for all your work on this.

I'm struggling with a use-case which may or may not be supported.

I've got a profile page

export const getServerSideProps: GetServerSideProps = withSession(
    async ({ req }) => {

        console.log(req.session.get('user')) // This is properly set here
        let studio;
        try {
            studio = await axios.get<any>(GetUrl('/user/studioprofile'));
        }
        catch {
            // silence
        }

        return {
            props: {
                studio
            }
        }
    }
);

This page is trying to preload from a protected userprofile endpoint

const handler = async (req: SessionRequest, res: NextApiResponse) => {
    console.log(req.session) // req.session exists
    console.log(req.session.get('user')) // user is undefined

    res.status(401);
    res.end();
}

But in the api the user does not exist. The session is there wth get,set etc functions.

When I check the api in the browser it works.
I'm guessing this is because browser sends cookie, but my getserversideprops does not?
I tried the old withCredentials:true option to axios blindly, but didn't work.

I think this is a use-case not covered in your examples, and perhaps isn't supported?

Thanks for your help!

login fails on production build

I noticed some intermittent problems in the dev build (sometimes a successful login would not redirect as it should) so I tried the production build and it always fails.

Steps to reproduce:

  1. npx create-next-app --example with-iron-session with-iron-session-app
  2. cd with-iron-session-app
  3. npm run build && npm run start
  4. open a browser to localhost:3000/login
  5. enter a valid github username and submit
  6. observe that the login page does not change state.

Authentication for wss

Hello, thanks for this awesome lib!

I'm using next-iron-session but with a custom server to handle wss requests

https://nextjs.org/docs/advanced-features/custom-server

After the user authenticate I can move between protected routes perfectly, but I need to check if the user is logged in when he tries to connect to the websocket session.

My serve.js is the following

const { createServer } = require('http');
const { parse } = require('url');
const next = require('next');
const WebSocket = require('ws');
const http = require('http');

const wss = new WebSocket.Server({ clientTracking: false, noServer: true });

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

const server = http.createServer(app);

server.on('upgrade', async function (request, socket, head) {
  console.log('Parsing session from request...');

  //>>>>>>>>>>>>>>>>>>>something needs to happen here to get the session and check if everything is ok<<<<<<<<<<<<<<<<

  if (!request.session.user) {
    socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
    socket.destroy();
    return;
  }

  wss.handleUpgrade(request, socket, head, function (ws) {
    wss.emit('connection', ws, request);
  });
});

wss.on('connection', function (ws, request) {
  const user = request.session.user.cpf;

  map.set(user, ws);

  ws.on('message', function (message) {
    console.log(`Received message ${message} from user ${user}`);
  });

  ws.on('close', function () {
    map.delete(user);
  });
});

app.prepare().then(() => {
  createServer((req, res) => {
    // Be sure to pass `true` as the second argument to `url.parse`.
    // This tells it to parse the query portion of the URL.
    const parsedUrl = parse(req.url, true);

    handle(req, res, parsedUrl);
  }).listen(3000, (err) => {
    if (err) throw err;
    console.log('> Ready on http://localhost:3000');
  });
});

server.listen(3232, function () {
  console.log('Websockerts listening on http://localhost:3232');
});

The important part is that when I receive a request in a certain port, the server needs to check if the user is logged in:

server.on('upgrade', async function (request, socket, head) {
  console.log('Parsing session from request...');

  //>>>>>>>>>>>>>>>>>>>something needs to happen here to get the session and check if everything is ok<<<<<<<<<<<<<<<<

  if (!request.session.user) {
    socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n');
    socket.destroy();
    return;
  }

  wss.handleUpgrade(request, socket, head, function (ws) {
    wss.emit('connection', ws, request);
  });
});

This is the documentation part of the wss lib
https://github.com/websockets/ws#client-authentication

Anyone has a clue of how can I check if the user is logged in in this scenario?

Unable to parse cookie: TypeError: argument str must be a string

Hello,

I have this error when I run the example:

TypeError: argument str must be a string
    at Object.parse (C:\Users\vducorps\Development\next-iron-session\example\node_modules\cookie\index.js:51:11)
    at withIronSessionHandler (C:\Users\vducorps\Development\next-iron-session\example\node_modules\next-iron-session\dist\index.js:53:31)

[...]

https://github.com/vvo/next-iron-session/blob/ae1dd6a2cfe2d20add4666e9f91213a5700db0e2/lib/index.js#L50

Do you have any idea why?
Looks like the cookie is not provided.

Usage with next-connect

I try to use next-connect with next-iron-session but my API call stays in pending.

I don't know if the problem comes from this package or from the other, that's why I opened this issue and another one in next-connect.

function withSession(handler) {
  return withIronSession(handler, {
    password: process.env.SECRET_COOKIE_PASSWORD,
    cookieOptions: {
      secure: process.env.NODE_ENV === 'production' ? true : false,
    },
  })
}

const handler = nextConnect()

handler
  .use(withSession)
  .get(async (req, res) => {
    const user = req.session.get('user')
    res.json(user)
  })

export default handler

How to get context on getServerSideProps

Hi, I am new to next-iron-session and I got stuck because I can't get the context for dynamic routes on next.
export const getServerSideProps = withIronSession( async ({ req, res }) => {})

and I need something like

export const getServerSideProps = (context) => withIronSession( async ({ req, res }) => {})

But doing it like that always returns me the following error:
Error serializing props returned from getServerSideProps
Props must be returned as a plain object from getServerSideProps

So I don't know if there is any way I can get context, so I can access the query of URL

Error: Incorrect number of sealed components

I'm getting this error suddenly, very weird. This is a 500 so it seems my next API route is broken, but no error/broken build. Next still saying compile successful. I have found that using incognito bypass the problem.

Duplicated Read Me

The two paragraphs are identical, so I think one can be removed.

I created a PR, plz review

Typescript | Type '{ maxAge: number; }' is not assignable to type 'CookieOptions'.

I'm trying to modify the maxAge so that when a user completes a sales funnel. This cookies expiration should change change from the default to 30 minutes so that they can see their receipt for that amount of time.

Unfortunately, the maxAge is showing an overload, saying it's not assignable to type 'CookieOptions' in typescript.

maxAge

import { Duration } from 'luxon';
import { SessionOptions } from 'next-iron-session';

const leadConfig: SessionOptions = {
  password: process.env.ironSessionPassword,
  cookieName: 'lead',
  cookieOptions: {
    secure: false,
    // secure: process.env.NODE_ENV === 'production',
    maxAge: Duration.fromObject({ hours: 8 }).as('seconds'),

  },
};

export default leadConfig;

While I'm not sure of the proper way to change the expiration on a session, the documentation shows that maxAge is available of cookieOptions.

req.session.get() returns undefined, but cookie is set

I'm probably missing something here, but inside getServerSideProps if I call req.session.get('user') it returns undefined. But when I check the req object:

  • req.cookies has the user key with the token as its value
  • req.headers.cookie also shows the user=abce3234 token value.

What am I missing here?

My hope was to do req.session.get('user') and get the JWT decoded value (like

{ 
  id: 1, 
  name: 'John Doe', 
  login: '[email protected]',  
  // etc...
 }`

Or, if not possible, get at least the token string, so I can manually decode it.

Thank you in advance for any help.

Screenshot 2021-02-12 at 02 41 32

req.session.destroy() doesn't delete cookie

Cookie not being deleted on logout click when deployed on Vercel. Used with your example from:
https://github.com/vercel/next.js/tree/canary/examples/with-iron-session
"dependencies": {
"next": "10.0.6",
"next-iron-session": "4.1.11",
"prop-types": "15.7.2",
"react": "17.0.1",
"react-dom": "17.0.1",
"swr": "0.4.2"
},

All happening on random basis with a random sequence. Login (1), Logout (1), Login (2), Logout (2) - fail, Logout (3) - fail...
Sometimes on login cookie being set but UI doesn't change, after you click login second time, cookie being rewritten and then you get logged in.
Results differ with :

  • Google Chrome @Version 88.0.4324.150 (Official Build) (64-bit)
  • Firefox @85.0.2 (64-bit)

Also your example at https://next-iron-session.now.sh/
is not working (on random basis) on Google Chrome:
TypeError: Cannot read property 'type' of undefined
at SEwE.t.default (profile-sg-b75f51d20ee90d0df268.js:1)
at ro (framework.9d524150d48315f49e80.js:1)
at jo (framework.9d524150d48315f49e80.js:1)
at Qu (framework.9d524150d48315f49e80.js:1)
at Pi (framework.9d524150d48315f49e80.js:1)
at xi (framework.9d524150d48315f49e80.js:1)
at _i (framework.9d524150d48315f49e80.js:1)
at vi (framework.9d524150d48315f49e80.js:1)
at framework.9d524150d48315f49e80.js:1
at t.unstable_runWithPriority (framework.9d524150d48315f49e80.js:1)

On FIrefox trying to logout gives:
GEThttps://next-iron-session.now.sh/api/events
[HTTP/2 401 Unauthorized 165ms]

SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data

UPDATE:
when I set disable cache on Chrome dev tools everything is working perfect.

req.session.save() overrides existing Set-Cookie header values

https://github.com/vvo/next-iron-session/blob/master/lib/index.js#L62-L65

I "think" this line: res.setHeader("set-cookie", [cookieValue] is assuming that there were no other attempts to set cookies before calling req.session.save()

It looks like destroy does something similar: https://github.com/vvo/next-iron-session/blob/master/lib/index.js#L73

I think these calls don't seem to keep any existing values in the Set-Cookie header?

This doesn't seem to work:


const postHandler = async (req, res) => {
  destroyCookie({ req, res }, 'token');
  req.session.destroy();
  res.status(200).json({ message: 'logged out' }).end();
};

But this works fine:

const postHandler = async (req, res) => {
  req.session.destroy();
  destroyCookie({ req, res }, 'token');
  res.status(200).json({ message: 'logged out' }).end();
};

*destoyCookie comes from this package: https://github.com/maticzav/nookies which I am using to manage other cookies.

This kind of hung me up a little bit and required a fair bit of debugging and moving code around trying to understand why my extra cookies weren't being set.

`req.session.destroy()` does not remove cookies with the latest version of NextJS when hosting on Vercel

Hi, I have come across a very annoying bug recently. If you upgrade nextjs to the latest version, which is 10.0.3, and deploy the application on Vercel, the method req.session.destroy() does not remove the cookies.

Here is a repo to reproduce this issue, it's just a redeployment of the next-icon-session's nextjs example but with nextjs upgraded to 10.0.3.

Steps to reproduce:

  1. login
  2. logout (first time it will succeed with a 200 return)
  3. login again
  4. logout (this time it will fail to remove the cookie with a 304 return)

next-iron-session's nextjs example:

1607513150016

after upgrading nextjs to 10.0.3:

1607513145304

Additional information: This issue only happens when deploying on vercel, it works correctly when I run it locally.


Edit: After downgrading nextjs to 10.0.0, it works correcly on vercel.

req.session.get returning undefined

Hi there,

I've been attempting to get a login system set up using sessions, but every time I attempt to get the data out of the req.session, it returns undefined. Finally, I just reverted back to the most basic example included in the README but get the same undefined. Here are what my files look like at this point:

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

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

export default withIronSession(handler, {
  password: process.env.SECRET_COOKIE_PASSWORD,
  cookieName: 'test-cookie',
  cookieOptions: {
    secure: process.env.NODE_ENV === 'production'
  }
});
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: process.env.SECRET_COOKIE_PASSWORD,
  cookieName: 'test-cookie',
  cookieOptions: {
    secure: process.env.NODE_ENV === 'production'
  }
});

I haven't made any changes to the vanilla API set up for Nextjs up to this point, so not really sure what could be causing the issue. I'm even able to see the persistent cookie when printed in getInitialProps from one of the pages, just can't get to the data it contains.

Some further clarification, if it helps: I'm attempting to develop on localhost and have set the secure: false option.

I've been struggling with this for a solid day at this point and hoping you might be able to point me in the right direction. Thanks for your assistance!

Warn on cookie length?

I've just spent a little while debugging an issue with the cookie not being saved as expected. Upon inspection I realised that the API I was working with started returning more information, thus exceeding the cookie byte limit.

Although that issue is on me, if someone else was to run into it, having a warning on cookie size (even dev only) would have reduce the debugging time.

Great work so far!

Automatically renew sessions?

Update: There's no way to automatically renew sessions. You can read this issue to know why. If you want very long sessions then set ttl appropriately or set it to 0 for very long sessions or set maxAge: undefined for session cookies.

Update: Yes we should do it, taking ideas and PR on this issue, thanks

Should we automatically refresh/touch/renew sessions? For example every time the session is saved we should make sure the cookie expiration time is pushed further.

We should also provide examples where calling .save() would be beneficial (like when user logs in?)

Production usage will provide better insights, comment if you have some!

Best Practice for using access token

Hi there, I am quite new to nextjs and JAMstack. The project I am building right now is using laravel as backend and Nextjs as front end, communicate through API. I am able to do a simple login and logout using the example given. Based on what I understand (using login as example here), after user press the login button, it will first go through api routes in Nextjs for example /api/login then only go the the backend API. Is this the best practice to go through two API for one function for CRUD as well? Or I can just use 'getStaticProps' or 'getServerSideProps' to get the access token then directly from there, call to the real API.

What I really want to know is that, is it really need to go through the Nextjs api route first?

Can't Provide sameSite: "none" in cookieOptions

First of all, thanks for creating this amazing library.
I know I shouldn't use sameSite: "none" in cookieOptions. But I need to use this for testing purposes.
This is my cookieOptions:

cookieOptions: {
    httpOnly: true,
    secure: isProduction,
    // sameSite: "strict" as "strict",
    sameSite: "none" as "none",
    maxAge: 60 * 60, // 2 hours
    path: "/",
  },

The strict and lax work as expected and it sets the cookie as strict or lax.
But when I use none, my server crashes with this error.

Error [ERR_STREAM_WRITE_AFTER_END]: write after end
    at writeAfterEnd (_http_outgoing.js:668:15)
    at ServerResponse.end (_http_outgoing.js:788:7)
    at ServerResponse.end (G:\Development\final-project\frontend\node_modules\next\dist\compiled\compression\index.js:1:2746)
    at DevServer.handleRequest (G:\Development\final-project\frontend\node_modules\next\dist\next-server\server\next-server.js:34:1169)
Emitted 'error' event on ServerResponse instance at:
    at writeAfterEndNT (_http_outgoing.js:727:7)
    at processTicksAndRejections (internal/process/task_queues.js:81:21) {
  code: 'ERR_STREAM_WRITE_AFTER_END'
}
error Command failed with exit code 1.

Why is this happening and what's the solution?

Hello~

Hello~
I'm learning web development. I am using next-iron-session and get the message: Missing password parameter even if I have set SECRET_COOKIE_PASSWORD in the .env file. I would like to knwo what could be the cause of the error.

Originally posted by @pkabore in #130 (comment)

Automatically warn on bad usage

  • [ ] When handler was wrapped with iron session but no session.save ran, or no session.get ran then we should console.warn because there's no need to wrap your handler if you're not using the session: there could be a "performance" issue if you do so because of crypto tools taking some time. cancelled, there could be cases where this is on-purpose (like a global session installation instead of per route).
  • When session.save is called after res finish then warn also because we can't set headers in this case
  • When secure: true and req protocol is http

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.