Giter Site home page Giter Site logo

hoangvvo / next-session Goto Github PK

View Code? Open in Web Editor NEW
336.0 2.0 21.0 2.01 MB

Simple promise-based session middleware for Next.js, micro, Express, and more

Home Page: https://www.npmjs.com/package/next-session

License: MIT License

JavaScript 3.51% TypeScript 96.49%
javascript middleware session promise nextjs

next-session's Introduction

next-session

npm minified size CircleCI codecov PRs Welcome

Lightweight promise-based session middleware for Next.js. Also works in micro or Node.js HTTP Server, Express, and more.

Also check out alternatives like next-iron-session. Take a look at nextjs-mongodb-app to see this module in use.

Installation

// NPM
npm install next-session
// Yarn
yarn add next-session

Usage

๐Ÿ‘‰ Upgrading from v1.x to v2.x? Please read the release notes here!

๐Ÿ‘‰ Upgrading from v2.x to v3.x? Please read the release notes here!

๐Ÿ‘‰ Upgrading from v3.x to v4.x? Please read the release notes here!

Warning The default session store (if options?.store is undefined), MemoryStore, DOES NOT work in production or serverless environment. You must use a Session Store.

// ./lib/get-session.js
import nextSession from "next-session";
export const getSession = nextSession(options);

API Routes

import { getSession } from "./lib/get-session.js";

export default function handler(req, res) {
  const session = await getSession(req, res);
  session.views = session.views ? session.views + 1 : 1;
  // Also available under req.session:
  // req.session.views = req.session.views ? req.session.views + 1 : 1;
  res.send(
    `In this session, you have visited this website ${session.views} time(s).`
  );
}

Usage in API Routes may result in API resolved without sending a response. This can be solved by either adding:

import nextSession from "next-session";
const getSession = nextSession();

export default function handler(req, res) {
  const session = await getSession(req, res);
  /* ... */
}

export const config = {
  api: {
    externalResolver: true,
  },
};

...or setting options.autoCommit to false and do await session.commit().

import nextSession from "next-session";
const getSession = nextSession({ autoCommit: false });

export default function handler(req, res) {
  const session = await getSession(req, res);
  /* ... */
  await session.commit();
}

getServerSideProps

import { getSession } from "./lib/get-session.js";

export default function Page({ views }) {
  return (
    <div>In this session, you have visited this website {views} time(s).</div>
  );
}

export async function getServerSideProps({ req, res }) {
  const session = await getSession(req, res);
  session.views = session.views ? session.views + 1 : 1;
  // Also available under req.session:
  // req.session.views = req.session.views ? req.session.views + 1 : 1;
  return {
    props: {
      views: session.views,
    },
  };
}

Others

express, next-connect

const express = require("express");
const app = express();
app.use(async (req, res, next) => {
  await getSession(req, res); // session is set to req.session
  next();
});
app.get("/", (req, res) => {
  req.session.views = req.session.views ? req.session.views + 1 : 1;
  res.send(
    `In this session, you have visited this website ${req.session.views} time(s).`
  );
});

micro, Vercel Serverless Functions

module.exports = (req, res) => {
  const session = await getSession(req, res);
  res.end(
    `In this session, you have visited this website ${session.views} time(s).`
  );
};

Node.js HTTP Server

const http = require("http");

const server = http.createServer(async (req, res) => {
  const session = await getSession(req, res);
  res.end(`In this session, you have visited this website ${session.views} time(s).`;
});
server.listen(8080);

Options

next-session accepts the properties below.

options description default
name The name of the cookie to be read from the request and set to the response. sid
store The session store instance to be used. Required to work in production! MemoryStore
genid The function that generates a string for a new session ID. nanoid
encode Transforms session ID before setting cookie. It takes the raw session ID and returns the decoded/decrypted session ID. undefined
decode Transforms session ID back while getting from cookie. It should return the encoded/encrypted session ID undefined
touchAfter Only touch after an amount of time (in seconds) since last access. Disabled by default or if set to -1. See touchAfter. -1 (Disabled)
autoCommit Automatically commit session. Disable this if you want to manually session.commit() true
cookie.secure Specifies the boolean value for the Secure Set-Cookie attribute. false
cookie.httpOnly Specifies the boolean value for the httpOnly Set-Cookie attribute. true
cookie.path Specifies the value for the Path Set-Cookie attribute. /
cookie.domain Specifies the value for the Domain Set-Cookie attribute. unset
cookie.sameSite Specifies the value for the SameSite Set-Cookie attribute. unset
cookie.maxAge (in seconds) Specifies the value for the Max-Age Set-Cookie attribute. unset (Browser session)

touchAfter

Touching refers to the extension of session lifetime, both in browser (by modifying Expires attribute in Set-Cookie header) and session store (using its respective method) upon access. This prevents the session from being expired after a while.

In autoCommit mode (which is enabled by default), for optimization, a session is only touched, not saved, if it is not modified. The value of touchAfter allows you to skip touching if the session is still recent, thus, decreasing database load.

encode/decode

You may supply a custom pair of function that encode/decode or encrypt/decrypt the cookie on every request.

// `express-session` signing strategy
const signature = require("cookie-signature");
const secret = "keyboard cat";
session({
  decode: (raw) => signature.unsign(raw.slice(2), secret),
  encode: (sid) => (sid ? "s:" + signature.sign(sid, secret) : null),
});

API

session object

This allows you to set or get a specific value that associates to the current session.

//  Set a value
if (loggedIn) session.user = "John Doe";
//  Get a value
const currentUser = session.user; // "John Doe"

session.touch()

Manually extends the session expiry by maxAge. Note: You must still call session.commit() if autoCommit = false.

session.touch();

If touchAfter is set with a non-negative value, this will be automatically called accordingly.

session.destroy()

Destroy to current session and remove it from session store.

if (loggedOut) await session.destroy();

session.commit()

Save the session and set neccessary headers. Return Promise. It must be called before sending the headers (res.writeHead) or response (res.send, res.end, etc.).

You must call this if autoCommit is set to false.

session.hello = "world";
await session.commit();
// always calling res.end or res.writeHead after the above

session.id

The unique id that associates to the current session.

Session Store

The session store to use for session middleware (see options above).

Implementation

A compatible session store must include three functions: set(sid, session), get(sid), and destroy(sid). The function touch(sid, session) is recommended. All functions must return Promises.

Refer to MemoryStore.

TypeScript: the SessionStore type can be used to aid implementation:

import type { SessionStore } from "next-session";

class CustomStore implements SessionStore {}

Compatibility with Express/Connect stores

Promisify functions

To use Express/Connect stores, you must promisify get, set, destroy, and (if exists) touch methods, possibly using util.promisify.

We include the util promisifyStore in next-session/lib/compat to do just that:

import nextSession from "next-session";
import { promisifyStore } from "next-session/lib/compat";
import SomeConnectStore from "connect-xyz";

const connectStore = new SomeConnectStore();

const getSession = nextSession({
  store: promisifyStore(connectStore),
});

You can use expressSession from next-session/lib/compat if the connect store has the following pattern.

const session = require("express-session");
const RedisStore = require("connect-redis")(session);

// Use `expressSession` from `next-session/lib/compat` as the replacement

import nextSession from "next-session";
import { expressSession, promisifyStore } from "next-session/lib/compat";
import RedisStoreFactory from "connect-redis";
import Redis from "ioredis";

const RedisStore = RedisStoreFactory(expressSession);
export const getSession = nextSession({
  store: promisifyStore(
    new RedisStore({
      client: new Redis(),
    })
  ),
});

Contributing

Please see my contributing.md.

License

MIT

next-session's People

Contributors

akifumisato avatar dbachrach avatar dependabot-preview[bot] avatar dependabot[bot] avatar ealtunyay avatar georgekamar avatar hannut91 avatar hoangvvo avatar lumakernel 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

next-session's Issues

Does this work with a custom server like express?

Hello, I'm having issues trying to use next-session with a custom server.

const expressSession = require('next-session').expressSession
const promisifyStore = require('next-session').promisifyStore
const session = require('next-session').session
MemcachedStore = require("connect-memcached")(expressSession);

const server = express()
server.use(session({
store: promisifyStore(new MemcachedStore({
hosts: ["127.0.0.1:11211"],
secret: "super-mega-secret"
}))
}))

This doesn't even return a Set-Cookie header. I've tried several methods but still no luck.

Replace MemoryStore with CookieStore

MemoryStore is currently the default store in next-session. It saves all the data in the memory, which is obviously not suited for Production.

CookieStore stores everything in a signed cookie. The cookie is readable by the user but will not be tamperable.

Like MemoryStore, CookieStore will still be noted as not recommended and should only be used in development.

Any opinion?

Please add export for isNew in new release.

Due to my custom needs in building a backend for my application I have extended the package.json to export isNew from symbol. So that I can use a validation check if the session is new and easily import it with import { isNew } from "next-session/lib/symbol"; . I am writing here as a suggestion to add this to the official release if possible as I think that isNew, isTouched and isDestroyed can give us more freedom to write some custom functionalities.

Why are we not auto setting sid cookie?

Noticed that we do not set sid cookie automatically. Would like to understand the idea behind doing this?

Shouldn't there be a way to automatically check if sid cookie exists, set req.session else generate a new session and set the cookie?

Next also supports express style middlewares (https://nextjs.org/docs/middleware) Can we leverage that?

Multiple calls to res.end()

We are running into an issue with how next-session intercepts res.end. If we do redirects, we will have multiple calls to res.end(). This looks to be fine and should just silently do nothing on the second occurence of res.end().

You can see that express-session handles this case: https://github.com/expressjs/session/blob/85682a2a56c5bdbed8d7e7cd2cc5e1343c951af6/index.js#L249

Which came in this commit:
expressjs/session@7077190

I think we should have similar logic in next-session like, so it handles multiple invocations of res.end():

let ended = false;
//  Proxy res.end
res.end = function resEndProxy(...args) {
  if (ended) {
    return false;
  }

  ended = true;
  // ...

Session doesn't save

The session does not save anything even though autoCommit is passed into the options. export default withSession(handler, { autoCommit: true, cookie: { maxAge: 2.628e9 } }); When I refresh and try to update the session before that, all the properties are gone. Is there a way to get that session by it's id? The docs aren't very clear about that.

Cookie does not provide an export names "serialize"

register.js

import { getSession } from "lib/sessionStore.js";

export default async function handler(req, res) {

}

sessionStore.js

import nextSession from "next-session";
export const getSession = nextSession();

Upon removing the import the error goes away indicating an issue with the module.

Improve View count example

Hi, i have this setup
"dependencies": {
"@formatjs/intl-relativetimeformat": "^2.8.3",
"@formatjs/intl-utils": "^0.7.0",
"@material-ui/core": "^4.3.3",
"@material-ui/icons": "^4.2.1",
"express": "^4.17.1",
"formatjs": "^0.1.1",
"intl": "^1.2.5",
"isomorphic-unfetch": "^3.0.0",
"js-cookie": "^2.2.1",
"material-ui": "^0.20.2",
"micro": "^9.3.4",
"next": "9.0.5",
"next-cookies": "^1.1.3",
"next-i18n-routes": "^1.0.2",
"next-i18next": "^1.1.2",
"next-session": "^2.0.0",
"react": "16.9.0",
"react-dom": "16.9.0",
"react-intl": "^3.1.13",
"react-redux": "^7.1.1",
"redux": "^4.0.4",
"redux-devtools-extension": "^2.13.8"
}

and here is my /api/x.js test page

import { withSession } from 'next-session';

const yourOptions = {}

const handler = (req, res) => {
req.session.views = req.session.views ? (req.session.views + 1) : 0;
res.send(In this session, you have visited this website ${req.session.views} time(s).)
};
export default withSession(handler, { ...yourOptions });

all i get is "0" after each page refresh

Memory store not working with npm run dev

When we run the next-session with default memory store in npm run dev mode. This refresh the memory on every page hit as nextjs recompiles the app and the session is not maintained. It works in npm run build mode though.

Just not working :)

Library is not working with actual Next version.

If you setting session in API handler, it will set. And it can be obtained in API handler. But if you will try to work with session in getServerSideProps(), there will be a conflict, and session will reset (rewrite). "Common" session in API handlers and in pages is not common actually.

How to reproduce:

  1. Run following command:
yarn create next-app

with all defaults.

Result in package.json is:

...
"next": "12.0.7",
...
yarn add next-connect next-session

Result in package.json is:

...
"next-connect": "^0.11.0",
"next-session": "^4.0.4",
...
  1. lib/get-session.js
import nextSession from "next-session";
export const getSession = nextSession();
  1. Replace pages/api/hello.js code with following one:
import { getSession } from "../../lib/get-session.js";

export default async function handler(req, res) {
  const session = await getSession(req, res);
  session.views = session.views ? session.views + 1 : 1;
  // Also available under req.session:
  // req.session.views = req.session.views ? req.session.views + 1 : 1;
  res.send(
    `In this session, you have visited this website ${session.views} time(s).`
  );
}
  1. In pages/index.js add:
import { getSession } from "../lib/get-session.js";
  1. And also in pages/index.js, at bottom, add:
export async function getServerSideProps({ req, res }) {
  const session = await getSession(req, res);
  session.views = session.views ? session.views + 1 : 1;
  // Also available under req.session:
  // req.session.views = req.session.views ? req.session.views + 1 : 1;
  console.log('VIEWS-SSP', session.views);
  return {
    props: {
      views: session.views,
    },
  };
}
  1. Run it!
yarn run dev
  1. Visit in browser 3 times: http://localhost:3000/api/hello
    Output is "In this session, you have visited this website 3 time(s)."

  2. Now visit http://localhost:3000.

Expected server log:

VIEWS-SSP 4

Actual server log:

VIEWS-SSP 1
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res
warn  - You should not access 'res' after getServerSideProps resolves.
Read more: https://nextjs.org/docs/messages/gssp-no-mutating-res

9.1. Also, now visit http://localhost:3000/api/hello again.

Expected result: "In this session, you have visited this website 5 time(s)."

Actual result: "In this session, you have visited this website 1 time(s)."

So session just resets when we access it from getServerSideProps().


Also tried autoCommit: false. Now there are no couples of warns when I access page, but session still resetting.

res.redirect() doesn't set actual session cookie

Hi!

I'm using the following code to authenticate a user using passport.js:

import nextConnect from 'next-connect'
import {middleware} from 'src/core/middleware'
import {passport} from 'src/core/passport'

export default nextConnect()
  .use(middleware)
  .get(passport.authenticate('github', {successRedirect: '/'}))

Not so important, but FYI: middleware includes database and next-session configuration, passport.initialize(), passport.session(). As far as I can see everything configured properly.

It works well if I do not use a redirect after successful authentication.
For example, this code works well:

export default nextConnect()
  .use(middleware)
  .get(passport.authenticate('github'))
  .get(function (req, res) {
    res.json({user: req.user});
  })

The result of this code: session is set correctly, the browser cookie points to the correct session.

But if I replace res.json() with redirect: res.redirect('/'), or use {successRedirect: '/'} parameter of passport.js, then the valid session cookie simply not set. I can see the correct session in my Redis storage, but the browser still holds the previous cookie, and the correct one is simply lost.

What's wrong with redirects? Why they lose the correct cookie and how to workaround this?

Thanks!

useSession to return session values

Beside applying session into req object, useRouter should also return an object with all the session values.

This might be helpful in the case where there is no logic in getInitialProps:

Page.getInitialProps = (req,res) => useSession(req, res)

In this case, all session variables will be available for use inside the component.

Add next-session usage for document middleware

Hi, we are investigating session management with nextjs and like this project's approach. Attaching middleware for API routes works great. I'm not sure how this project works with fetching session data from the client-side code (i.e. how _app.js's getInitialProps can fetch session data). Since getInitialProps is executed and bundled for both client and server side code, how can Session Store (a postgres js library -- in our case) not be bundled into the client-side javascript? Even if it can be bundled, it's not going to be able to communicate to the postgres database since it's executing in the user's browser instead of our backend server.

Maybe the approach is to only do this in _document.js since that should be only bundled in a server bundle? This next RFC would be the ideal solution (I think) vercel/next.js#7208

At the moment we are doing the following: Expose an API route /api/auth/session, which responds with JSON with session details. In the _app's getInitialProps we make an API call to this endpoint. That API call can be executed from client or server code.

Thanks!

cookie.maxAge doesnt work

I have set cookie.maxAge to 30 days (2592000s) and it used to work (@3.2.3). But it stopped working recently after updating to the latest version (@3.3.1). I was looking at this line of the code and was wondering if this should be req.session.cookie.expires instead? Thanks.

feature request: specify a type for session object

I want to be able to specify the types that can be set in a session.

type AppSession = {
  name?: string
}
export const getSession = nextSession<AppSession>(options);

// useage
const session = await getSession()
session.name = 'name' // ok
session.foo = 'bar' // type error

Setting cookie.httpOnly option to false does not work

Setting cookie.httpOnly option to false does not work.

export const getSession = nextSession({
  httpOnly: false,
});

export default async (req, res) => {
  const session = await getSession(req, res);
  // session: {
  //   cookie: {
  //     path: '/',
  //     httpOnly: true,
  //     domain: undefined,
  //     sameSite: undefined,
  //     secure: false
  //   },
  //   [Symbol(session.isNew)]: true
  // }
}

V3 Roadmap

Is there a V3 roadmap out there? I'd love to help push this along to the next version.

Module not found with Yarn 2

Hi,

I'm trying to migrate my NextJS app with Yarn 2 (version 3.1.1) and PnP, but when app compile I've got an error which says next-session is not defined.
Stacktrace suggestion is to import /lib/session, but it doesn't work either.

Is there a reason why is does not work ?

Stacktrace :

This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason:
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'next-session' imported from /Users/xxxxxx/.next/server/pages/_app.js
Did you mean to import next-session-npm-4.0.4-01166cd426-d602a2d75a.zip/node_modules/next-session/lib/session.cjs?
    at new NodeError (node:internal/errors:371:5)
    at packageResolve (node:internal/modules/esm/resolve:894:9)
    at moduleResolve (node:internal/modules/esm/resolve:940:18)
    at defaultResolve (node:internal/modules/esm/resolve:1043:11)
    at ESMLoader.resolve (node:internal/modules/esm/loader:530:30)
    at ESMLoader.getModuleJob (node:internal/modules/esm/loader:251:18)
    at ESMLoader.import (node:internal/modules/esm/loader:332:22)
    at importModuleDynamically (node:internal/modules/cjs/loader:1036:29)
    at importModuleDynamicallyWrapper (node:internal/vm/module:437:21)
    at importModuleDynamically (node:vm:381:46)

redirect in getServerSideProps

is this working with the new redirect declaration?
I cant get it to store the session with redirect otherwise its working.

 this does not help either
 await req.session.commit();

 return {
    props: { },
     redirect: {
       permanent: false,
       destination,
     },
  };

cheers!

How to implement SessionStore, can you give me example of implement SessionStore function๏ผŸ(typescript)

I try to use withSession like:

export default function withMySession(handler: NextApiHandler) {
    return withSession(handler, {
      store: new MySessionStore(),
      cookieName: 'next.js/examples/with-iron-session',
      cookieOptions: {
        // the next line allows to use the session in non-https environments like
        // Next.js dev mode (http://localhost:3000)
        secure: process.env.NODE_ENV === 'production' ? true : false,
      },
    })
  }

but when I try to implement the session, I find errors:

interface MySession {
  userName: string;
  password: string;
}
class MyStore extends SessionStore {
    get: (sid: string): SessionData => {

    };
    innerStore: MySession
    constructor(){
        super();
        this.innerStore= {userName:'', password:''}
    }

    set: (sid: string, sess: SessionData) => Promise<void>;
    destroy: (sid: string) => Promise<void>;

}

the get method need like:

get: (sid: string) => Promise<SessionData | null>;

I do not know how to return SessionData with Promise in simple method. and I do not understand why using promise here. Can i just work like:

get: (sid: string) => {
        for(sid in this.innerStore) {
            return innerStore[sid];
            }
            return null;
        }

The return type mismatched, How to return SessionData in simple way.

Bug/New Feature: Set-Cookie headers are overwritten for new sessions

When req.session.isNew is true a new cookie and session id will be written. If a the 'Set-Cookie' header is called before this in an api route for example those cookies will be overwritten instead of concatenated. Here's a code example:

import { withSession } from 'next-session';
import { serialize } from "cookie";

export default withSession(async (req, res) => {
  res.setHeader('Set-Cookie', serialize('test', 'test', {path:'/', httpOnly: false}));
  res.statusCode = 200
  req.session.test = req.session.test ? req.session.test + 1 : 1;
  res.json({ name: 'John Doe', test: req.session.test })
}, {});

The cookie won't get saved to the browser since it gets overwritten in the 'commitHead' method in core.ts. Here's a potential fix that will allow cookies to be concatenated in this situation (the code below is in Javascript not Typescript):

const commitHead = (req, res, options) => {
    if (res.headersSent || !req.session)
        return;
    if (req.session.isNew || (options.rolling && req[SESS_TOUCHED])) {
        let sessionCookie = [cookie_1.serialize(options.name, options.encode ? options.encode(req.session.id) : req.session.id, {
            path: req.session.cookie.path,
            httpOnly: req.session.cookie.httpOnly,
            expires: req.session.cookie.expires,
            domain: req.session.cookie.domain,
            sameSite: req.session.cookie.sameSite,
            secure: req.session.cookie.secure,
        })];
        const currentCookie = res.getHeader('set-cookie');
        if (currentCookie) {
            if (typeof currentCookie === 'string') {
                sessionCookie.push(currentCookie);
            } else {
                sessionCookie = sessionCookie.concat(currentCookie);
            }
        }
        res.setHeader('Set-Cookie', sessionCookie);
    }
};

Not sure if you originally designed the code just to overwrite any cookies set before but I think cookies should be concatenated if the header has already been set

res.end() does not work if autoCommit is true

So Playing with this and i think i've nailed down a problem with applySessions stub of res.end

You can see in this example = demo

If you change the options argument "autoCommit: false" and reload the page, it will correctly redirect to /foo. But with autoCommit: true (the default) it will just hang..

Jim

Session is loading in localhost but not loading in aws amplify.

I tried to save user info in login handler using applySession and loads it in custom app getInitialProps.
It's working well in localhost but not working when I deploy to aws amplify.

export default async function handler(req, res) {
    try {
        const signedUser = req.body
        await applySession(req, res, {cookie: {httpOnly: false, secure: true}})
        req.session.user = signedUser
        res.status(200).json({
            success: true,
        })
    } catch (err) {
        console.log(err)
    }
}
MyApp.getInitialProps = async (appContext) => {
    const appProps = await App.getInitialProps(appContext)
    const req = appContext.ctx.req
    const res = appContext.ctx.res
    if (!req) {
        return {
            ...appProps,
            signedUser: null,
        }
    }
    await applySession(req, res, {})
    return {
        ...appProps,
        signedUser: req.session.user || null,
    }
}

UnhandledPromiseRejectionWarning when an error occurred in session auto commit

Hi! I'm writing a custom Sequelize Store for one of my projects. The error happens when next-session is trying to commit a session. As far as I can tell from the logs, the error appears in this function:

next-session/src/core.ts

Lines 54 to 57 in 07a200b

res.end = async function resEndProxy(...args: any) {
await req.session.save();
oldEnd.apply(this, args);
};

Perhaps having error handler within the res.end() would fix that problem?
If you want to see the error in action, just clone this repo and throw any error from Store#set() method and then open /api/auth/login.
Or just do the same thing in MemoryStore, it should work same way :)

next-session does not touch browser cookie

Hi Hoang!

It's still an issue for me: the browser cookie doesn't update its "expires" value although rolling = true:

session({
  store: promisifyStore(redisStore),
  rolling: true,
  cookie: {
    secure: true,
    httpOnly: true,
    sameSite: 'lax',
    maxAge: 7 * 24 * 60 * 60,
  }
})

Here is a screencast: https://take.ms/12A7f

Maybe I do something wrong?

Relates to: #46

next-session showing "API resolved without sending a response" log message every request

I installed next-session
Then I make a file in api/withsession.js

import { withSession } from 'next-session';
 
const handler = (req, res) => {
  req.session.views = req.session.views ? (req.session.views + 1) : 1;
  res.send(`In this session, you have visited this website ${req.session.views} time(s).`)
};
export default withSession(handler);

It shows this message every time I visit this page in the logs.
API resolved without sending a response for /api/withsession, this may result in a stalled requests.

Not working in production versions

My session data doesn't persist across multiple routes on the production version; specifically on the vercel deployment. This originally wouldn't even work on my localhost production version, but its seems disabling the secure field in the cookie fixed it for localhost, but not on vercel. To be clear, running the app with npm run dev allows sessions to work with no issues at all.

/api/auth/steam routes

// Session stuff
handler.use(session({
    name: 'somecookiename',
    cookie : {
        secure: false
    }
}));

handler.use(steam.middleware({
    realm: `${host}/`, 
    verify: `${host}/api/auth/steam/return`,
    apiKey: steamapikeyhere
}));
handler.get('/api/auth/steam', steam.authenticate());
handler.get('/api/auth/steam/return', steam.verify(), async (req, res) => {
     // At this point the session variables are valid and readable in req.user and req.session.steamUser (they successfully console log)
    await req.session.commit();
    res.redirect("/");
});
handler.get('/api/auth/logout', steam.enforceLogin("/"), (req, res) => {
    req.logout();
    res.redirect("/");
});
export default handler

/index.js - The route i am trying to retrieve the session data at

export async function getServerSideProps({req, res}) {
    await applySession(req, res, {
        name: 'somecookiename',
        cookie : {
            secure: false
        }
    });
    console.log(req.user, req.session.steamUser)
    // undefined
    return {props: {stuff: "foo"}};
}

Rewrite tests

May close #37 in favor of this.

The way next-session is tested is not ideal since it has a lot of monkey-patching. Furthurmore, the only read thing that is tested is withSession() - useSession() is not well tested.

Session stores expect maxAge to be in milliseconds not seconds

Session stores that are made for express-session use milliseconds in maxAge, and because next-session uses seconds instead the session gets dropped from the session store well before it should and well before the cookie expires. For example, putting 86400 (1 day) into maxAge results in the session expiring in about a minute.

Using seconds in next-session should not be a problem, but passing this value to the session store might have this affect based on what the session store uses to calculate the expiration date. The ones that base their calculations on maxAge are affected, the ones that use the precalculated date provided in expires are not. So, for example connect-redis works fine. But so far, I tested connect-sqlite3 and better-sqlite3-session-store and both of them have this problem.

Here is the documentation for express-session covering the maxAge option: https://expressjs.com/en/resources/middleware/session.html#cookiemaxage

Works on localhost but not deployed

Thank you so much for this amazing project.

Is there a special configuration to ensure it works on the server? It's deployed on the .now.sh.
This is my current configuration
cookie: {
path: "/",
maxAge: 60
602414*1000
}

Thanks.

Remove parseToMs

There is a chance I will remove parseToMs because this feature is not ideal, redundant.

connectMongo type check from example documentation

Hi I'm doing this:

import Session, {
  session,
  promisifyStore,
  Store,
  MemoryStore,
} from "next-session";
import connectMongo from "connect-mongo";

const MongoStore = connectMongo({ Store, MemoryStore });

declare module "next" {
  interface NextApiRequest {
    session: any;
  }
}

export default function (req, res, next) {
  const mongoStore = new MongoStore({
    mongooseConnection: req.mongooseConnection, // see how we use req.dbClient from the previous step
    stringify: true,
    secret: "squirrel",
  });
  return session({
    store: promisifyStore(mongoStore),
  })(req, res, next);
}

and I'm getting this type error:
Screenshot 2020-05-19 at 22 11 04

I'm using:

    "connect-mongo": "^3.2.0",
    "mongoose": "^5.9.14",
    "next": "9.4.1",
    "next-connect": "^0.6.6",
    "next-session": "^3.1.0",

However the basic session functionality seems to work - but haven't tested extensively.
What can I do to fix this?

Documentation discussion: invoking connect function as middleware leads to event listener accumulation

I didnt find anywhere to open a discussion so Im mentioning this here.

In the documentation for this project I found that the connect function is called as a middleware like so:

import { session } from 'next-session';

const handler = nextConnect()
  .use(session({ ...options }))

I found that when calling it that way, instead of saving what the function returns and using that, .on('connect') and .on('disconnect') event listeners start piling up every time the middleware function is called, leading to a maxListenersExceeded warning. This doesn't:

import { session } from 'next-session';

const mySession = session({ ...options });

const handler = nextConnect()
  .use(mySession)

Not sure if this is fine, Im interested in your feedback on this. If Im making a good point maybe it would be a good idea to update the ReadMe to avoid having to dig in the code to find out where the event listeners get added.

Thanks

How to use withSession method with TypeScript?

export default withSession((req: NextApiRequest, res: NextApiResponse): void => {
  res.json(req.session);
});

or

export default withSession(((req, res) => {
  res.json(req.session);
}) as NextApiHandler);

then

TypeScript error: Property 'session' does not exist on type 'NextApiRequest'.

Implement touch

Implement the touch method.

Quoting from express-session:

This is primarily used when the store will automatically delete idle sessions and this method is used to signal to the store the given session is active, potentially resetting the idle timer.

This is used for session with an expiry. The session should prolong when the user is still using the app. This avoid removing the session while the app is being used.

TypeError with connect-pg-simple Session Store

We are using a postgres based express-compatible session store: https://github.com/voxpelli/node-connect-pg-simple. We get the following error though:

TypeError: Class constructor Store cannot be invoked without 'new'

This looks like the way next-session declares its Store does not conform to how express-session declares its Store.

You can compare

Unfortunately, next-session's usage of class makes it incompatible with some express-session compatible stores.

Rewrite documentation

Documentation needs to be rewritten to showcase usage in both API Routes and _app, _document and pages.

It also needs to update for 2.0.0 in #20

Bugs! Sessions read from all browsers

Hi guys, i try use session, but I found my session data can read from all browser, I use 2 browser in same time (firefox and chrome) and in my page, resut is give same value from same session.

this my scripts:


var data=0;
export default class Home extends Component {
constructor (props) {
    super(props)
   data++; //incrementing
 }
 render() {
  let { path, url } = this.props;
  return ( <h1>{props}</h1>)
}
}

export async function getServerSideProps({ req, res }) {
  await applySession(req, res, {});
  req.session.views=data; //req.session.destroy();
  return {
    props: {
      views: req.session.views
    }
  }
}

Result:

3

in firefox show ### 3 too. in firefox should show 1 case I open firefox for one time

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.