Keystone: Cookies not set in the client v5

Created on 14 Feb 2020  路  10Comments  路  Source: keystonejs/keystone

Bug report

Describe the bug

When using password auth strategy keystone is not setting the keystone.sid cookie in the client preventing the auth process to function properly (app deployed to Heroku).

I have another app (also deployed to Heroku) built with keystone v4 and it works fine.

To Reproduce

  1. Install Keystone
  2. Configure it with password auth strategy
  3. Deploy it
  4. Check that the login process is not working due to cookie issues

Expected behavior

It was expected that a cookie was set in the client app to enable the login process.

Most helpful comment

I haven't reproduced this personally but it sounds like the same issue people have with secure cookies when Keystone apps are deployed behind proxies (#1887).

Basically, when NODE_ENV is production, secureCookies defaults to true. This causes Express to _not_ send cookies to the client unless the request it receives is over HTTPS. Since Heroku usually provides the TLS certificate they terminates TLS for you -- the requests that actually reach your app are HTTP (but are internal to Heroku's network so it's ok).

Specifying secureCookies as false will resolve the issue but a better solution (assuming it works, I haven't tested on Heroku) would be to configure the trust proxy Express setting. This (and a heap of related information) is included in my write up on the topic.

TL;DR, you can to export a a configureExpress() function from your app entry point that configures the trust proxy setting. This tells Express to send the secure cookie, event though the request is over HTTP, because the connection between the proxy and the browser is still secure.

You end up with something like...

module.exports = {
  keystone,
  apps: [
    new GraphQLApp(),
    new AdminUIApp({ authStrategy, enableDefaultRoute: true }),
  ],
  configureExpress: app => {
    app.set('trust proxy', true);
  },
};

In this scenario you can leave secureCookies as true which is, well, more secure. 馃檪

All 10 comments

When saying "login process", are you referring to the Admin UI or your own frontend client?

The Admin UI should set a keystone.sid cookie as part of the authentication process (ie; using the login screen). This will grant you access to the Admin UI only.

For your own frontend, you'll need to look store the token returned from Mutation.authenticateUserWithPassword, then send it along with each request your frontend makes as an auth header: Authentication: Bearer [token]

It's built this way to support the usecases of cookie-less environments (ie; querying from node) which will want direct access to the token that it can store in another method besides a cookie.

Hey,

I'm sorry for the poor explanation. What is happening is that I simply cannot login. When I check the dev tools (chrome) for the keystone.sid cookie it's not there, so I'm assuming it's related to that. This is happening in the AdminUI. Meanwhile, I've read other issues (#2042 ) and got the app to work by setting the secure cookies to false. Again I'm sorry for rushing a bug report. I should have read these issues first!

How exactly is the token returned from mutation authenticateUser created? I checked and it doesnt seem like it JWT, if so, how is keystone verifying it?

I have the same problem in heroku that I can't log in.

I haven't reproduced this personally but it sounds like the same issue people have with secure cookies when Keystone apps are deployed behind proxies (#1887).

Basically, when NODE_ENV is production, secureCookies defaults to true. This causes Express to _not_ send cookies to the client unless the request it receives is over HTTPS. Since Heroku usually provides the TLS certificate they terminates TLS for you -- the requests that actually reach your app are HTTP (but are internal to Heroku's network so it's ok).

Specifying secureCookies as false will resolve the issue but a better solution (assuming it works, I haven't tested on Heroku) would be to configure the trust proxy Express setting. This (and a heap of related information) is included in my write up on the topic.

TL;DR, you can to export a a configureExpress() function from your app entry point that configures the trust proxy setting. This tells Express to send the secure cookie, event though the request is over HTTP, because the connection between the proxy and the browser is still secure.

You end up with something like...

module.exports = {
  keystone,
  apps: [
    new GraphQLApp(),
    new AdminUIApp({ authStrategy, enableDefaultRoute: true }),
  ],
  configureExpress: app => {
    app.set('trust proxy', true);
  },
};

In this scenario you can leave secureCookies as true which is, well, more secure. 馃檪

@molomby Brilliant! Your writeup should be linked in the docs.

@molomby Thanks! Worked like a charm. It might be nice if this was part of the create-keystone app. It could ask you if you are using Heroku and add this automatically for you.

It looks like there hasn't been any activity here in over 6 months. Sorry about that! We've flagged this issue for special attention. It wil be manually reviewed by maintainers, not automatically closed. If you have any additional information please leave us a comment. It really helps! Thank you for you contribution. :)

@molomby Thanks! Worked like a charm. It might be nice if this was part of the create-keystone app. It could ask you if you are using Heroku and add this automatically for you.

Just to note this is also true for AWS setups that include a Load Balancer proxying requests from HTTPS targeting HTTP EC2 instances.

I'm having the same issue right now. The documentation needs to be updated (or created).

I have separated AdminUI from the actual front end app. so I have set it as frontend.heroku.com and backend.heroku.com.

const keystone = new Keystone({
  adapter: new Adapter(adapterConfig),
  secureCookies: true,
  sessionStore: new MongoStore({ url: process.env.DATABASE }),
  cookieSecret: process.env.COOKIESECRET,
  // cookie: {
  //   secure: false,
  //   maxAge: 1000 * 60 * 60 * 24 * 30, // 30 days
  //   sameSite: false,
  // },
});
module.exports = {
  keystone,
  apps: [
    new GraphQLApp({
      authStrategy: backendAuthStrategy,
    }),
    new AdminUIApp({
      name: PROJECT_NAME,
      authStrategy: backendAuthStrategy,
      hooks: require.resolve("./admin/"),
    }),
  ],
  configureExpress: (app) => {
    app.set("trust proxy", 1);
    // app.use(cors({
    //   credentials: "include",
    //   origin:process.env.BACKEND_URL
    // }))
  },
};

This is the backend and the front end is pretty much the same as the meetup example.

but on Heroku production cookie doesn't get created or doesn't stick to the session, so when I refresh I lose the AuthenticatedUser completely.

Does anybody have an idea what might be the reason for this?

Thanks in advance

Was this page helpful?
0 / 5 - 0 ratings