We have a requirement to implement an IDP that supports authentication/authorization using another external IDP in addition to the standard the username/password form authentication. Does picketlink support this scenario out of the box?.
Here are the required components:
- Multiple SPs that depend on our IDP (Lets call it IDP-Internal) for authentication.
- IDP-Internal should support username/password form authentication using LdapLoginModule and a DatabaseLogingModule for different user groups. It should also support authentication using another IDP (Lets call it IDP-External)
- IDP-External is from an external provider with some SAML extensions.
We have currently implemented the SP and IDP-Internal using picketlink (following picketlink samples). However, the IDP-Internal to IDP-External integration is currently implemented using some hacky borrowed code that handcrafts/parses SAML request/response (no picketlink here). I have managed to integrated this hacky code into IDP-Internal by extending picketlink's IDPWebBrowserSSOValve. This custom valve parses the incoming SAMLResponse from IDP-External and does the local container authentication using a SAML2LoginModule similar to how it is done on the SP side.
Here is the custom valve:
public class IDPInternalAuthentiationValve extends IDPWebBrowserSSOValve {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
String samlResponseXML = request.getParameter(GeneralConstants.SAML_RESPONSE_KEY);
if (isNotNull(samlResponseXML)) {
// This is a SAML Response from IDP-External
// Parse SAML and get subjectName and roles
// Lookup subject in an internal database and get local roles
ServiceProviderSAMLContext.push(subjectName, combinedRoles);
// Authenticate locally using SAML2LoginModule
Principal principal = container.getRealm().authenticate(subjectName, ServiceProviderSAMLContext.EMPTY_PASSWORD);
ServiceProviderSAMLContext.clear();
Session session = request.getSessionInternal(false);
// Save the authenticated Principal in our session
session.setNote(Constants.FORM_PRINCIPAL_NOTE, principal);
String samlRequestFromSession = (String)session.getNote(GeneralConstants.SAML_REQUEST_KEY);
// This is the request from SP
if (isNotNull(samlRequestFromSession)) {
// Send a response back to SP using the principal created above
processSAMLRequestMessage(request, response);
}
} else {
// Username/password form authentication flow
super.invoke(request, response);
}
}
}
This solutions, although works, feels a bit hacky. Is there a better way of doing this using just picketlink?