Skip to content

Commit 898eef5

Browse files
author
Samuel Campos
committed
Initial support for SSO with HTTP-POST binding
1 parent dd3f0b4 commit 898eef5

File tree

5 files changed

+135
-28
lines changed

5 files changed

+135
-28
lines changed

core/src/main/java/com/onelogin/saml2/exception/SettingsException.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ public class SettingsException extends Exception {
1010
public static final int PRIVATE_KEY_NOT_FOUND = 4;
1111
public static final int PUBLIC_CERT_FILE_NOT_FOUND = 5;
1212
public static final int PRIVATE_KEY_FILE_NOT_FOUND = 6;
13-
13+
public static final int UNSUPPORTED_BINDING = 7;
14+
1415
private int errorCode;
1516

1617
public SettingsException(String message, int errorCode) {

core/src/main/java/com/onelogin/saml2/util/Util.java

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,29 @@ public static Document copyDocument(Document source) throws ParserConfigurationE
10811081
* @throws XPathExpressionException
10821082
*/
10831083
public static String addSign(Document document, PrivateKey key, X509Certificate certificate, String signAlgorithm) throws XMLSecurityException, XPathExpressionException {
1084+
return addSign(document, key, certificate, signAlgorithm, Constants.C14N_WC);
1085+
}
1086+
1087+
/**
1088+
* Signs the Document using the specified signature algorithm with the private key and the public certificate.
1089+
*
1090+
* @param document
1091+
* The document to be signed
1092+
* @param key
1093+
* The private key
1094+
* @param certificate
1095+
* The public certificate
1096+
* @param signAlgorithm
1097+
* Signature Algorithm
1098+
* @param c14nMethod
1099+
* Canonicalization method
1100+
*
1101+
* @return the signed document in string format
1102+
*
1103+
* @throws XMLSecurityException
1104+
* @throws XPathExpressionException
1105+
*/
1106+
public static String addSign(Document document, PrivateKey key, X509Certificate certificate, String signAlgorithm, String c14nMethod) throws XMLSecurityException, XPathExpressionException {
10841107
org.apache.xml.security.Init.init();
10851108

10861109
// Check arguments.
@@ -1095,7 +1118,7 @@ public static String addSign(Document document, PrivateKey key, X509Certificate
10951118
if (key == null) {
10961119
throw new IllegalArgumentException("Provided key was null");
10971120
}
1098-
1121+
10991122
if (certificate == null) {
11001123
throw new IllegalArgumentException("Provided certificate was null");
11011124
}
@@ -1104,17 +1127,13 @@ public static String addSign(Document document, PrivateKey key, X509Certificate
11041127
signAlgorithm = Constants.RSA_SHA1;
11051128
}
11061129

1107-
// document.normalizeDocument();
1108-
1109-
String c14nMethod = Constants.C14N_WC;
1110-
11111130
// Signature object
11121131
XMLSignature sig = new XMLSignature(document, null, signAlgorithm, c14nMethod);
11131132

11141133
// Including the signature into the document before sign, because
11151134
// this is an envelop signature
11161135
Element root = document.getDocumentElement();
1117-
document.setXmlStandalone(false);
1136+
document.setXmlStandalone(false);
11181137

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

11431162
// Add the certification info
1144-
sig.addKeyInfo(certificate);
1163+
sig.addKeyInfo(certificate);
11451164

11461165
// Sign the document
11471166
sig.sign(key);
@@ -1553,5 +1572,5 @@ private static byte[] toBytesUtf8(String str) {
15531572
}
15541573
}
15551574

1556-
1575+
15571576
}

toolkit/src/main/java/com/onelogin/saml2/Auth.java

Lines changed: 73 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@
1111
import java.util.LinkedHashMap;
1212
import java.util.List;
1313
import java.util.Map;
14+
import java.util.Objects;
15+
import java.util.Scanner;
1416

1517
import javax.servlet.http.HttpServletRequest;
1618
import javax.servlet.http.HttpServletResponse;
19+
import javax.xml.parsers.ParserConfigurationException;
20+
import javax.xml.xpath.XPathExpressionException;
1721

1822
import org.apache.commons.lang3.StringUtils;
23+
import org.apache.xml.security.exceptions.XMLSecurityException;
1924
import org.joda.time.DateTime;
2025
import org.joda.time.Instant;
2126
import org.slf4j.Logger;
@@ -34,6 +39,7 @@
3439
import com.onelogin.saml2.settings.SettingsBuilder;
3540
import com.onelogin.saml2.util.Constants;
3641
import com.onelogin.saml2.util.Util;
42+
import org.xml.sax.SAXException;
3743

3844
/**
3945
* Main class of OneLogin's Java Toolkit.
@@ -139,6 +145,8 @@ public class Auth {
139145
*/
140146
private String lastResponse;
141147

148+
private String requestPostBindingHTML;
149+
142150
/**
143151
* Initializes the SP SAML instance.
144152
*
@@ -224,6 +232,12 @@ public Auth(Saml2Settings settings, HttpServletRequest request, HttpServletRespo
224232
throw new SettingsException(errorMsg, SettingsException.SETTINGS_INVALID);
225233
}
226234
LOGGER.debug("Settings validated");
235+
236+
237+
if (Objects.equals(Constants.BINDING_HTTP_POST, settings.getIdpSingleSignOnServiceBinding())) {
238+
Scanner s = new Scanner(getClass().getClassLoader().getResourceAsStream("request-post-binding.html")).useDelimiter("\\A");
239+
this.requestPostBindingHTML = s.hasNext() ? s.next() : "";
240+
}
227241
}
228242

229243
/**
@@ -262,37 +276,77 @@ public String login(String returnTo, Boolean forceAuthn, Boolean isPassive, Bool
262276

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

265-
String samlRequest = authnRequest.getEncodedAuthnRequest();
266-
267-
parameters.put("SAMLRequest", samlRequest);
268-
269279
String relayState;
270280
if (returnTo == null) {
271281
relayState = ServletUtils.getSelfRoutedURLNoQuery(request);
272282
} else {
273283
relayState = returnTo;
274284
}
275285

276-
if (!relayState.isEmpty()) {
277-
parameters.put("RelayState", relayState);
278-
}
286+
if (Objects.equals(Constants.BINDING_HTTP_REDIRECT, settings.getIdpSingleSignOnServiceBinding())) {
287+
String samlRequest = authnRequest.getEncodedAuthnRequest();
279288

280-
if (settings.getAuthnRequestsSigned()) {
281-
String sigAlg = settings.getSignatureAlgorithm();
282-
String signature = this.buildRequestSignature(samlRequest, relayState, sigAlg);
289+
parameters.put("SAMLRequest", samlRequest);
283290

284-
parameters.put("SigAlg", sigAlg);
285-
parameters.put("Signature", signature);
286-
}
291+
if (!relayState.isEmpty()) {
292+
parameters.put("RelayState", relayState);
293+
}
287294

288-
String ssoUrl = getSSOurl();
289-
lastRequestId = authnRequest.getId();
290-
lastRequest = authnRequest.getAuthnRequestXml();
295+
if (settings.getAuthnRequestsSigned()) {
296+
String sigAlg = settings.getSignatureAlgorithm();
297+
String signature = this.buildRequestSignature(samlRequest, relayState, sigAlg);
291298

292-
if (!stay) {
293-
LOGGER.debug("AuthNRequest sent to " + ssoUrl + " --> " + samlRequest);
299+
parameters.put("SigAlg", sigAlg);
300+
parameters.put("Signature", signature);
301+
}
302+
303+
String ssoUrl = getSSOurl();
304+
lastRequestId = authnRequest.getId();
305+
lastRequest = authnRequest.getAuthnRequestXml();
306+
307+
if (!stay) {
308+
LOGGER.debug("AuthNRequest sent to " + ssoUrl + " --> " + samlRequest);
309+
}
310+
return ServletUtils.sendRedirect(response, ssoUrl, parameters, stay);
311+
} else if (Objects.equals(Constants.BINDING_HTTP_POST, settings.getIdpSingleSignOnServiceBinding())) {
312+
String requestPostBinding = null;
313+
314+
try {
315+
String authnRequestXML;
316+
317+
if (settings.getAuthnRequestsSigned()) {
318+
authnRequestXML = Util.addSign(
319+
Util.convertStringToDocument(authnRequest.getAuthnRequestXml()),
320+
settings.getSPkey(),
321+
settings.getSPcert(),
322+
settings.getSignatureAlgorithm(),
323+
Constants.C14NEXC);
324+
325+
LOGGER.debug("Signed XML: {}", authnRequestXML);
326+
} else {
327+
authnRequestXML = authnRequest.getEncodedAuthnRequest(false);
328+
}
329+
330+
requestPostBinding = String.format(
331+
this.requestPostBindingHTML,
332+
settings.getOrganization().getOrgDisplayName(),
333+
settings.getIdpSingleSignOnServiceUrl(),
334+
Util.base64encoder(authnRequestXML),
335+
relayState
336+
);
337+
338+
if (!stay) {
339+
ServletUtils.respondWithContentString(response, requestPostBinding, "text/html; charset=utf-8");
340+
}
341+
342+
} catch (Exception e) {
343+
LOGGER.error("Error {}", e.getMessage(), e);
344+
}
345+
346+
return requestPostBinding;
347+
} else {
348+
throw new SettingsException("Unsupported SSO binding: " + settings.getIdpSingleSignOnServiceBinding(), SettingsException.UNSUPPORTED_BINDING);
294349
}
295-
return ServletUtils.sendRedirect(response, ssoUrl, parameters, stay);
296350
}
297351

298352
/**

toolkit/src/main/java/com/onelogin/saml2/servlet/ServletUtils.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,20 @@ public static void sendRedirect(HttpServletResponse response, String location) t
214214
Map<String, String> parameters =new HashMap<String, String>();
215215
sendRedirect(response, location, parameters);
216216
}
217+
218+
/**
219+
* Respond to a request with a given {@link String}.
220+
*
221+
* @param response The response.
222+
* @param contentToWrite The content to write in the {@code response}.
223+
* @param contentType The content type of the content to be written.
224+
* @throws IOException If it fails to write in the {@code response}.
225+
*/
226+
public static void respondWithContentString(HttpServletResponse response, String contentToWrite, String contentType) throws IOException {
227+
response.setContentType(contentType);
228+
response.setContentLength(contentToWrite.getBytes("UTF-8").length);
229+
response.getWriter().write(contentToWrite);
230+
response.getWriter().flush();
231+
response.getWriter().close();
232+
}
217233
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>%s</title>
5+
</head>
6+
<body>
7+
<form method="POST" name="hiddenform" action="%s">
8+
<input type="hidden" name="SAMLRequest" value="%s" />
9+
<input type="hidden" name="RelayState" value="%s" />
10+
<noscript>
11+
<p>Script is disabled. Click Submit to continue.</p>
12+
<input type="submit" value="Submit" />
13+
</noscript>
14+
</form>
15+
<script language="javascript">window.setTimeout('document.forms[0].submit()', 0);</script>
16+
</body>
17+
</html>

0 commit comments

Comments
 (0)