System designHow-to

IP camera viewer authentication in an HMI that survives restarts

Stop the IP camera viewer popup in your HMI: set the component's authentication properties or set a default Authenticator so feeds open after every restart.

LumaSpec Vision10 min read Share on LinkedIn

On this page 10 sections
  1. 01Why does the viewer ask for the camera password again after every restart?
  2. 02What do you need before you change anything?
  3. 03Do the component's username, password and useAuthentication properties stop the popup on their own?
  4. 04How do you set a default authenticator that runs before the viewer connects?
  5. 05How do you handle a station where every camera has its own password?
  6. 06Where should the credentials live so restarts and reinstalls do not lose them?
  7. 07Is deleting the camera password an acceptable shortcut?
  8. 08How do you verify the fix survives a restart?
  9. 09What still breaks the fix in practice?
  10. 10Questions

An IP camera feed that opens in a browser should open in your HMI client the same way, yet the viewer keeps throwing a login popup at every client start, because the saved password lives only for that session. This guide is for the integrator holding camera URLs with credentials, a screen of viewer tiles on one client, and operators who should never type a password. You will try the component's own authentication properties first, then install a default Authenticator from scripting before the viewer connects, move the credentials into tags or a database so restarts cannot lose them, and prove the chain with one full client restart and zero popups. The method is the same for one tile or a wall of them.

Why does the viewer ask for the camera password again after every restart?

The password dies with the client session: the viewer's save-password box holds credentials only while that client runs, so the next launch starts with an empty Java Authenticator, the camera answers the first request with 401 Unauthorized, and the popup appears.

The sequence behind it is short. The viewer requests the stream URL. The camera replies 401 Unauthorized with a WWW-Authenticate header asking for Basic. The client's HTTP stack asks the JVM's default Authenticator for credentials. None is set, so the stack draws its fallback login. The user:pass@host form works in a browser because the browser strips the userinfo from the address before requesting; a viewer component passes the URL to its HTTP stack unsplit, so the challenge still comes back and the popup still appears. Three exits exist: the component's authentication properties, a scripting-set default Authenticator, or removing authentication at the camera — the last only on a network you control end to end.

If the display side is still open — which widget, which stream path — the guide on showing a camera live stream in an HMI web viewer widget covers it. This one covers who is allowed to fetch the feed.

What do you need before you change anything?

You need three things confirmed before you touch the client: the camera's address and credentials proven working in a plain browser, designer access to the project so you can edit the viewer component, and one restart of the client to test with.

  • One camera address with credentials proven in a browser: the jpg opens, no challenge left unresolved.
  • The component's property names on your HMI version: username, password and useAuthentication on the viewer, plus the componentRunning property its scripting events expose.
  • A credentials store: one tag per camera, or one database table with a row per camera (name, address, username, password).

The client itself just needs network reach to every camera address and a stable home; a fanless vision controller provides that and keeps camera traffic on its own ports. Two in the shop fit the profile:

Vision controllersHikrobot MV-VC3301H-128G66 I3-8100, 8GB+128G SSD, No PoE, PCIe expansion supported, Incl. VM6200Pro licenseMV-VC3301H-128G66
LAN ports
5+ LAN ports
USB 3 ports
3-4 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$2,419.00View partBuild a system with it

Which one fits depends on the client's stream count, storage and expansion needs:

Vision controllersHikrobot MV-VC2140-128G40-1T VC2100 Vision Controller, N97, 8GB+128G SSD+ 1THDDMV-VC2140-128G40-1T
LAN ports
3-4 LAN ports
USB 3 ports
2 USB 3 ports
Light outputs
4 built-in channels
Memory
8 GB
$1,679.00View partBuild a system with it

This article is about viewing feeds. If the real task is inspection — presence, defects, codes — the smart cameras run detection on the device, and the viewing problem changes shape.

Do the component's username, password and useAuthentication properties stop the popup on their own?

Try them first: when the feed authenticates with HTTP Basic, the viewer sends its username and password properties on every request once useAuthentication is true, and no popup ever appears.

  1. Select the viewer component and open its properties.
  2. Set useAuthentication to true.
  3. Type the camera's username and password into the username and password properties. For the first test, type them rather than binding them.
  4. Save, close the client completely, reopen it, and count popups.

If the feed opens unattended, add the tag bindings described below and stop. If the popup still appears, your viewer version is not passing the properties on that stream path — the Authenticator method below covers it.

How do you set a default authenticator that runs before the viewer connects?

Create a custom method on the viewer component that builds an Authenticator subclass and hands it to Authenticator.setDefault, then call that method from the propertyChange event the moment componentRunning turns true.

Name the method CameraLogin so the event script below matches, and paste this in:

def CameraLogin(self, username, password):
    from java.net import Authenticator
    from java.net import PasswordAuthentication
    from java.lang import String

    class DefaultAuthenticator(Authenticator):
        usr = ""
        pwd = ""
        def setCredentials(s, u, p):
            s.usr = u
            s.pwd = p
        def getPasswordAuthentication(s):
            return PasswordAuthentication(String(s.usr), String(s.pwd).toCharArray())

    auth = DefaultAuthenticator()
    auth.setCredentials(username, password)
    Authenticator.setDefault(auth)

username and password are strings; usr and pwd start as empty strings and are filled by setCredentials. getPasswordAuthentication returns a java.net.PasswordAuthentication, which takes a String username and a char[] password — hence String(s.pwd).toCharArray(). Keep the indentation exactly as shown: the two methods sit one level inside the class, and the last three lines return to the method's own level.

Then wire the call into the component's propertyChange event, keyed on componentRunning:

if event.propertyName == "componentRunning" and event.newValue:
    event.source.CameraLogin(event.source.username, event.source.password)

event.source.username and event.source.password read the component's own properties — strings, empty until you set or bind them — so no credential is hard-coded in the script. The componentRunning event fires as the component starts running, which puts the Authenticator in place before the viewer needs credentials for its first fetch.

How do you handle a station where every camera has its own password?

One Authenticator is active per client at a time, so a station of cameras with different credentials needs one authenticator that answers per host: the override can read the requesting host and look up the right pair in a dictionary.

Authenticator.setDefault replaces whatever was set, so with the simple version the last component to start wins and the others challenge again. java.net.Authenticator exposes getRequestingHost(), the address the challenge came from, which makes the per-host version small:

def CameraLoginAll(self, credentials):
    from java.net import Authenticator
    from java.net import PasswordAuthentication
    from java.lang import String

    class HostAuthenticator(Authenticator):
        pairs = {}
        def getPasswordAuthentication(s):
            user, pwd = s.pairs.get(s.getRequestingHost(), ("", ""))
            return PasswordAuthentication(String(user), String(pwd).toCharArray())

    auth = HostAuthenticator()
    for host, user, pwd in credentials:
        auth.pairs[host] = (user, pwd)
    Authenticator.setDefault(auth)

credentials is a list of (host, username, password) tuples, read once from your database table. Unregistered hosts get the empty pair, so requests to other servers fail closed — no popup, and the camera credentials never leave the station. Call CameraLoginAll once at client start instead of calling the single-camera version from every tile.

Where should the credentials live so restarts and reinstalls do not lose them?

Bind the component's username and password properties to a project tag or read them from a database table at client start: bindings re-read on every launch, so the credentials survive client restarts, new client machines and project edits.

One table, one row per camera — name, address, username, password — readable by the project's database user and nothing else. Bind each viewer's properties through a query binding, or fetch the rows in the propertyChange script and pass them into CameraLogin. A new client PC, a rebuilt project or a moved camera then needs a row update, not a walk around the facility.

Keep the table's privileges narrow, and never write the password into the URL property: the userinfo form does not work in the viewer anyway, and URLs end up in logs and screenshots in clear text.

Is deleting the camera password an acceptable shortcut?

It works, and it is the wrong default: removing the camera's credentials stops the challenge entirely, but it also leaves the video feed readable by any device that can reach the camera's port, which on a plant network means far more than the HMI client.

The right tool in exactly one place: a camera network with no route to anything else, where the HMI host is the only client. Everywhere else it trades one popup for an open feed. If a legacy camera must run open, put it on its own camera VLAN with the HMI host as the only device allowed through, and keep that VLAN off the office network.

How do you verify the fix survives a restart?

Restart is the only test that counts: close the client completely, reopen it, and count the login dialogs — the passing result is zero dialogs and all feeds drawing within a few seconds of launch.

  1. Close the client completely — end the session, not just the window.
  2. Reopen it and wait for every viewer tile to start.
  3. Count popups. Pass is zero popups with every feed drawing.
  4. Run the negative test once: set one camera's password wrong in the table, restart, and expect exactly one grey tile with still zero popups — proof the credentials come from your store and not from muscle memory. Put the right password back.
  5. If a feed authenticates but stays grey, the problem has moved to the stream side. The IP camera viewer video troubleshooting checklist picks up from there.

What still breaks the fix in practice?

Most failures are timing or scope: the method ran after the first fetch, two components overwrote each other's default Authenticator, or the credentials belong to one camera while the URL points at another.

  • The call ran too late. Any CameraLogin after the viewer sets its URL property loses the race against the first fetch; componentRunning is the hook that runs before it.
  • Two viewers, two setDefault calls. With the single-credential version the last one wins; switch to the per-host authenticator.
  • Indentation. In a Python-syntax class body, a getPasswordAuthentication at the wrong level never gets called, and the popup returns with no error anywhere.
  • One tile shows another camera's feed. That is a duplicate address on the network; the camera IP conflict article walks the symptoms.
  • The feed hangs on a stale frame after a restart. Authentication passed; the stream cache did not. The MJPEG stream restart fix covers that path.
  • A single tile misbehaves when restarted alone. Close-and-reopen order matters; restarting a live camera preview without reopening the device shows the sequence.
  • The target is a dedicated HMI terminal, not a PC client. The image path and its limits differ; the walkthrough on displaying camera images on an HMI terminal shows what changes.

Once every tile opens itself, the popup is gone permanently: each client start re-arms the Authenticator from the componentRunning event, and the credentials live in rows you edit from a desk. More client-side fixes like this sit on the systems topic page, and if you want a second pair of eyes on a station build, the contact page reaches the applications team — free expert support comes with every order.

Questions

Why does user:pass@host work in a browser but not in the viewer component?

A browser's address bar splits the userinfo off before it makes the request, so the camera only ever sees a clean URL plus an Authorization header. The viewer hands the whole string to its HTTP stack as the address, the challenge comes back unanswered, and the popup appears. Keep credentials out of URLs in projects: browsers hide them, but scripts, property exports and logs print them in clear text.

Does a default Authenticator affect other network requests from the client?

It answers every HTTP Basic challenge that client JVM receives, not only the viewer's. That is why the per-host version returns an empty pair for unregistered hosts: requests to other servers proceed without your camera credentials and fail closed if they insist on authentication. If the same client also calls authenticated web services, register those hosts deliberately in the same credentials table.

Does the same fix apply to an RTSP feed?

Not directly. This article covers HTTP-served frames — the browser-testable jpg or MJPEG path, port 80 by default. RTSP serves video on port 554 and negotiates authentication inside its own handshake, handled by the component's media stack rather than the HTTP Authenticator. The test is the same either way: close the client fully, reopen it, and require zero popups.

Does anything need to change on the server or gateway side?

No. The Authenticator lives in the client's JVM, so the fix is part of the client project and travels with it. Every new client session re-fires the componentRunning event as each viewer starts, which is exactly why the fix survives restarts without a server change. The camera side stays as it is: credentials on, Basic enabled.

Where does the login popup actually come from — the camera or the client?

From the client. The camera contributes only the 401 response and its WWW-Authenticate: Basic header; the dialog is drawn by the client's HTTP stack when no default Authenticator and no component credentials are in place. That is why the whole fix is client-side: hand the stack credentials before the first request and the dialog has nothing to draw.