Skip to content

Commit

Permalink
Initial support for SSO with HTTP-POST binding
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel Campos committed Jan 24, 2018
1 parent dd3f0b4 commit 898eef5
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ public class SettingsException extends Exception {
public static final int PRIVATE_KEY_NOT_FOUND = 4;
public static final int PUBLIC_CERT_FILE_NOT_FOUND = 5;
public static final int PRIVATE_KEY_FILE_NOT_FOUND = 6;

public static final int UNSUPPORTED_BINDING = 7;

private int errorCode;

public SettingsException(String message, int errorCode) {
Expand Down
35 changes: 27 additions & 8 deletions core/src/main/java/com/onelogin/saml2/util/Util.java
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,29 @@ public static Document copyDocument(Document source) throws ParserConfigurationE
* @throws XPathExpressionException
*/
public static String addSign(Document document, PrivateKey key, X509Certificate certificate, String signAlgorithm) throws XMLSecurityException, XPathExpressionException {
return addSign(document, key, certificate, signAlgorithm, Constants.C14N_WC);
}

/**
* Signs the Document using the specified signature algorithm with the private key and the public certificate.
*
* @param document
* The document to be signed
* @param key
* The private key
* @param certificate
* The public certificate
* @param signAlgorithm
* Signature Algorithm
* @param c14nMethod
* Canonicalization method
*
* @return the signed document in string format
*
* @throws XMLSecurityException
* @throws XPathExpressionException
*/
public static String addSign(Document document, PrivateKey key, X509Certificate certificate, String signAlgorithm, String c14nMethod) throws XMLSecurityException, XPathExpressionException {
org.apache.xml.security.Init.init();

// Check arguments.
Expand All @@ -1095,7 +1118,7 @@ public static String addSign(Document document, PrivateKey key, X509Certificate
if (key == null) {
throw new IllegalArgumentException("Provided key was null");
}

if (certificate == null) {
throw new IllegalArgumentException("Provided certificate was null");
}
Expand All @@ -1104,17 +1127,13 @@ public static String addSign(Document document, PrivateKey key, X509Certificate
signAlgorithm = Constants.RSA_SHA1;
}

// document.normalizeDocument();

String c14nMethod = Constants.C14N_WC;

// Signature object
XMLSignature sig = new XMLSignature(document, null, signAlgorithm, c14nMethod);

// Including the signature into the document before sign, because
// this is an envelop signature
Element root = document.getDocumentElement();
document.setXmlStandalone(false);
document.setXmlStandalone(false);

// If Issuer, locate Signature after Issuer, Otherwise as first child.
NodeList issuerNodes = Util.query(document, "//saml:Issuer", null);
Expand All @@ -1141,7 +1160,7 @@ public static String addSign(Document document, PrivateKey key, X509Certificate
sig.addDocument(reference, transforms, Constants.SHA1);

// Add the certification info
sig.addKeyInfo(certificate);
sig.addKeyInfo(certificate);

// Sign the document
sig.sign(key);
Expand Down Expand Up @@ -1553,5 +1572,5 @@ private static byte[] toBytesUtf8(String str) {
}
}


}
92 changes: 73 additions & 19 deletions toolkit/src/main/java/com/onelogin/saml2/Auth.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Scanner;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;

import org.apache.commons.lang3.StringUtils;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.joda.time.DateTime;
import org.joda.time.Instant;
import org.slf4j.Logger;
Expand All @@ -34,6 +39,7 @@
import com.onelogin.saml2.settings.SettingsBuilder;
import com.onelogin.saml2.util.Constants;
import com.onelogin.saml2.util.Util;
import org.xml.sax.SAXException;

/**
* Main class of OneLogin's Java Toolkit.
Expand Down Expand Up @@ -139,6 +145,8 @@ public class Auth {
*/
private String lastResponse;

private String requestPostBindingHTML;

/**
* Initializes the SP SAML instance.
*
Expand Down Expand Up @@ -224,6 +232,12 @@ public Auth(Saml2Settings settings, HttpServletRequest request, HttpServletRespo
throw new SettingsException(errorMsg, SettingsException.SETTINGS_INVALID);
}
LOGGER.debug("Settings validated");


if (Objects.equals(Constants.BINDING_HTTP_POST, settings.getIdpSingleSignOnServiceBinding())) {
Scanner s = new Scanner(getClass().getClassLoader().getResourceAsStream("request-post-binding.html")).useDelimiter("\\A");
this.requestPostBindingHTML = s.hasNext() ? s.next() : "";
}
}

/**
Expand Down Expand Up @@ -262,37 +276,77 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool

AuthnRequest authnRequest = new AuthnRequest(settings, forceAuthn, isPassive, setNameIdPolicy);

String samlRequest = authnRequest.getEncodedAuthnRequest();

parameters.put("SAMLRequest", samlRequest);

String relayState;
if (returnTo == null) {
relayState = ServletUtils.getSelfRoutedURLNoQuery(request);
} else {
relayState = returnTo;
}

if (!relayState.isEmpty()) {
parameters.put("RelayState", relayState);
}
if (Objects.equals(Constants.BINDING_HTTP_REDIRECT, settings.getIdpSingleSignOnServiceBinding())) {
String samlRequest = authnRequest.getEncodedAuthnRequest();

if (settings.getAuthnRequestsSigned()) {
String sigAlg = settings.getSignatureAlgorithm();
String signature = this.buildRequestSignature(samlRequest, relayState, sigAlg);
parameters.put("SAMLRequest", samlRequest);

parameters.put("SigAlg", sigAlg);
parameters.put("Signature", signature);
}
if (!relayState.isEmpty()) {
parameters.put("RelayState", relayState);
}

String ssoUrl = getSSOurl();
lastRequestId = authnRequest.getId();
lastRequest = authnRequest.getAuthnRequestXml();
if (settings.getAuthnRequestsSigned()) {
String sigAlg = settings.getSignatureAlgorithm();
String signature = this.buildRequestSignature(samlRequest, relayState, sigAlg);

if (!stay) {
LOGGER.debug("AuthNRequest sent to " + ssoUrl + " --> " + samlRequest);
parameters.put("SigAlg", sigAlg);
parameters.put("Signature", signature);
}

String ssoUrl = getSSOurl();
lastRequestId = authnRequest.getId();
lastRequest = authnRequest.getAuthnRequestXml();

if (!stay) {
LOGGER.debug("AuthNRequest sent to " + ssoUrl + " --> " + samlRequest);
}
return ServletUtils.sendRedirect(response, ssoUrl, parameters, stay);
} else if (Objects.equals(Constants.BINDING_HTTP_POST, settings.getIdpSingleSignOnServiceBinding())) {
String requestPostBinding = null;

try {
String authnRequestXML;

if (settings.getAuthnRequestsSigned()) {
authnRequestXML = Util.addSign(
Util.convertStringToDocument(authnRequest.getAuthnRequestXml()),
settings.getSPkey(),
settings.getSPcert(),
settings.getSignatureAlgorithm(),
Constants.C14NEXC);

LOGGER.debug("Signed XML: {}", authnRequestXML);
} else {
authnRequestXML = authnRequest.getEncodedAuthnRequest(false);
}

requestPostBinding = String.format(
this.requestPostBindingHTML,
settings.getOrganization().getOrgDisplayName(),
settings.getIdpSingleSignOnServiceUrl(),
Util.base64encoder(authnRequestXML),
relayState
);

if (!stay) {
ServletUtils.respondWithContentString(response, requestPostBinding, "text/html; charset=utf-8");
}

} catch (Exception e) {
LOGGER.error("Error {}", e.getMessage(), e);
}

return requestPostBinding;
} else {
throw new SettingsException("Unsupported SSO binding: " + settings.getIdpSingleSignOnServiceBinding(), SettingsException.UNSUPPORTED_BINDING);
}
return ServletUtils.sendRedirect(response, ssoUrl, parameters, stay);
}

/**
Expand Down
16 changes: 16 additions & 0 deletions toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,20 @@ public static void sendRedirect(HttpServletResponse response, String location) t
Map<String, String> parameters =new HashMap<String, String>();
sendRedirect(response, location, parameters);
}

/**
* Respond to a request with a given {@link String}.
*
* @param response The response.
* @param contentToWrite The content to write in the {@code response}.
* @param contentType The content type of the content to be written.
* @throws IOException If it fails to write in the {@code response}.
*/
public static void respondWithContentString(HttpServletResponse response, String contentToWrite, String contentType) throws IOException {
response.setContentType(contentType);
response.setContentLength(contentToWrite.getBytes("UTF-8").length);
response.getWriter().write(contentToWrite);
response.getWriter().flush();
response.getWriter().close();
}
}
17 changes: 17 additions & 0 deletions toolkit/src/main/resources/request-post-binding.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>%s</title>
</head>
<body>
<form method="POST" name="hiddenform" action="%s">
<input type="hidden" name="SAMLRequest" value="%s" />
<input type="hidden" name="RelayState" value="%s" />
<noscript>
<p>Script is disabled. Click Submit to continue.</p>
<input type="submit" value="Submit" />
</noscript>
</form>
<script language="javascript">window.setTimeout('document.forms[0].submit()', 0);</script>
</body>
</html>

0 comments on commit 898eef5

Please sign in to comment.