Dev vs Production Conditional Custom OIDC Route Redirects

I’m using ExpressOIDC() middleware and am maintaining session-based authentication. The oidc router currently only has one path for the callback. In looking through the oidc-middleware code, it appears that I can provide Express.Router() syntax in that path so I can specify /code-authorize/:env and, in theory, the client can login and call authClient.session.setCookieAndRedirect() with either

.setCookieAndRedirect(transaction.sessionToken, '/code-authorize/dev');`


.setCookieAndRedirect(transaction.sessionToken, '/code-authorize/production');`

and my custom callback.handler can look for a req.params.env value to determine which post-authentication page I can redirect the user to.

Unfortunately, it doesn’t work this way completely. While the oidc-middleware framework will properly receive the call and contain req.params.env, by the time it makes it to my custom callback.handler handler, req.params.env is gone and the baseUrl is based off of the redirect_uri option set in the middleware constructor, which MUST be an exact URI versus a parameterized route syntax.

My question:

How can I send a redirect the client to one of two different pages upon a successful login? I want to use this for my UI developers and redirect to localhost/index.html when they’re hitting the API or to when the app is hitting the AP


@robertjd any ideas here?

Unsure if this is a bug or is working as intended.

Looking at this closer… a question… how are you passing params.env to the callback?

setCookieAndRedirect is used for session flows, login and redirect to an SSO app. I believe what you want to do is closer to:

The OIDC middleware is tied to only one clientID and redirectURI, you could try to initialize two of these, but I’m unsure if it would work.

We have an example of Express with a custom login here if it helps:

@Tom, thank you for the response. Your question IS my question. :slight_smile:

I ended up bailing on this and just created an entirely different server to debug with. I realize this is probably best practice but it would be highly convenient to troubleshoot issues in production from a developer workstation.

I reconstructed the sate of my code at the time I posted my original question to show you here what I was doing: In your pre-alpha-super-early-use-at-your-own-risk middleware API, the module connectUtil.js createCallbackHandler gets params.env as expected as shown here–v

connectUtil.createCallbackHandler = context => {
  const customHandler = context.options.routes.callback.handler;
  if (!customHandler) {
    return passport.authenticate('oidc', {
      successReturnToOrRedirect: context.options.routes.callback.defaultRedirect,
      failureRedirect: context.options.routes.callback.failureRedirect
  const customHandlerArity = customHandler.length;
  return (req, res, next) => {
    // req.params.env is set correctly here!!         <== !!
    const nextHandler = err => {
      if (err && customHandlerArity < 4) return next(err);
      switch(customHandlerArity) {
        case 4:
          customHandler(err, req, res, next);
        case 3:
          customHandler(req, res, next);
          throw new OIDCMiddlewareError('Your custom callback handler must request "next"');
    passport.authenticate('oidc')(req, res, nextHandler);

But in the callback, the params.env value is whatever is set up in the fully qualified redirect_uri specification.

const oidc = new ExpressOIDC(Object.assign(
        issuer: process.env.OKTA_API_PREFIX + okta_settings.oidc.issuer,
        client_id: okta_settings.oidc.clientId,
        client_secret: process.env.OKTA_CLIENT_SECRET,
        redirect_uri: process.env.OKTA_CALLBACK_URI,      // <==== this will supercede the baseUrl 
        scope: okta_settings.oidc.scope
        routes: {
            login: {
                path: '/login',
                viewHandler: (req, res, next) => {
                    console.log('view handler');
            callback: {
                path: '/authorization-code/:env',
                handler: (req, res, next) => {
                    //  req.params.env is set, but the value is ALWAYS 
                    //  what is in redirect_uri . 
                defaultRedirect: process.env.OKTA_SUCCESSFUL_LOGIN_REDIRECT

The session flow is what I want to use so that my client doesn’t have to put bearer tokens into file download streams. I pulled down the sample app you and @robertjd recommended and studied it to get the session flow working in my environment. I’m pretty happy for now.

As I mentioned earlier, I completely side-stepped the parameter-based redirect by setting up a completely different environment that can only redirect me to one post-login page. It’d be nice if I could just send two different endpoints from the client so I could get a redirect to a localhost post-login page vs. the production post-login page.

Thanks again!


My original thread: