From db267d16e65953a4702e4da6555d68ebb5f112d9 Mon Sep 17 00:00:00 2001 From: maximthomas Date: Wed, 14 Oct 2026 16:44:28 +0300 Subject: [PATCH 1/4] [#1174] WindowsDesktopSSO: fail instead of ignore when the token check rejects the token --- .../windowsdesktopsso/WindowsDesktopSSO.java | 23 ++++++++- .../WindowsDesktopSSOKerberosTest.java | 49 ++++++++++++++++--- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java b/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java index b24e7d642c..752e4667d7 100644 --- a/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java +++ b/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java @@ -232,7 +232,10 @@ public int process(Callback[] callbacks, int state) throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, e); } - } + } else { + debug.error("Authentication failed with PrivilegedActionException. Stack Trace", e); + throw toAuthLoginException(e); + } } catch (GSSException e1 ){ int major = e1.getMajor(); if (major == GSSException.CREDENTIALS_EXPIRED) { @@ -360,6 +363,21 @@ private static Exception extractException(Exception e) { return e; } + /** + * Returns the AuthLoginException that the token check threw inside Subject.doAs, with its error code and + * arguments, or wraps any other failure in an "auth" AuthLoginException. + * + * @param e the exception from the token check, possibly wrapped in PrivilegedActionException(s) + * @return the exception for the module to throw + */ + private static AuthLoginException toAuthLoginException(Exception e) { + Exception cause = extractException(e); + if (cause instanceof AuthLoginException) { + return (AuthLoginException) cause; + } + return new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, cause); + } + /** * TODO-JAVADOC */ @@ -879,7 +897,8 @@ private void verifyAttributes() throws AuthLoginException { * @return the attribute value for the identity searched. Empty string if not found or * null if an error occurs */ - private String searchUserAccount(String attributeValue, String organization) + @VisibleForTesting + String searchUserAccount(String attributeValue, String organization) throws AuthLoginException { String classMethod = "WindowsDesktopSSO.searchUserAccount: "; diff --git a/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java b/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java index 3628f6aed5..daff41fb7c 100644 --- a/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java +++ b/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java @@ -35,6 +35,8 @@ import javax.security.auth.Subject; import java.io.File; +import java.io.IOException; +import java.security.PrivilegedActionException; import java.util.Base64; import java.util.Collections; import java.util.HashMap; @@ -209,20 +211,42 @@ public void tokenFromTrustedRealmLogsUserIn() throws Exception { } @Test - public void tokenFromUntrustedRealmDoesNotLogUserIn() throws Exception { + public void tokenFromUntrustedRealmIsRejected() throws Exception { Map options = options(); options.put(ATTR_PREFIX + "kerberos-realms-trusted", Collections.singleton("OTHER.COM")); TestModule module = newModule("untrustedRealm", negotiate(SERVICE_PRINCIPAL, SPNEGO_MECH), options); - // #1174: the "untrustedToken" AuthLoginException is thrown inside Subject.doAs, and process() rethrows only a - // wrapped GSSException, so the module ignores the token instead of failing. Once #1174 is fixed this turns - // red: expect an AuthLoginException with the "untrustedToken" error code instead. - assertEquals(module.process(null, ISAuthConstants.LOGIN_START), ISAuthConstants.LOGIN_IGNORE, - "the module ignores an untrusted token instead of failing: " - + "https://github.com/OpenIdentityPlatform/OpenAM/issues/1174"); + AuthLoginException e = expectThrows(AuthLoginException.class, + () -> module.process(null, ISAuthConstants.LOGIN_START)); + assertEquals(e.getErrorCode(), "untrustedToken"); assertNull(module.getPrincipal()); } + @Test + public void tokenForUnknownUserIsRejected() throws Exception { + Map options = options(); + options.put(ATTR_PREFIX + "lookupUserInRealm", Collections.singleton("true")); + TestModule module = newModule("unknownUser", negotiate(SERVICE_PRINCIPAL, SPNEGO_MECH), options); + + AuthLoginException e = expectThrows(AuthLoginException.class, + () -> module.process(null, ISAuthConstants.LOGIN_START)); + assertEquals(e.getErrorCode(), "notfound"); + assertEquals(e.getMessageArgs(), new Object[] {USER, "/"}); + assertNull(module.getPrincipal()); + } + + @Test + public void unexpectedFailureInTokenCheckIsRejected() throws Exception { + TestModule module = newModule("unexpectedFailure", negotiate(SERVICE_PRINCIPAL, SPNEGO_MECH), options()); + IOException failure = new IOException("token check failed"); + module.tokenCheckFailure = failure; + + AuthLoginException e = expectThrows(AuthLoginException.class, + () -> module.process(null, ISAuthConstants.LOGIN_START)); + assertEquals(e.getErrorCode(), "auth"); + assertSame(e.getCause(), failure); + } + @Test public void tokenForAnotherServiceIsRejected() throws Exception { TestModule module = newModule("otherService", negotiate(OTHER_SERVICE_PRINCIPAL, SPNEGO_MECH), options()); @@ -294,6 +318,8 @@ private static final class TestModule extends WindowsDesktopSSO { private int serviceLogins; // Null: the service credentials never expire. private ExpiringServiceCredentials credentials; + // Null: the token check runs; otherwise it fails as Subject.doAs reports a checked exception. + private Exception tokenCheckFailure; TestModule(HttpServletRequest request) { this.request = request; @@ -322,9 +348,18 @@ protected void createUpdateKrb5ConfigFile() { serviceLogins++; } + /** No identity store in this test: every user is unknown. */ + @Override + String searchUserAccount(String attributeValue, String organization) { + return null; + } + @Override void authenticateToken(Subject subject, byte[] kerberosToken, Set trustedRealms) throws Exception { + if (tokenCheckFailure != null) { + throw new PrivilegedActionException(tokenCheckFailure); + } if (credentials != null) { credentials.check(subject); } From e43fbe6bcf20ffb79d198475ac3ca44e5e49ef50 Mon Sep 17 00:00:00 2001 From: maximthomas Date: Wed, 14 Oct 2026 16:50:58 +0300 Subject: [PATCH 2/4] [#1174] WindowsDesktopSSO: keep the token check's error code after a retry on expired credentials --- .../modules/windowsdesktopsso/WindowsDesktopSSO.java | 6 ++---- .../windowsdesktopsso/WindowsDesktopSSOKerberosTest.java | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java b/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java index 752e4667d7..6793462e26 100644 --- a/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java +++ b/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java @@ -224,8 +224,7 @@ public int process(Callback[] callbacks, int state) } } catch (Exception ee) { debug.error("Authentication failed with new cred.Stack Trace", ee); - throw new AuthLoginException(amAuthWindowsDesktopSSO, - "auth", null, ee); + throw toAuthLoginException(ee); } } else { debug.error("Authentication failed with PrivilegedActionException wrapped GSSException. Stack Trace", e); @@ -250,8 +249,7 @@ public int process(Callback[] callbacks, int state) } } catch (Exception ee) { debug.error("Authentication failed with new cred. Stack Trace", ee); - throw new AuthLoginException(amAuthWindowsDesktopSSO, - "auth", null, ee); + throw toAuthLoginException(ee); } } else { debug.error("Authentication failed with GSSException. Stack Trace", e1); diff --git a/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java b/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java index daff41fb7c..ade7bcaa69 100644 --- a/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java +++ b/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java @@ -178,7 +178,7 @@ public void renewedServiceLoginIsCachedWhenRetriedTokenFails(String shape, boole TestModule first = newModule("cache-failed-" + shape, negotiate(SERVICE_PRINCIPAL, SPNEGO_MECH), untrusted); first.credentials = credentials; AuthLoginException e = expectThrows(AuthLoginException.class, () -> processAtServerDebugLevel(first)); - assertEquals(e.getErrorCode(), "auth"); + assertEquals(e.getErrorCode(), "untrustedToken"); assertEquals(first.serviceLogins, 2); Subject renewed = credentials.lastUsed(); From cc0d25065ee43d41f8ac5b87ee90d4773afe9b7c Mon Sep 17 00:00:00 2001 From: maximthomas Date: Wed, 14 Oct 2026 17:28:46 +0300 Subject: [PATCH 3/4] [#1174] WindowsDesktopSSO: name the user principal, not a realm, in the untrustedToken message --- .../src/main/resources/amAuthWindowsDesktopSSO.properties | 2 +- .../windowsdesktopsso/WindowsDesktopSSOKerberosTest.java | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/openam-authentication/openam-auth-windowsdesktopsso/src/main/resources/amAuthWindowsDesktopSSO.properties b/openam-authentication/openam-auth-windowsdesktopsso/src/main/resources/amAuthWindowsDesktopSSO.properties index c01e24189e..816294f41b 100644 --- a/openam-authentication/openam-auth-windowsdesktopsso/src/main/resources/amAuthWindowsDesktopSSO.properties +++ b/openam-authentication/openam-auth-windowsdesktopsso/src/main/resources/amAuthWindowsDesktopSSO.properties @@ -75,5 +75,5 @@ ssoSearch=SSO exception while trying to find the user {0} in the realm {1} notfound=Unable to find the user {0} in the realm {1} i18nTrue=Enabled i18nFalse=Disabled -untrustedToken=Kerberos tokens from realm {0} are not trusted. +untrustedToken=Kerberos token for {0} is not from a trusted realm. diff --git a/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java b/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java index ade7bcaa69..242b1225a5 100644 --- a/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java +++ b/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java @@ -219,6 +219,8 @@ public void tokenFromUntrustedRealmIsRejected() throws Exception { AuthLoginException e = expectThrows(AuthLoginException.class, () -> module.process(null, ISAuthConstants.LOGIN_START)); assertEquals(e.getErrorCode(), "untrustedToken"); + // The argument is the user principal from the token, not its realm. + assertEquals(e.getMessage(), "Kerberos token for " + USER_PRINCIPAL + " is not from a trusted realm."); assertNull(module.getPrincipal()); } From 509b017443ad78c2da99f926ef3e9e055191f167 Mon Sep 17 00:00:00 2001 From: maximthomas Date: Thu, 15 Oct 2026 10:06:17 +0300 Subject: [PATCH 4/4] [#1174] WindowsDesktopSSO: fix the searchUserAccount Javadoc and test a known user's login searchUserAccount returns null when no identity is found and throws on a search error; the Javadoc said it returned "" and null. The test stub now finds a configurable known user, so the lookupUserInRealm success path is covered alongside the rejection. Add the 3A Systems copyright line to amAuthWindowsDesktopSSO.properties. --- .../windowsdesktopsso/WindowsDesktopSSO.java | 5 +++-- .../amAuthWindowsDesktopSSO.properties | 1 + .../WindowsDesktopSSOKerberosTest.java | 17 +++++++++++++++-- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java b/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java index 6793462e26..3a4592b20d 100644 --- a/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java +++ b/openam-authentication/openam-auth-windowsdesktopsso/src/main/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSO.java @@ -892,8 +892,9 @@ private void verifyAttributes() throws AuthLoginException { * identity in the organization * @param organization organization or the organization name where the identity will be * looked up - * @return the attribute value for the identity searched. Empty string if not found or - * null if an error occurs + * @return the attribute value for the identity searched, or null if no identity is found + * @throws AuthLoginException if the alias search attribute names cannot be read, or the search fails with an + * identity repository ("idRepoSearch") or SSO ("ssoSearch") error */ @VisibleForTesting String searchUserAccount(String attributeValue, String organization) diff --git a/openam-authentication/openam-auth-windowsdesktopsso/src/main/resources/amAuthWindowsDesktopSSO.properties b/openam-authentication/openam-auth-windowsdesktopsso/src/main/resources/amAuthWindowsDesktopSSO.properties index 816294f41b..7f40b98e1a 100644 --- a/openam-authentication/openam-auth-windowsdesktopsso/src/main/resources/amAuthWindowsDesktopSSO.properties +++ b/openam-authentication/openam-auth-windowsdesktopsso/src/main/resources/amAuthWindowsDesktopSSO.properties @@ -26,6 +26,7 @@ # # Portions Copyrighted 2011-2014 ForgeRock AS +# Portions Copyright 2026 3A Systems, LLC. # diff --git a/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java b/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java index 242b1225a5..f742142344 100644 --- a/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java +++ b/openam-authentication/openam-auth-windowsdesktopsso/src/test/java/com/sun/identity/authentication/modules/windowsdesktopsso/WindowsDesktopSSOKerberosTest.java @@ -224,6 +224,17 @@ public void tokenFromUntrustedRealmIsRejected() throws Exception { assertNull(module.getPrincipal()); } + @Test + public void tokenForKnownUserLogsUserIn() throws Exception { + Map options = options(); + options.put(ATTR_PREFIX + "lookupUserInRealm", Collections.singleton("true")); + TestModule module = newModule("knownUser", negotiate(SERVICE_PRINCIPAL, SPNEGO_MECH), options); + module.knownUser = USER; + + assertEquals(module.process(null, ISAuthConstants.LOGIN_START), ISAuthConstants.LOGIN_SUCCEED); + assertEquals(module.getPrincipal().getName(), USER); + } + @Test public void tokenForUnknownUserIsRejected() throws Exception { Map options = options(); @@ -322,6 +333,8 @@ private static final class TestModule extends WindowsDesktopSSO { private ExpiringServiceCredentials credentials; // Null: the token check runs; otherwise it fails as Subject.doAs reports a checked exception. private Exception tokenCheckFailure; + // Null: the identity store has no users; otherwise the only user that the search finds. + private String knownUser; TestModule(HttpServletRequest request) { this.request = request; @@ -350,10 +363,10 @@ protected void createUpdateKrb5ConfigFile() { serviceLogins++; } - /** No identity store in this test: every user is unknown. */ + /** No identity store in this test: finds only the known user and, as the module does, returns the value. */ @Override String searchUserAccount(String attributeValue, String organization) { - return null; + return attributeValue.equals(knownUser) ? attributeValue : null; } @Override