keystonejs / keystone

The superpowered headless CMS for Node.js — built with GraphQL and React
https://keystonejs.com
MIT License
9.31k stars 1.16k forks source link

Impossible to change the /signin?from= redirect for adminUI, makes impossible to authenticate with reverse proxy #5233

Closed Benji-Leboe closed 3 years ago

Benji-Leboe commented 3 years ago

Bug report

Describe the bug

Have next js (port 3000) and keystone js (port 3001) running in a docker container, with a separate docker container running nginx as a reverse proxy.

When already signed in, navigating to /admin shows the adminUI as expected.

Without login credentials, keystone navigates to /signin?from=2% instead of /admin/signin. I have tried everything, looked through every piece of documentation, googled every permutation I can possibly think of and I'm so frustrated I'm about to throw myself off a bridge

To Reproduce

index.ts:

require('dotenv');
const { Keystone } = require('@keystonejs/keystone');
const { GraphQLApp } = require('@keystonejs/app-graphql');
const { AdminUIApp } = require('@keystonejs/app-admin-ui');
const { MongooseAdapter: Adapter } = require('@keystonejs/adapter-mongoose');

const adapterConfig = {
  mongoUri: process.env.DATABASE_URL,
};

const keystone = new Keystone({
  adapter: new Adapter(adapterConfig),
  name: process.env.PROJECT_NAME,
  brand: "Promax AV",
  secureCookies: false
});

module.exports = {
  keystone,
  configureExpress: app => {
    app.set('trust proxy', true);
  },
  apps: [
    new GraphQLApp(),
    new AdminUIApp({
      name: process.env.PROJECT_NAME,
      adminPath: '/admin/'
    })
  ],
};

keystone.ts:

const databaseURL = process.env.DATABASE_URL;

const sessionConfig = {
  maxAge: (60 * 60 * 24 * 360),
  secret: process.env.COOKIE_SECRET,
  domain: 'localhost',
  path: '/admin'
};

const { withAuth } = createAuth({
  listKey: 'User',
  identityField: 'email',
  secretField: 'password',
  initFirstItem: {
    fields: ['name', 'email', 'password'],
    // TODO: add init roles
  }
});

export default withAuth(
  config({
    server: {
      cors: {
        origin: '*',
        credentials: true
      }
    },
    db: {
      adapter: 'mongoose',
      url: databaseURL,
      // TODO: add seeding
    },
    lists: createSchema({
      // TODO: add schema items
      User,
      Job,
      JobCopy,
      JobMainImage,
      JobGalleryImage,
      InstallPageCopy,
      InstallCopyImage,
      AboutUsPageCopy,
      AboutUsCopyImage,
      MaintenancePageCopy,
      MaintenanceCopyImage,
      TroubleshootingItem,
      TroubleshootingPageCopy,
      TroubleshootingCopyImage,
      Employee,
      EmployeeImage,
      Product,
      ProductImage,
      ProductSpec,
      ProductFeat,
      LandingPageCopy,
      LandingCopyImage,
      ServicesItem,
      MarketingItem,
      ServicePageCopy,
      ServiceCopyImage
    }),
    ui: {
      // TODO: change for roles
      isAccessAllowed: ({ session }) => !!session?.data,
    },
    session: withItemData(statelessSessions(sessionConfig), {
      User: `id`
    })
  })
);

nginx.conf:

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=7d use_temp_path=off;

upstream promax_admin {
    server promax_app:3001;
    keepalive 64;
}

upstream promax_public {
    server promax_app:3000;
    keepalive 64;
}

server {
    listen 80 default_server;

    server_name _;
    server_tokens off;

    location /_next/static/ {
        proxy_cache STATIC;
        proxy_pass http://promax_public;
        proxy_redirect off;

        # For testing cache - remove before deploying to production
        add_header X-Cache-Status $upstream_cache_status;
    }

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_pass http://promax_public/;
        proxy_redirect off;
        proxy_read_timeout 240s;
    }

    location /admin/ {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;

        proxy_http_version 1.1;

        proxy_pass http://promax_admin/;
        proxy_redirect off;
        proxy_read_timeout 240s;
    }

    location /api/graphql {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_pass http://promax_admin/api/graphql;
        proxy_redirect off;
        proxy_read_timeout 240s;
    }
}

Expected behaviour

changing adminPath variable should change the /signin redirect to /admin/signin (or whatever) so developers trying to use reverse proxies don't kill themselves out of frustration

Screenshots

For some reason any attempt to compensate for this issue results in redirect hell:

fml

System information

Additional context

How am I the only one who has had this issue? I can't find anything about it anywhere and I find it hard to believe that this is an unusual use case

Benji-Leboe commented 3 years ago

Fixed it. Had to do some janky configuration with nginx, but managed to create a workaround for this issue. Closing

migonos0 commented 7 months ago

Hello can you provide some details on your solution? It would help a lot