I have a number of applications currently running on Wildfly 10 and using the Picketbox security system with SSO. I am currently upgrading to Wildfly 17 and have converted the security configuration to use the Elytron subsystem, but am having issues getting the SSO cookie to write. I am not upgrading to the JEE8 security APIs.
One of the apps ("app1") being migrated is quite simple, using stock standard form posts via a login-config section in web.xml. This app works correctly: I get the login form, submit my credentials, and the response includes the JSESSIONIDSSO cookie. A second app ("app2") is implemented a bit differently. It also uses login-config but the login page submits to a custom servlet which logs in programmatically using HttpServletRequest.login(username, password). When I submit my credentials in this app they are authenticated correctly but no JSESSIONIDSSO cookie is written.
Wildfly Config
(it's originally set up for AD, but is temporarily set to read users from a file for simpler testing)
<subsystem ...>
...
<application-security-domains>
<application-security-domain name="active-directory" http-authentication-factory="ad-http-auth">
<single-sign-on domain="localhost" key-store="sso-ad-keystore" key-alias="localhost">
<credential-reference clear-text="ssopass"/>
</single-sign-on>
</application-security-domain>
</application-security-domains>
</subsystem>
<subsystem xmlns="urn:wildfly:elytron:7.0" final-providers="combined-providers" disallowed-providers="OracleUcrypto">
<security-domains>
...
<security-domain name="LocalFileDomain" default-realm="LocalFileRealm" permission-mapper="default-permission-mapper">
<realm name="LocalFileRealm"/>
</security-domain>
</security-domains>
<security-realms>
...
<filesystem-realm name="LocalFileRealm">
<file path="fs-realm-users" relative-to="jboss.server.config.dir"/>
</filesystem-realm>
</security-realms>
<http>
...
<http-authentication-factory name="ad-http-auth" security-domain="LocalFileDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="FORM">
<mechanism-realm realm-name="active-directory"/>
</mechanism>
<mechanism mechanism-name="BASIC">
<mechanism-realm realm-name="active-directory"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
</subsystem>
App1
<login-config>
<auth-method>BASIC?silent=true,FORM</auth-method>
<realm-name>App Realm</realm-name>
<form-login-config>
<form-login-page>/login.html</form-login-page>
<form-error-page>/noAccess.html</form-error-page>
</form-login-config>
</login-config>
<form action="j_security_check" method="post">
Username: <input name="j_username" type="text"/>
Password: <input name="j_password" type="password"/>
<input type="submit"/>
</form>
App2
<login-config>
<auth-method>BASIC?silent=true,FORM</auth-method>
<realm-name>App Realm</realm-name>
<form-login-config>
<form-login-page>/login</form-login-page>
<form-error-page>/login</form-error-page>
</form-login-config>
</login-config>
Login form uses Angular to post to the login servlet, which looks like this:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
if (req.getUserPrincipal() == null) {
String username = req.getParameter(USERNAME_FIELD);
String password = req.getParameter(PASSWORD_FIELD);
if (username == null || password == null) {
setResponseStatusAndOutput(LoginResultStatus.UNAUTHORISED, resp);
return;
}
try {
Person person = this.personRepository.findByLogin(username);
if (person != null) {
req.login(username, password);
req.authenticate(resp); // I've tried with and without this line
setResponseStatusAndOutput(LoginResultStatus.SUCCESS, resp);
} else {
setResponseStatusAndOutput(LoginResultStatus.UNAUTHORISED, resp);
}
} catch (ServletException ex) {
setResponseStatusAndOutput(LoginResultStatus.UNAUTHORISED, resp);
}
} else {
setResponseStatusAndOutput(LoginResultStatus.SUCCESS, resp);
}
}
private void setResponseStatusAndOutput(LoginResultStatus loginResultStatus, HttpServletResponse response) throws IOException {
response.setContentType("application/json");
response.setStatus(loginResultStatus.getCode());
response.getOutputStream().print(String.format("{ \"status\": \"%s\" }", loginResultStatus.getValue()));
}
Does Undertow not apply the SSO stuff to requests that are authenticated this way, maybe because I've missed the spot in the filter chain where it's set? Or is there something else I'm not doing right?
Edit 1: I've done some more testing to try and narrow down the issue.
Wildfly 17 Elytron, posting to j_security_check: Logs in OK, writes the JSESSIONIDSSO cookie.
Wildfly 17 Elytron, posting to custom login servlet: Logs in OK, does not write the JSESSIONIDSSO cookie.
Wildfly 10 Legacy, posting to j_security_check: Logs in OK, writes the JSESSIONIDSSO cookie.
Wildfly 10 Legacy, posting to custom login servlet: Logs in OK, writes the JSESSIONIDSSO cookie.
I've created a test project to demonstrate this, which includes the config files for Wildfly 10 and 17.