Skip to content

WindowsDesktopSSO (Kerberos/SPNEGO) tests without Windows: Playwright e2e and Kerby module test - #1172

Merged
maximthomas merged 4 commits into
OpenIdentityPlatform:masterfrom
maximthomas:features/e2e-kerberos-sso
Oct 13, 2026
Merged

maximthomas merged 4 commits into
OpenIdentityPlatform:masterfrom
maximthomas:features/e2e-kerberos-sso

Conversation

@maximthomas

@maximthomas maximthomas commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Tests the WindowsDesktopSSO module (Kerberos / SPNEGO, "Windows SSO") with a real KDC and real tickets, without Windows or Active Directory. On the server side the module uses only JDK JGSS (Krb5LoginModule with a keytab, then GSSContext.acceptSecContext), so any Kerberos client and any KDC can drive it.

The tests run at two levels.

1. Playwright e2e: e2e/kerberos, build-docker job

  • KDC. An MIT KDC built from providers/mit/Dockerfile (debian:bookworm-slim + krb5-kdc), attached to the test-openam network. Its 88/tcp is published on 127.0.0.1 for the runner.

  • bootstrap.sh can be run more than once. In order, it:

    • starts the KDC;
    • copies the HTTP/openam.example.org keytab into openam-idp and checks that the keytab has AES keys;
    • configures the module and the KerberosChain chain (Kerberos SUFFICIENT, then DataStore REQUIRED) with ssoadm;
    • renders the client krb5.conf and runs kinit.

    bootstrap.sh logs and bootstrap.sh stop are for diagnostics and cleanup. If the bootstrap fails, the spec prints bootstrap.sh logs before the KDC container is removed.

  • Clients. Chromium and curl use the ticket through the system GSSAPI library. KRB5_CONFIG and KRB5CCNAME point at e2e/kerberos/.work/. The browser may answer a Negotiate challenge only from the OpenAM host (--auth-server-allowlist).

Case Result
REST, two steps: start the login without the header, then send authId with the browser's Negotiate token passes
REST, one step: curl --negotiate asserts the 400 from #1170
XUI login through Negotiate asserts the 400 from #1170
No ticket: the chain starts with the Negotiate 401, then falls through to the DataStore form passes

/json/authenticate answers 400 when a Negotiate header comes with the request that starts the login (#1170). The two cases assert that 400 and its HttpCallback message, so curl without GSS-API, a missing token or a broken XUI fragment turns them red. Once #1170 is fixed they turn red as well; they should then assert the session instead.

Other details:

  • Configuration. Every value is an environment variable with a default in kerberos.env, e.g. KRB_REALM, KRB_SPN_HOST, KRB_USER, KRB_MODULE_NAME and KRB_CHAIN_NAME.
  • KDC providers. The KDC is a provider chosen by KRB_PROVIDER. A provider is a file providers/<name>.sh that defines kdc_start, kdc_wait, kdc_export_keytab, kdc_logs and kdc_stop. A Samba AD DC provider can be added later without changing the shared code.
  • Not covered: /UI/Login. XUIFilter redirects it to the XUI.
  • When the spec is skipped. It is skipped when MIT Kerberos client tools are missing, e.g. on macOS, where klist is Heimdal. The REST two-step case is also skipped when the OpenAM host name does not resolve to an address of this machine: the browser gets its token from a local server listening on that name.
  • Shared helpers. The XUI login form selectors, submitXuiLogin and idFromSession live in e2e/common/openam-commons.mjs; the XUI HttpOnly spec uses them too.
  • build.yml. Installs krb5-user and gettext-base in a step of its own, after the Playwright install and before the first Playwright run (HttpOnly disabled), and removes openam-kdc in the cleanup step. The Kerberos bootstrap takes about 40-55 s.

2. Module test against an embedded Apache Kerby KDC

  • WindowsDesktopSSOKerberosTest.
    • The module performs its real service login and accepts tokens that the JDK Kerberos client makes against an Apache Kerby KDC on a random port.
    • The Kerby dependency is org.apache.kerby:kerb-simplekdc 2.1.2, test scope. Its version is the kerby.version property in the root pom.
    • No Docker; the module's test run takes about 5 s.
  • KerberosTestRealm keeps the KDC, JAAS and JGSS code out of the PowerMock-prepared test class. PowerMock routes that class's JDK calls through reflection, which fails for packages the surefire argLine does not open, such as java.nio.file. Its stop() restores the JAAS configuration and the krb5 system properties and reloads the JDK's cached krb5.conf.
  • Cases:
    • SPNEGO and raw Kerberos V5 tokens log the user in;
    • a second request to the same module instance logs the user in with the cached service login, without a new one;
    • returnRealm keeps the realm in the user name;
    • a token from a trusted realm is accepted;
    • a token from an untrusted realm is ignored (see below);
    • a ticket for another service fails with auth;
    • an NTLM token fails with token.
  • createUpdateKrb5ConfigFile() is a no-op in the test. The module splits the KDC attribute on : and cannot express Kerby's port, so the test keeps Kerby's own krb5.conf.
  • JAAS configuration. The test installs its own. On Windows the module sets java.security.auth.login.config to /dev/null, a path that does not exist there, and the JDK's default configuration then fails to load.

Note for reviewers

The untrusted-realm case pins the current behaviour: the module returns LOGIN_IGNORE instead of failing with untrustedToken. The exception is thrown inside Subject.doAs, and process() rethrows only a wrapped GSSException. The user is not logged in. In a chain, however, the module counts as ignored, not as failed. This is tracked in #1174, and the assertion links it: once #1174 is fixed, the case has to expect an AuthLoginException with untrustedToken.

Testing

  • CI on the PR:
    • Run 38358428477 at 59ffaa8: all build-maven legs ran the module's tests 13/13 (8 new ones and the 5 existing ones): ubuntu with JDK 11/17/21/25/27, macOS with JDK 11/27 and Windows with JDK 11/27. In build-docker, all four Kerberos cases passed ("12 passed, 1 skipped", the skipped one being the existing HttpOnly step-up test), and the XUI spec on the shared helpers passed with HttpOnly enabled too ("3 passed").
  • CI on the fork, earlier commits:
  • Locally:
    • The module tests passed on JDK 11, 17, 21, 25 and 26, with the test classes run in both orders. At 59ffaa8: 13/13 on JDK 26, in both orders.
    • Mutants make the matching cases fail: parseToken returning null, the trusted-realm check disabled, returnRealm ignored. The cache case fails when the cached service subject is not restored, both with and without the module's fallback to a new service login.
    • At 3d3ec5c, the e2e spec also passed in a Linux container with KRB_REALM=TEST.ORG.

Related: #1170, #1174

Adds e2e/kerberos: an MIT KDC in a container on the test-openam network,
a WindowsDesktopSSO module and a "Kerberos -> DataStore" chain configured
with ssoadm, and a Playwright spec that uses a real ticket through the
system GSSAPI library (KRB5_CONFIG and KRB5CCNAME point at .work/).

Cases:
- the browser's Negotiate token completes a login started without it
  (authId first, then the header): proves the KDC, keytab, module
  settings and the browser's Kerberos;
- one-step REST (curl --negotiate) and the XUI: marked test.fail(),
  /json/authenticate answers 400 to a Negotiate header (OpenIdentityPlatform#1170);
- no ticket: the chain falls through to the DataStore form.

/UI/Login is not covered: XUIFilter redirects it to the XUI.

Every value is an environment variable (kerberos.env). The KDC is a
provider (providers/<name>.sh: kdc_start, kdc_wait, kdc_export_keytab,
kdc_logs, kdc_stop) chosen by KRB_PROVIDER, so a Samba AD DC can be added
later without changing the shared code. bootstrap.sh can be run again.

The build-docker job installs krb5-user and gettext-base before the
first Playwright step.
Adds WindowsDesktopSSOKerberosTest: the module runs its real service
login and accepts tokens made by the JDK Kerberos client against an
Apache Kerby KDC (kerb-simplekdc 2.1.2, test scope) on a random port.
KerberosTestRealm starts the KDC, exports the keytabs and logs the user
in; it is a separate class because PowerMock routes the test class's
JDK calls through reflection that needs packages the surefire argLine
does not open.

Cases:
- SPNEGO and raw Kerberos V5 tokens log the user in;
- returnRealm keeps the realm in the user name;
- a token from a trusted realm is accepted; from an untrusted one the
  module returns LOGIN_IGNORE (the untrustedToken exception thrown in
  Subject.doAs is not rethrown by process());
- a ticket for another service fails with "auth", an NTLM token with
  "token".

The module's krb5.conf update is a no-op in the test: it splits the KDC
attribute on ':' and cannot express Kerby's port. The test installs its
own JAAS configuration, because the default one cannot be created once
the module sets java.security.auth.login.config to /dev/null on Windows.
@maximthomas maximthomas added tests Test suite: coverage, fixtures, or test infrastructure ci CI, GitHub Actions, or build pipeline java Pull requests that update java code dependencies Pull requests that update a dependency file labels Oct 12, 2026
Comment thread e2e/kerberos/kerberos.spec.mjs Dismissed
…s Negotiate challenge

The curl and XUI cases assert the 400 and the HttpCallback message from
OpenIdentityPlatform#1170 instead of test.fail(), which accepted any failure. curl without
GSS-API, no token sent or a broken XUI fragment now turn them red, and
so does the fix of OpenIdentityPlatform#1170.

The fallback case first checks that the chain answers 401 with
WWW-Authenticate: Negotiate, so it fails when the Kerberos module is
missing from the chain.

A failed bootstrap prints the KDC and module logs before rethrowing:
Playwright skips afterEach after a failed beforeAll, and afterAll
removes the KDC container.

Also corrects the comment on the no-op createUpdateKrb5ConfigFile() in
WindowsDesktopSSOKerberosTest: the module would write one kdc=localhost
(port 88), not two KDCs.

@vharseko vharseko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this: running the module against a real KDC, with no Windows involved, closes a long-standing gap. A few changes are needed before merge; details are inline.

Two problems sit outside the diff, so I filed them separately:

  • #1173: after the service credentials expire, a successful retry returns LOGIN_IGNORE unless message-level debug logging is on. None of the new tests reaches this path.
  • #1174: the untrustedToken / context / notfound behaviour described in your "Note for reviewers". Please reference it from the assertion (see the inline comment), the way the e2e spec references #1170.


// The "untrustedToken" AuthLoginException is thrown inside Subject.doAs, and process() rethrows only a
// wrapped GSSException, so the module ignores the token instead of failing.
assertEquals(module.process(null, ISAuthConstants.LOGIN_START), ISAuthConstants.LOGIN_IGNORE);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please point this assertion at #1174, the way REST_NEGOTIATE_BUG points at #1170 in the e2e spec. Whoever fixes the module then knows this case is meant to flip to expectThrows(AuthLoginException.class, ...) with untrustedToken, and is not a behaviour to preserve.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 59ffaa8: the assertion message now carries the #1174 link. The comment above it says the case turns red once #1174 is fixed, and that it should then expect an AuthLoginException with untrustedToken.

* Creates the module for a request with the given Authorization header. Every test uses its own instance name,
* because the module caches the service login per instance in a static table.
*/
private TestModule newModule(String instanceName, String authorization, Map<String, Object> options) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A fresh instance name per test means every test runs only the first-login path of getConfigParams(). The production steady state is never exercised: a second process() on the same instance, which hits the configTable cache and restores serviceSubject from it.

Could you add a case that logs in twice on one instance name? A regression on the cache-hit path (for example serviceSubject not restored, so Subject.doAs runs without the acceptor keys) would currently pass all seven tests.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added secondRequestUsesCachedServiceLogin. It sends two requests to one instance name and expects both to return LOGIN_SUCCEED. TestModule counts service logins through createUpdateKrb5ConfigFile(), which the module calls once per service login in krb5.conf mode, and the test asserts that the second request made none.

Your example is caught only because of that counter. With serviceSubject = (Subject) configMap.get(...) deleted, getConfigParams() reaches the serviceSubject == null check and returns false. The module then logs the service in again, and the second login still succeeds. Measured locally:

  • assignment deleted: only the new test fails, on the counter (expected [0] but found [1]);
  • assignment and null check both deleted (a cache hit with no subject): only the new test fails, with Failed to authentication.;
  • the seven existing tests pass under both mutants.

kdc.stop();
}
} finally {
Configuration.setConfiguration(null);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The class Javadoc says stop() restores the JAAS configuration, but setConfiguration(null) drops whatever Configuration was installed before start() instead of restoring it. Two more pieces of JVM-wide state stay behind:

  • the client LoginContext from start() is never logged out;
  • the JDK keeps its cached krb5 config (Kerby's realm and port) after the file is deleted, until something refreshes it.

A later test class in the same surefire JVM that uses JGSS or Krb5LoginModule without refreshKrb5Config would then talk to a dead KDC, and fail only in that test order. Please save and restore the previous Configuration, log the client out, and refresh the krb5 config once the properties are restored.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. start() saves Configuration.getConfiguration() and stop() puts it back. If no configuration was installed, that call creates the default one, and the default is what gets restored. stop() also logs the client out. Once the properties are restored, it calls sun.security.krb5.Config.refresh(); the root pom already exports sun.security.krb5 to javac and surefire. The class Javadoc now lists all three.

Nothing in this module trips over it today. The module's tests run in their own surefire JVM, and the only other class there, WindowsDesktopSSOTest, uses neither JGSS nor Krb5LoginModule. The module tests pass with the classes in either order (13/13).

* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions copyright [year] [name of copyright owner]".
*
* Copyright 2026 3A Systems LLC.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3A Systems LLC. lacks the comma used in the other new files of this PR (bootstrap.sh, kerberos.env, the spec, the Dockerfile): 3A Systems, LLC.. The same applies to line 14 of WindowsDesktopSSOKerberosTest.java.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in both Java files: 3A Systems, LLC.

@@ -0,0 +1,15 @@
[libdefaults]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new file has no CDDL header. krb5.conf supports # comments, so it can carry the same header as the other new files.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the header as # comments. MIT kinit accepts the rendered file.

Comment thread e2e/kerberos/kerberos.spec.mjs Outdated
res.end();
}
});
await new Promise((listening) => server.listen(0, "127.0.0.1", listening));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The helper server listens on 127.0.0.1, but the browser is sent to http://${OPENAM_HOST}:<port>/. That only works while the OpenAM host name resolves to loopback, as it does in CI. With OPENAM_BASE_URL pointing at a host that resolves to a container or LAN address, the browser never reaches the server, authorization stays undefined, and the REST two-step case fails with "the browser should answer the Negotiate challenge", which reads like a Kerberos fault.

Please either listen on all interfaces, or check that OPENAM_HOST resolves to loopback and skip the case with a clear message otherwise.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server now listens on OPENAM_HOST itself, so Node binds whatever address the name resolves to:

  • if that address belongs to this machine, loopback or LAN, the browser reaches the server;
  • if it does not, as with a container or a remote address, listen fails with EADDRNOTAVAIL, and the case is skipped with a message that names the resolved address.

Listening on all interfaces would not have covered the container case, because a container's address is not one of the host's.

Comment thread e2e/kerberos/kerberos.spec.mjs Outdated
}

/** Resolve the username of the active session from the session cookie or the given token. */
async function idFromSession(request, tokenId) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idFromSession() and the SEL map (line 69) duplicate the private helpers in e2e/xui/xui-httponly.spec.mjs, and the login form fill is repeated too. The comment there notes that the submit button id varies between XUI builds; with two copies, the next change has to be made in both specs. Please move them to e2e/common/openam-commons.mjs next to getAuthToken and use them from both specs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved XUI_LOGIN (the selectors), submitXuiLogin(page, user, pass) and idFromSession(request, tokenId?) to e2e/common/openam-commons.mjs, and both specs now use them:

  • idFromSession returns the raw id, as the XUI spec's copy did, and callers lowercase it;
  • the visibility wait in submitXuiLogin is 30 s, the longer of the two (the XUI spec used 20 s).

saml-test.spec.mjs keeps its own SEL, since its submit selector differs.

: > /etc/krb5kdc/kadm5.acl

kdb5_util create -s -r "$KRB_REALM" -P "$KRB_KDC_ADMIN_PASSWORD"
kadmin.local -r "$KRB_REALM" -q "addprinc -pw $KRB_PASSWORD $KRB_USER@$KRB_REALM"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$KRB_PASSWORD goes into the -q query unquoted, and the value is meant to be overridable (kerberos.env, mit.sh). A password with a space or a leading - makes addprinc misparse its arguments. A failed -q query does not reliably give kadmin.local a non-zero exit status, so set -e may not stop here: the keytab is still exported, the KDC reports ready, and the failure only surfaces later as "kinit failed" after the retries in bootstrap.sh.

Please quote the value inside the query and check that the principal exists (getprinc) before exporting the keytab.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed on this image (krb5 1.20.1-2+deb12u5):

  • kadmin.local -q exits with 0 even when the query fails;
  • getprinc of a missing principal also exits with 0;
  • an unquoted password with a space fails addprinc with a usage message, so no principal is created;
  • a leading - on its own was harmless, because -pw takes the next word.

The password is now quoted inside the query, with any " doubled; that is how kadmin's parser escapes it. Before the keytab is exported, both principals are checked with getprinc, by its output, and the entrypoint exits with 1 if either is missing. Tested with two cases:

  • the password -a "b" c: kinit succeeds;
  • a user name that kadmin cannot parse: the container exits with Principal de mo@EXAMPLE.ORG was not created.

Comment thread .github/workflows/build.yml Outdated
npm install @playwright/test
npx playwright install chromium --with-deps
# MIT Kerberos client for the Kerberos spec (kinit, klist); libgssapi_krb5 is already on the runner
sudo apt-get update && sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends krb5-user gettext-base

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

playwright install --with-deps has just run apt-get update, and this line runs it again in the same step. Every run pays for a second index refresh, and a transient mirror error (Hash Sum mismatch, 404) now fails the whole "HttpOnly disabled" smoke step, losing the SAML, OAuth2 and XUI results over a package only the Kerberos spec needs. Please move the install into its own step, without the extra apt-get update.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Split into three steps:

  • Install Playwright;
  • Install MIT Kerberos client, now only apt-get install: there is no second apt-get update, because --with-deps has just refreshed the package lists;
  • the smoke-test step.

A failure in the install step still stops the job before the tests. I left it that way rather than adding continue-on-error, which would quietly turn the Kerberos spec into a skip.

kdc_start() {
# The build resolves the base image on Docker Hub every time; retry its transient failures.
local attempt
for attempt in 1 2 3; do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every bootstrap rebuilds the KDC image (Docker Hub resolve plus apt-get in the Dockerfile), with up to three attempts, and Playwright runs beforeAll again after a worker restart, so one failed Kerberos case pays for it twice.

Also, only afterAll removes the KDC: the "Stopping containers and removing the network" step in build.yml does not list openam-kdc. If Playwright is killed before afterAll (step timeout, OOM), docker network rm test-openam fails with "network has active endpoints". Please add openam-kdc to that cleanup (tolerating its absence), and consider skipping the build when the image already exists.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added docker rm -f openam-kdc to the cleanup step; it tolerates the container being absent. Strictly speaking, the step cannot hit the case you describe. It has no if: always(), so it runs only after a passing Playwright step, and by then afterAll has removed the KDC. The line covers a future change to either of those.

I'd rather keep the build on every bootstrap:

  • In the run at 56d1c9e, the cold build plus KDC start took about 15 s of a roughly 41 s bootstrap; most of the rest is ssoadm.
  • A warm rebuild with the layer cache takes about 5 s here, and the second bootstrap happens only after a case has already failed.
  • Skipping the build when the image exists would keep a stale image after a local edit of entrypoint.sh, which this round made, unless the tag tracked the content.

That is more machinery than about 5 s on a failure path is worth.

@vharseko vharseko added the kerberos Kerberos / SPNEGO (WindowsDesktopSSO) authentication label Oct 12, 2026
Module test:
- secondRequestUsesCachedServiceLogin: two requests on one module
  instance; the second must succeed without a new service login, which
  TestModule counts through createUpdateKrb5ConfigFile(). Without the
  count a lost cached subject would pass: the module logs in again when
  the cached subject is null.
- The untrusted-realm assertion points at OpenIdentityPlatform#1174.
- KerberosTestRealm.stop() restores the JAAS configuration installed
  before start(), logs the client out and reloads the JDK's krb5.conf
  (sun.security.krb5.Config.refresh()) after restoring the properties.

e2e:
- The browser's Negotiate helper server listens on the address the
  OpenAM host name resolves to; when that is not an address of this
  machine the case is skipped with the address in the message.
- XUI login form selectors, submitXuiLogin() and idFromSession() move
  to e2e/common/openam-commons.mjs, used by the Kerberos and XUI specs.
- The MIT KDC entrypoint quotes the password inside the kadmin.local
  query and checks that both principals exist: kadmin.local -q exits
  with 0 when the query fails.
- krb5.conf.template carries the CDDL header.

build.yml: krb5-user is installed in its own step after the Playwright
install, without a second apt-get update; the cleanup step also removes
openam-kdc.
@maximthomas
maximthomas requested a review from vharseko October 13, 2026 08:07

@vharseko vharseko left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, all ten points from the previous round are addressed in 59ffaa8, and I checked each one against the diff:

  • #1174 reference: the untrusted-realm assertion now carries the link and says what it has to expect once the issue is fixed.
  • Cache-hit path: secondRequestUsesCachedServiceLogin covers it. Two module objects with one instance name is how production reaches the configTable cache, and counting service logins through createUpdateKrb5ConfigFile() catches the case where the module silently logs the service in again, which a plain LOGIN_SUCCEED check would miss. Thanks for running both mutants.
  • KerberosTestRealm.stop(): it restores the previous Configuration, logs the client out and reloads the krb5 config.
  • Headers: 3A Systems, LLC. in both Java files, and krb5.conf.template now has the CDDL header.
  • Helper server: it listens on OPENAM_HOST, and the case is skipped with the resolved address when that address is not on this machine. You're right that listening on all interfaces would not have covered the container case. In run 38358428477 the REST two-step case passed and was not skipped.
  • Shared helpers: XUI_LOGIN, submitXuiLogin and idFromSession are in openam-commons.mjs, and the XUI spec passes on them in both HttpOnly modes.
  • entrypoint.sh: the password is quoted for kadmin's parser, and both principals are checked with getprinc before the keytab is exported. It's good to know that kadmin.local -q exits with 0 even on failure.
  • build.yml: the Kerberos client is installed in its own step, without the second apt-get update, and the cleanup step removes openam-kdc. You're right that the cleanup step has no if: always(), so this is a safeguard for later.
  • KDC image build: I accept your reasoning for keeping it. About 5 s on a failure path doesn't justify tracking image content.

CI is green on every leg. Approving.

@maximthomas
maximthomas merged commit 510c4bd into OpenIdentityPlatform:master Oct 13, 2026
16 checks passed
@maximthomas
maximthomas deleted the features/e2e-kerberos-sso branch October 13, 2026 08:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci CI, GitHub Actions, or build pipeline dependencies Pull requests that update a dependency file java Pull requests that update java code kerberos Kerberos / SPNEGO (WindowsDesktopSSO) authentication tests Test suite: coverage, fixtures, or test infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants