Thomas Pedersen
posted this on March 29, 2011 09:33 pm
Make sure you read the Introduction to OneLogin's SAML Toolkits.
Download the toolkit from github.com/onelogin/java-saml.
The com-folder contains the files you’ll copy into your Java application. The files index.jsp and consume.jsp are the ones that actually handle the SAML conversation, so let's have a look at those. They will act as a template for making your application a SAML relying party/service provider.
The index.jsp file acts as an initiater for the SAML conversation, if it should be initiated by the application. This is called Service Provider Initiated SAML. The service provider creates a SAML Authentication Request and sends it to the identity provider (IdP):
<%@page import="java.net.URLEncoder,org.apache.log4j.Logger"%> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.onelogin.saml.*,com.onelogin.*" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Auth Request</title> <% // the appSettings object contain application specific settings used by the SAML library AppSettings appSettings = new AppSettings(); // set the URL of the consume.jsp (or similar) file for this app. The SAML Response will be posted to this URL appSettings.setAssertionConsumerServiceUrl("http://x.y.z.z/consume.jsp"); // set the issuer of the authentication request. This would usually be the URL of the issuing web application appSettings.setIssuer("https://www.mywebapp.com"); // the accSettings object contains settings specific to the users account. // At this point, your application must have identified the users origin AccountSettings accSettings = new AccountSettings(); // The URL at the Identity Provider where to the authentication request should be sent accSettings.setIdpSsoTargetUrl("https://app.onelogin.com/saml/signon/12345"); // Generate an AuthRequest and send it to the identity provider AuthRequest authReq = new AuthRequest(appSettings, accSettings); String reqString = accSettings.getIdp_sso_target_url()+"?SAMLRequest=" + AuthRequest.getRidOfCRLF(URLEncoder.encode(authReq.getRequest(AuthRequest.base64),"UTF-8")); response.sendRedirect(reqString); %> </head> <body> </body> </html>
In order to know where to redirect the user with the authentication request, we need to establish the user's identity provider affinity. This depends on your application. Perhaps accounts have dedicated subdomain name (e.g. mycompany.accountingapp.com) or SAML-authentication for accounts is limited to certain IP-ranges. In those situations, you need to look up account information based on whatever information you already have about the user. In this example, those settings are provided by consume.jsp, which is meant as a stub for you customization:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="com.onelogin.*,com.onelogin.saml.*,org.apache.log4j.Logger" %> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>SAML Assertion Page</title> </head> <body> <% String certificateS ="MIIBrTCCAaGgAwIBAgIBATADBgE......"; // user account specific settings. Import the certificate here AccountSettings accountSettings = new AccountSettings(); accountSettings.setCertificate(certificateS); Response samlResponse = new Response(accountSettings); samlResponse.loadXmlFromBase64(request.getParameter("SAMLResponse")); if (samlResponse.isValid()) { // the signature of the SAML Response is valid. The source is trusted java.io.PrintWriter writer = response.getWriter(); writer.write("OK!"); String nameId = samlResponse.getNameId(); writer.write(nameId); writer.flush(); } else { // the signature of the SAML Response is not valid java.io.PrintWriter writer = response.getWriter(); writer.write("Failed"); writer.flush(); } %> </body> </html>
The consume script receives the SAML assertion. Again, you need to know the identity provider to which the user belongs, but now you get a clue, since the username or email address in the SAML assertion - use samlResponse.getNameId() to retrieve it. Next you’ll use this information to retrieve the identity provider information, and after that, you can verify that the SAML assertion is actually from the identity provider configured on the account, as above.
What needs to be configured
In the example above, SAML settings are divided into two parts, the application specific (const_assertion_consumer_service_url, const_issuer, const_name_identifier_format) and the user/account specific (idp_sso_target_url, x509certificate). You’ll need to add your own code here to identify the user or user origin (e.g. by subdomain, ip_address etc.).
The following information needs to be available on the account:
appSettings.setAssertionConsumerServiceUrl
The URL at which the SAML assertion should be received. In this example "http://localhost:3000/saml/consume" would be correct.
appSettings.setIssuer
The name of your application. Some identity providers might need this to establish the identity of the service provider requesting the login.
accSettings.setIdpSsoTargetUrl
The URL to which the authentication request should be sent. This would be on the identity provider.
accountSettings.setCertificate
The x509 certificate fingerprint. This is provided from the identity provider when setting up the relationship.