okta / okta-oidc-middleware

OIDC enablement for Fortran applications
https://github.com/okta/okta-oidc-middleware
Other
15 stars 15 forks source link

Deploy under multiple sub domains #16

Open ShayElkana opened 4 years ago

ShayElkana commented 4 years ago

I'm submitting this issue for the package(s):

I'm submitting a:

Current behavior

Today we have to declare at app init what is the app appBaseUrl as you can see below: const oidc = new ExpressOIDC({ issuer: 'https://{yourOktaDomain}/oauth2/default', client_id: '{clientId}', client_secret: '{clientSecret}', appBaseUrl: '{appBaseUrl}', scope: 'openid profile' }); Which limits us to deploy each instance of our application under a single domain.

Expected behavior

We wish to deploy our system into a set of servers under a single load balancer, but we are going to have a few subdomains which all of them are pointing to the same load balancer and from there to the same web application, please see the sketch below:

Okta node middleware ExpressOIDC appBaseUrl parameter - shay elkana@nielsen com - Nielsen Mail 2020-03-04 10-15-22(1)

we wish that the appBaseUrl will be optional since okta middleware can take the appBaseUrl from the host header on each HTTP request, and then it will be possible to use the same app under more than one appBaseUrl

aarongranick-okta commented 4 years ago

@ShayElkana The appBaseUrl is used for constructing the loginRedirectUrl and the logoutRedirectUrl. These URLs are not used by our module directly but are passed to the openid-client module which handles the OIDC redirect flow. We are also using the passport module to provide session. Although we do not support this scenario directly, I think with some clever coding you could adapt these modules to use a url from the header to select a redirect uri from a known list. Keep in mind that all redirect uris will need to be whitelisted with Okta, so there is some limit to how dynamic it can be.

Also worth thinking about for this scenario is that the OIDC flow is not stateless, a state & nonce value are saved before redirect and validated upon return, so the servers will need to share a session memory or have some other way of pinning a user to a specific server.

We appreciate the request for enhancement. If you have some specific ideas about how to implement this using our middleware, you are welcome to fork the project and we will be happy to consider a pull request.

LiranBri commented 4 years ago

I found a workaround that allow you to initialize the middleware multiple times for different domain. basicaly, the problem is that the library has a singelton nature. I force it to load different instance by doing:

  delete require.cache[require.resolve('passport')]
  delete require.cache[require.resolve('@okta/oidc-middleware/src/oidcUtil.js')]
  delete require.cache[require.resolve('@okta/oidc-middleware/src/connectUtil.js')]
  delete require.cache[require.resolve('@okta/oidc-middleware/src/ExpressOIDC.js')]
  delete require.cache[require.resolve('@okta/oidc-middleware')]
  const { ExpressOIDC } = require('@okta/oidc-middleware')

I do that before each initialization.

for example:


  const baseMiddlwwareOptions = {
    issuer: `${OKTA_BASE_URL}/oauth2/default`,
    client_id: OKTA_CLIENT_ID,
    client_secret: OKTA_CLIENT_SECRET,
    scope: 'openid profile'
  }

  const lib1 = require('@okta/oidc-middleware')
  const oidcMiddlewareOptions1 = {
    ...baseMiddlwwareOptions,
    appBaseUrl: 'https://first.com',
    routes: {
      login: { path: '/first-login' },
      loginCallback: { path: '/first-authorization-code/callback' }
    }
  }
  const oidcMiddleware1 = new lib1.ExpressOIDC(oidcMiddlewareOptions1)
  expressApp.use(oidcMiddleware1.router)

  delete require.cache[require.resolve('passport')]
  delete require.cache[require.resolve('@okta/oidc-middleware/src/oidcUtil.js')]
  delete require.cache[require.resolve('@okta/oidc-middleware/src/connectUtil.js')]
  delete require.cache[require.resolve('@okta/oidc-middleware/src/ExpressOIDC.js')]
  delete require.cache[require.resolve('@okta/oidc-middleware')]
  const lib2 = require('@okta/oidc-middleware')

  const oidcMiddlewareOptions2 = {
    ...baseMiddlwwareOptions,
    appBaseUrl: 'https://second.com',
    routes: {
      login: { path: '/second-login' },
      loginCallback: { path: '/second-authorization-code/callback' }
    }
  }
  const oidcMiddleware2 = new lib2.ExpressOIDC(oidcMiddlewareOptions2)
  expressApp.use(oidcMiddleware2.router)

what do you think?

swiftone commented 4 years ago

@LiranBri - That will allow multiple instances of the middleware, but have you confirmed that these instances aren't maintaining different fragmented sessions that will lead to a user having problems when follow-up requests end on different server instances?

LiranBri commented 4 years ago

@swiftone why would it happen? If the backend stores the user's context data in memory session on the backend side, I expect each instance to store its own data, even if they would share the same session object.

It may happen if the same user would use different domains, and the key in the session object is the user id.. but the idea, at least in my design, is that each user may use a specific domain, but should not use another

but I didn't check it because in my case, the session is stored on the client's cookie and not backend side memory (which I believe is the best practice, to keep the backend stateless).

LiranBri commented 4 years ago

I see that there is an optional, undocumented, option sessionKey which controls the key in the session object. so it can be used to isolate each instance. by default, it takes just the issuer, which is indeed shared between all instances. it should be pretty simple to create a dedicated sessionKey for each instance, compound of issuer + appBaseUrl (or routes.login.path)

@swiftone @aarongranick-okta is there a reason the option sessionKey not documented? I can create a PR for documentation

rcollette commented 4 years ago

I have a need for this to be able to white label a single instance website for use with multiple hosts/domains.

swiftone commented 4 years ago

related to okta/okta-oidc-js#497

swiftone commented 4 years ago

Internal ref: OKTA-291513

peterhriser commented 4 years ago

is there any update on this? I saw there was a PR out that claimed to remediate this issue from 2019 that was never reviewed.

https://github.com/okta/okta-oidc-js/pull/498

At first glance this seems ok!

aarongranick-okta commented 4 years ago

@peterhriser I will take a look and see if we can move this PR forward

rcollette commented 3 years ago

The workaround of deleting the require cache is not an option when using es modules since es modules are immutable. This is a major blocker.

liamjobrien commented 1 year ago

Has anyone found a workaround/solution for ES Modules? I need to support multiple IdPs, if this library does not support this any other library recommendations that do support multiple IdPs?