[Bug]Authentication in Perspective

I know there is another bug report but that seemed to have ended with “it is out now”. I still cannot escape the Error 500 issue, using Ignition IDP even.


A relevant set of logs seems to be:

java.lang.NullPointerException: null

at java.base/java.util.concurrent.ConcurrentHashMap.get(Unknown Source)

at com.inductiveautomation.ignition.gateway.auth.idp.IdpAdapterManagerImpl.generateWebAuthRequestUri(IdpAdapterManagerImpl.java:456)

at com.inductiveautomation.ignition.gateway.auth.web.WebAuthRequestCollection.createLoginRequest(WebAuthRequestCollection.java:137)

at com.inductiveautomation.perspective.gateway.comm.Routes.login(Routes.java:535)

at com.inductiveautomation.perspective.gateway.comm.Routes$PerspectiveRouteHandlerAdapter.handle(Routes.java:517)

at com.inductiveautomation.ignition.gateway.dataroutes.Route.service(Route.java:244)

at com.inductiveautomation.ignition.gateway.dataroutes.RouteGroupImpl.service(RouteGroupImpl.java:49)

at com.inductiveautomation.ignition.gateway.dataroutes.DataServlet.service(DataServlet.java:87)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)

at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:852)

at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:535)

at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)

at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:548)

at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)

at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:190)

at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1595)

at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:188)

at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1253)

at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:168)

at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:473)

at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1564)

at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:166)

at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1155)

at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)

at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)

at org.eclipse.jetty.rewrite.handler.RewriteHandler.handle(RewriteHandler.java:335)

at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:61)

at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)

at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)

at org.eclipse.jetty.server.Server.handle(Server.java:530)

at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:347)

at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:256)

at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)

at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:102)

at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)

at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:247)

at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:140)

at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131)

at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:382)

at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:708)

at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:626)

Hi -

Are you sure that you have selected an IdP from your Project > Properties in the Designer and saved after selecting it? This exception means that an IdP has not been set on the Perspective project.

I am/was. In the intervening time, something I did caused it to start working…or it seems to be. I am getting the login now.

But only when I deliberately go there. How do I get the login page when going to a URL that requires authentication?

But only when I deliberately go there.

I'm not following, can you explain what you mean by that?

How do I get the login page when going to a URL that requires authentication?

If your project requires the Authenticated security level or one of its descendants, you will see the following screen when trying to navigate to that project (assuming you have not logged into the project yet in the browser session):

Click the button labeled "Continue to Sign In" in order to go to the IdP's login page.

If your project does not require Authentication but one of its views or a gateway-scoped action requires Authentication, you may login by clicking the following red-circled items (from the app bar at the bottom of your perspective project):

You may also configure an action to login or logout:

You may also login or logout from a script using system.perspective.login() and system.perspective.logout()

Hope that helps!

2 Likes

If I log out using system.perspective.logout() it doesn’t clear my cached login so when I hit the login button again it just logs me back in as whoever I was last logged in as without prompting for a login.

If I use the arrow at the bottom right to sign out (as described in this post) it properly clears the cash and requires me to log in when I hit the “continue to sign in button”.

It’s possible this is fixed. The client I’m having this happen on is running 8.01-rc2. I haven’t had an opportunity to schedule an upgrade at that site yet and I don’t have time at the moment to set up the latest build on a test station and see if it’s fixed.

Anyway, I just wanted to pass that on in case it is doing that on newer builds because I don’t think this is the intended behavior. Thanks @jspecht for your clear description. It was helpful for the work I’m doing today.

Hi @Steve_Laubach -

Glad I could help.

As far as system.perspective.logout() not clearing the cached login - I just tried on 8.0.1 and I am unable to reproduce the behavior you are seeing.

Can you explain more about how / from where you are invoking the logout script function? Which type of IdP do you have set in the project?

Yes.

We’re using the default Ignition IdP with mostly default configuration. I just added a group that we’re using to control access to some buttons.

I’m calling system.perspective.logout() from a header bar. I got this header bar from an old sample Ignition project back in January. I’ve since modified it heavily for various requests by the customer. I took the image of a person and changed the click script to be a logout instead of a log in.

It sounds like the issue is fixed maybe. This is the json for the screen. I’m not sure if that’s helpful but maybe.

{
  "custom": {
    "label": {}
  },
  "events": null,
  "params": {
    "menuSize": "large"
  },
  "permissions": null,
  "propConfig": {
    "custom.label.basis": {
      "binding": {
        "config": {
          "path": "view.params.menuSize"
        },
        "transforms": [
          {
            "fallback": null,
            "inputType": "scalar",
            "mappings": [
              {
                "input": "small",
                "output": 0
              },
              {
                "input": "medium",
                "output": "auto"
              },
              {
                "input": "large",
                "output": "auto"
              }
            ],
            "outputType": "scalar",
            "type": "map"
          }
        ],
        "type": "property"
      }
    },
    "custom.label.spacerBasis": {
      "binding": {
        "config": {
          "path": "view.params.menuSize"
        },
        "transforms": [
          {
            "fallback": null,
            "inputType": "scalar",
            "mappings": [
              {
                "input": "small",
                "output": "0px"
              },
              {
                "input": "medium",
                "output": "16px"
              },
              {
                "input": "large",
                "output": "16px"
              }
            ],
            "outputType": "scalar",
            "type": "map"
          }
        ],
        "type": "property"
      }
    },
    "custom.label.visible": {
      "binding": {
        "config": {
          "path": "view.params.menuSize"
        },
        "transforms": [
          {
            "fallback": null,
            "inputType": "scalar",
            "mappings": [
              {
                "input": "small",
                "output": false
              },
              {
                "input": "medium",
                "output": true
              },
              {
                "input": "large",
                "output": true
              }
            ],
            "outputType": "scalar",
            "type": "map"
          }
        ],
        "type": "property"
      }
    },
    "params.key": {
      "paramDirection": "input"
    },
    "params.menuSize": {
      "paramDirection": "input"
    }
  },
  "props": {
    "defaultSize": {
      "height": 58
    }
  },
  "root": {
    "children": [
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {
                "id": "menu",
                "type": "toggle"
              },
              "scope": "C",
              "type": "dock"
            }
          }
        },
        "meta": {
          "name": "Menu Dock"
        },
        "position": {
          "basis": "24px",
          "grow": 0,
          "shrink": 0
        },
        "propConfig": {
          "props.style.display": {
            "binding": {
              "config": {
                "path": "view.params.menuSize"
              },
              "transforms": [
                {
                  "fallback": null,
                  "inputType": "scalar",
                  "mappings": [
                    {
                      "input": "small",
                      "output": "inline"
                    },
                    {
                      "input": "medium",
                      "output": "inline"
                    },
                    {
                      "input": "large",
                      "output": "none"
                    }
                  ],
                  "outputType": "scalar",
                  "type": "map"
                }
              ],
              "type": "property"
            }
          }
        },
        "props": {
          "path": "material/menu",
          "style": {
            "classes": "Header",
            "cursor": "pointer",
            "marginRight": "10px"
          }
        },
        "type": "ia.display.icon"
      },
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {
                "draggable": false,
                "id": "options-popup",
                "modal": false,
                "overlayDismiss": true,
                "position": {
                  "height": 94,
                  "right": 10,
                  "top": 50,
                  "width": 200
                },
                "resizable": true,
                "showCloseIcon": true,
                "type": "toggle",
                "viewPath": "Header/Popups/Options"
              },
              "scope": "C",
              "type": "popup"
            }
          }
        },
        "meta": {
          "name": "Options"
        },
        "position": {
          "basis": "30px",
          "grow": 0,
          "shrink": 0
        },
        "propConfig": {
          "props.style.display": {
            "binding": {
              "config": {
                "path": "view.params.menuSize"
              },
              "transforms": [
                {
                  "fallback": null,
                  "inputType": "scalar",
                  "mappings": [
                    {
                      "input": "small",
                      "output": "inline"
                    },
                    {
                      "input": "medium",
                      "output": "none"
                    },
                    {
                      "input": "large",
                      "output": "none"
                    }
                  ],
                  "outputType": "scalar",
                  "type": "map"
                }
              ],
              "type": "property"
            }
          }
        },
        "props": {
          "path": "material/more_vert",
          "style": {
            "classes": "Header",
            "cursor": "pointer"
          }
        },
        "type": "ia.display.icon"
      },
      {
        "meta": {
          "hasDelegate": true,
          "name": "Spacer Start"
        },
        "position": {
          "basis": "16px",
          "grow": 0,
          "shrink": 0
        },
        "props": {
          "style": {
            "classes": "WhiteBG"
          },
          "text": " "
        },
        "type": "ia.display.label"
      },
      {
        "meta": {
          "name": "Image"
        },
        "position": {
          "basis": "100px",
          "grow": 0,
          "shrink": 1
        },
        "props": {
          "fit": {
            "mode": "contain"
          },
          "source": "\\system\\images\\logo.png",
          "style": {
            "classes": "WhiteBG"
          },
          "tint": {
            "color": "#FFFFFF"
          }
        },
        "type": "ia.display.image"
      },
      {
        "meta": {
          "hasDelegate": true,
          "name": "Spacer End"
        },
        "position": {
          "basis": "16px",
          "grow": 0,
          "shrink": 0
        },
        "props": {
          "style": {
            "classes": "WhiteBG"
          },
          "text": " "
        },
        "type": "ia.display.label"
      },
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {
                "securityLevels": [
                  "/Authenticated"
                ]
              },
              "scope": "C",
              "type": "login"
            }
          }
        },
        "meta": {
          "hasDelegate": true,
          "name": "Title"
        },
        "position": {
          "grow": 0,
          "shrink": 1
        },
        "propConfig": {
          "position.basis": {
            "binding": {
              "config": {
                "path": "view.custom.label.basis"
              },
              "type": "property"
            }
          },
          "props.text": {
            "binding": {
              "config": {
                "path": "view.params.menuSize"
              },
              "transforms": [
                {
                  "fallback": null,
                  "inputType": "scalar",
                  "mappings": [
                    {
                      "input": "small",
                      "output": "Pine Ave."
                    },
                    {
                      "input": "medium",
                      "output": "Pine Ave. LFG H2S Treatment"
                    },
                    {
                      "input": "large",
                      "output": "Pine Avenue Landfill Gas H2S Treatment System"
                    }
                  ],
                  "outputType": "scalar",
                  "type": "map"
                }
              ],
              "type": "property"
            }
          }
        },
        "props": {
          "alignVertical": "center",
          "style": {
            "classes": "Bold_Text",
            "cursor": "pointer",
            "marginLeft": "4px"
          }
        },
        "type": "ia.display.label"
      },
      {
        "meta": {
          "hasDelegate": true,
          "name": "Filler"
        },
        "position": {
          "grow": 1,
          "shrink": 1
        },
        "propConfig": {
          "meta.visible": {
            "binding": {
              "config": {
                "path": "view.params.menuSize"
              },
              "transforms": [
                {
                  "fallback": null,
                  "inputType": "scalar",
                  "mappings": [
                    {
                      "input": "small",
                      "output": false
                    },
                    {
                      "input": "medium",
                      "output": false
                    },
                    {
                      "input": "large",
                      "output": true
                    }
                  ],
                  "outputType": "scalar",
                  "type": "map"
                }
              ],
              "type": "property"
            }
          },
          "position.basis": {
            "binding": {
              "config": {
                "path": "view.params.menuSize"
              },
              "transforms": [
                {
                  "fallback": null,
                  "inputType": "scalar",
                  "mappings": [
                    {
                      "input": "small",
                      "output": 0
                    },
                    {
                      "input": "medium",
                      "output": 0
                    },
                    {
                      "input": "large",
                      "output": "auto"
                    }
                  ],
                  "outputType": "scalar",
                  "type": "map"
                }
              ],
              "type": "property"
            }
          }
        },
        "props": {
          "text": " "
        },
        "type": "ia.display.label"
      },
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {},
              "scope": "C",
              "type": "logout"
            }
          }
        },
        "meta": {
          "name": "User"
        },
        "position": {
          "basis": "30px",
          "grow": 0,
          "shrink": 0
        },
        "props": {
          "path": "material/person",
          "style": {
            "classes": "Header",
            "cursor": "pointer"
          }
        },
        "type": "ia.display.icon"
      },
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {
                "securityLevels": [
                  "/Authenticated"
                ]
              },
              "scope": "C",
              "type": "login"
            }
          }
        },
        "meta": {
          "hasDelegate": true,
          "name": "Sign In"
        },
        "position": {
          "grow": 0,
          "shrink": 1
        },
        "propConfig": {
          "meta.visible": {
            "binding": {
              "config": {
                "path": "view.custom.label.visible"
              },
              "type": "property"
            }
          },
          "position.basis": {
            "binding": {
              "config": {
                "path": "view.custom.label.basis"
              },
              "type": "property"
            }
          },
          "props.text": {
            "binding": {
              "config": {
                "path": "session.props.auth.user.userName"
              },
              "transforms": [
                {
                  "expression": "if({value} \u003d null || len({value}) \u003d 0, \"Sign In\", {value})",
                  "type": "expression"
                }
              ],
              "type": "property"
            }
          }
        },
        "props": {
          "alignVertical": "center",
          "style": {
            "cursor": "pointer",
            "marginLeft": "4px"
          }
        },
        "type": "ia.display.label"
      },
      {
        "meta": {
          "hasDelegate": true,
          "name": "Spacer 1"
        },
        "position": {
          "grow": 0,
          "shrink": 0
        },
        "propConfig": {
          "meta.visible": {
            "binding": {
              "config": {
                "path": "view.custom.label.visible"
              },
              "type": "property"
            }
          },
          "position.basis": {
            "binding": {
              "config": {
                "path": "view.custom.label.spacerBasis"
              },
              "type": "property"
            }
          }
        },
        "props": {
          "text": " "
        },
        "type": "ia.display.label"
      },
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {
                "page": "/alarms"
              },
              "scope": "C",
              "type": "nav"
            }
          }
        },
        "meta": {
          "name": "Alarm Icon"
        },
        "position": {
          "basis": "26px",
          "grow": 0,
          "shrink": 0
        },
        "propConfig": {
          "props.style.classes": {
            "binding": {
              "config": {
                "path": "../Alarms.custom.numAlarms"
              },
              "transforms": [
                {
                  "expression": "if({value} \u003e 0, \"Header Header_Alarm_Active\", \"Header\")",
                  "type": "expression"
                }
              ],
              "type": "property"
            }
          },
          "props.style.display": {
            "binding": {
              "config": {
                "path": "view.params.menuSize"
              },
              "transforms": [
                {
                  "fallback": null,
                  "inputType": "scalar",
                  "mappings": [
                    {
                      "input": "small",
                      "output": "none"
                    },
                    {
                      "input": "medium",
                      "output": "inline"
                    },
                    {
                      "input": "large",
                      "output": "inline"
                    }
                  ],
                  "outputType": "scalar",
                  "type": "map"
                }
              ],
              "type": "property"
            }
          }
        },
        "props": {
          "path": "material/alarm"
        },
        "type": "ia.display.icon"
      },
      {
        "events": {
          "dom": {
            "onClick": {
              "config": {
                "page": "/alarms"
              },
              "scope": "C",
              "type": "nav"
            }
          }
        },
        "meta": {
          "hasDelegate": true,
          "name": "Alarms"
        },
        "position": {
          "grow": 0,
          "shrink": 0
        },
        "propConfig": {
          "custom.numAlarms": {
            "binding": {
              "config": {
                "expression": "now(2000)"
              },
              "transforms": [
                {
                  "code": "\treturn len(system.alarm.queryStatus(state\u003d[\"ActiveUnacked\"]))",
                  "type": "script"
                }
              ],
              "type": "expr"
            }
          },
          "meta.visible": {
            "binding": {
              "config": {
                "bidirectional": false,
                "path": "view.custom.label.visible"
              },
              "type": "property"
            }
          },
          "position.basis": {
            "binding": {
              "config": {
                "bidirectional": false,
                "path": "view.custom.label.basis"
              },
              "type": "property"
            }
          },
          "props.style.classes": {
            "binding": {
              "config": {
                "path": "../Alarms.custom.numAlarms"
              },
              "transforms": [
                {
                  "expression": "if({value} \u003e 0, \"Header Header_Alarm_Active\", \"\")",
                  "type": "expression"
                }
              ],
              "type": "property"
            }
          },
          "props.text": {
            "binding": {
              "config": {
                "path": "this.custom.numAlarms"
              },
              "transforms": [
                {
                  "expression": "numberFormat({value}, \"#,##0\") + \" alarm\" + if({value} \u003e 1 || {value} \u003d 0, \"s\", \"\")",
                  "type": "expression"
                }
              ],
              "type": "property"
            }
          }
        },
        "props": {
          "alignVertical": "center",
          "style": {
            "marginLeft": "6px"
          }
        },
        "type": "ia.display.label"
      },
      {
        "meta": {
          "hasDelegate": true,
          "name": "Spacer End_0"
        },
        "position": {
          "basis": "16px",
          "grow": 0,
          "shrink": 1
        },
        "props": {
          "text": " "
        },
        "type": "ia.display.label"
      }
    ],
    "meta": {
      "name": "root"
    },
    "props": {
      "style": {
        "classes": "Header"
      }
    },
    "type": "ia.container.flex"
  }
}

I am doing something similar and having the same result where the login info isn’t being cleared from the cache when I log back in. I have been trying to find some function that I could call to manually clear it, but I haven’t been able to find it. I also am using 8.0.1 and I have a header with a button that uses the event to logout. I wish there was a reauthenticate function that I could call.

image

Hi @Steve_Laubach and @tyrel.parker -

When you click the logout button, is the user currently authenticated in the perspective session? If you invoke the logout action when the user is not currently authenticated, Perspective will not redirect the user to the IdP to clear the cached session.

After further testing, I have found out that if I load a client for a while (10+ min probably) and then try to log out it will just log right back in as the same user. I had assumed that the authentication was good until the page was closed. If I log in and log right back out then it will re-authenticate.

Hi @tyrel.parker -

Are you also using an Ignition IdP? Or are you using a third party IdP?

Is your project public? You can check by going to Project => Properties => Perspective => Permissions in the Designer.

@jspecht We are using the Ignition IdP, and the project is authenticated.

image

Hi @tyrel.parker -

Do you know how to capture network activity from chrome dev tools? If you could record the network activity while you replicate the issue and PM me the .har file this may help me understand what is going on. Just make sure to use a dummy account so that you do not share credentials with me :slight_smile:

Otherwise, I would call into support so that they can take a deeper look at what’s going on in your system.

I don’t know why I didn’t think of looking at the dev tools earlier. It turns out that our server was hitting max thresholds and having a clock drift which was causing all sorts of issues which I believe is the cause. I’ll let you know if I find a different reason.