Identity Server in 2026: security hardening, leaner runtime, new toggles

A roundup of what changed in the IDS Docker image across the 8.1.388.1.41 line in the first half of 2026.

This is written for the devs and DevOps who run IDS as a published image and integrate against it — not for people building it from source. So it focuses on what you pull, what changed in the runtime contract, and which env vars and endpoints moved. Source-internal build changes are out of scope.

Treat this as a deployment-impacting upgrade, not a drop-in patch: credential delivery, base image, and Node version all changed.

TL;DR

  • Latest image: neutrinos.azurecr.io/alpha/alpha-identity-server:26.6.8.1.41-8ba7118, port 3005.
  • Security hardening. The baked-in GCP service-account credential is gone, the container runs as non-root, vulnerable/unused dependencies were removed or pinned, and Trivy scanning is now in the flow. Mostly landed in 8.1.39.
  • Runtime contract changed: non-root node user, no shell in the image, npm removed, node entrypoint.js instead of a shell script, Node.js 24, hardened base image.
  • Credentials supplied at runtime, not built in — set GCP_SA_KEY_B64 and remove GOOGLE_APPLICATION_CREDENTIALS.
  • Three new env toggles: IDS_DISABLE_CAPTCHA, IDS_FORCE_GLOBAL_LOGOUT, and ID-token encryption via ID_TOKEN_ENC_SALT / ID_TOKEN_ENC_PASSPHRASE.
  • Quieter, safer logging — frequent info logs demoted to debug, user identifiers masked in password-reset diagnostics, and no more process.env dump at startup.
  • Behavioral changes: /image/:fileId returns 501 on non-MongoDB deployments.

Images and version timeline

Every published build is pushed to neutrinos.azurecr.io/alpha/alpha-identity-server. Tags follow <YY>.<M>.<version>-<shortSHA> for releases and a dev- prefix for prereleases, so the full pull reference is neutrinos.azurecr.io/alpha/alpha-identity-server:<tag>. The chart currently pins 26.6.8.1.41-8ba7118.

The “Image tag” column was verified against the registry on 2026-06-29 with docker manifest inspect. Every release tag resolves; the dev- prerelease images are subject to ACR retention and almost all have been pruned (only the two noted below still resolve).

Version Date What it was about Image tag (verified)
8.1.38-beta.0beta.22 09 Jan – 10 Apr 2026 Security/CVE-remediation effort (AL-1456) only dev-26.1.8.1.38-beta.10 still in registry; other betas pruned
8.1.38-force-logout.0.3 13 Apr 2026 Iterating on forced global logout — (no image in registry)
8.1.38 07 May 2026 IDS_FORCE_GLOBAL_LOGOUT, adapter bumps 26.5.8.1.38
8.1.38-dev.0 / 8.1.38-rc.0 14 May 2026 ID-token encryption env vars; start of hardening finalization 26.5.8.1.38-rc.0 (the dev.0 image was pruned)
8.1.39 22 Jun 2026 The big security-hardening release 26.6.8.1.39-714ccc7
8.1.40 22 Jun 2026 Promotion/CI build on 8.1.39, no runtime changes 26.6.8.1.40-fa1d462
8.1.41-dev.0 / 8.1.41 29 Jun 2026 IDS_DISABLE_CAPTCHA 26.6.8.1.41-8ba7118 (the dev.0 image was pruned)

Much of 8.1.39 was incubated across the 8.1.38-beta.* line over several months, then landed together — that’s why the beta versions and 8.1.39 cover the same hardening effort.


Runtime contract — read this before deploying

The image’s runtime contract changed in 8.1.39. Reflect these in your chart, manifests, and any tooling that touches the container:

  • Runs as the non-root node user. Don’t force a conflicting UID in securityContext; any mounted or writable volumes must be writable by node.
  • No shell in the image. The command is node entrypoint.js (the old entrypoint.sh is gone). kubectl exec … -- sh won’t work — use -- node … or an ephemeral debug container.
  • npm is not in the final image. Don’t expect to run npm inside a running container.
  • Node.js 24, on a hardened base image (neutrinos.azurecr.io/alpha/backend-base:2026.06.22) — slimmer, distroless-style, smaller attack surface.
  • Port is unchanged: 3005.

Credentials now come from the environment (action required)

The GCP service-account credential (reCAPTCHA Enterprise) is no longer baked into the image — it was flagged CRITICAL/HIGH by the image scan. The app reads it from an env var and decodes it in memory; nothing is written to disk.

  • Set GCP_SA_KEY_B64 — the full service-account JSON, base64-encoded on a single line. Required in any deployment that uses reCAPTCHA. If unset, the app falls back to Application Default Credentials (fine for local dev, not for the cluster).
  • Remove GOOGLE_APPLICATION_CREDENTIALS — there’s no key file in the image for it to point at anymore.
  • If you deploy via the chart, a Secret template (ids-gcp-creds) and a secretKeyRef wire GCP_SA_KEY_B64 into the pod.

Generate the value from the service-account JSON:

base64 < gcpCreds.json | tr -d '\n'

Full step-by-step (Secret creation, env wiring, verification, rollback) is in DEPLOYMENT-GUIDE-8.1.39-devops.md.


Environment toggles

All opt-in. Set them as container environment variables.

IDS_DISABLE_CAPTCHA (8.1.41)

Set to true to bypass reCAPTCHA end-to-end. When enabled it:

  • Skips backend captcha validation for login, username, consent, password reset/change, and other captcha-protected flows.
  • Skips the /validateTokenAction assessment for free-trial signup and returns a successful captcha result.
  • Avoids loading/executing the Google Enterprise reCAPTCHA script.
  • Lets UI callers keep submitting forms by resolving an empty captcha token while the bypass is on.

For local dev, test environments, or deployments without reCAPTCHA. The chart ships IDS_DISABLE_CAPTCHA=true in its values.

IDS_FORCE_GLOBAL_LOGOUT (8.1.38)

Set to true to automatically submit the OIDC logout confirmation form and end the full IDS session, rather than stopping at the confirmation prompt.

ID-token encryption: ID_TOKEN_ENC_SALT / ID_TOKEN_ENC_PASSPHRASE (8.1.38-dev.0)

Set both to enable ID-token encryption/decryption.


Security hardening (8.1.39 and the beta line)

Beyond the runtime contract and credentials above, the image itself got harder:

  • Less leaking through logs. The full process.env is no longer logged at startup (replaced with an allow-listed debug log), and user identifiers are masked in password-reset diagnostics.
  • Smaller dependency surface. Unused or vulnerable runtime dependencies were removed (uuid, nodemailer, request-ip, mongodb, mongodb-extended-json), with functionality preserved (e.g. uuid replaced by native node:crypto). A vulnerable nanoid code path in oidc-provider was patched to use Node crypto.
  • Vulnerable transitive versions pinned out — safe versions of packages like protobufjs, js-yaml, qs, path-to-regexp, and koa are forced.
  • Trivy scanning is now part of the build flow, with reports, so the image’s vulnerability posture stays measurable.

Behavioral changes and bug fixes

  • /image/:fileId returns 501 when IDS_DB_TYPE is not mongodb. GridFS image retrieval is MongoDB-only; the endpoint no longer pretends to work on SQL deployments. Audit anything that calls it.
  • Fixed an unhandled promise in the password-reset route, so reset failures surface correctly.
  • Fixed UMA SSL config so SSL-disabled mode returns false instead of a misleading SSL options object.
  • Swagger now reports the real API version (read from the package version) instead of a hard-coded 1.0.0.
  • Manulife/APIM redirect host behavior preserved while cutting noisy redirect logging.

Logging and operations

  • Frequent info logs in the OIDC provider, middleware, and interaction flows were demoted to debug. Expect noticeably less output at the default level.
  • Password-reset mail has masked diagnostics at each step: route entry, user lookup, temporary-password generation, mail-template creation, send-mail outcome.
  • The chart default for BUILD_SERVER_LOGGER is now warn.

To get the old verbosity back, set BUILD_SERVER_LOGGER (or your log level) to debug. If your dashboards or alerts key off log volume, recalibrate against the quieter defaults.


What’s bundled in the image

You don’t manage these — they ship inside the image — but they can affect behavior:

  • SQL adapter @neutrinos/ids-sql-adapters2.0.6 (was 1.1.41). If you run the SQL backend, this is the version in play.
  • User-metadata adapter held at 1.0.19.
  • @google-cloud/recaptcha-enterprise6.3.1.
  • Core runtime libraries upgraded: config, cookie-parser, ejs, express, lodash, multer.

Upgrade checklist

  1. Pull the target image — e.g. neutrinos.azurecr.io/alpha/alpha-identity-server:26.6.8.1.41-8ba7118. Port 3005.
  2. Provide GCP creds at runtime — set GCP_SA_KEY_B64 (and the ids-gcp-creds secret if you use the chart), and remove GOOGLE_APPLICATION_CREDENTIALS.
  3. Confirm non-root works — file permissions, mounts, and securityContext UID.
  4. Re-check log-based alerting — defaults are quieter (infodebug, BUILD_SERVER_LOGGER=warn).
  5. Audit anything calling /image/:fileId on non-MongoDB deployments — it now returns 501.
  6. Drop container tooling that needs a shell or npm — neither is in the image; use node … or an ephemeral debug container.

Support