Thursday, January 20, 2011

Redirect with AuthnRequest

This blog post is an example of the sign in procedure of the SAML Web Browser SSO Profile.

The sign on starts with a redirect from the SP(The one wanting to authenticate someone) to the IDP(The one authenticating).

To identify who the SP is, the SP sends a SAML AuthnRequest as parameter in the redirect.
Here is an example how to do this with OpenSAML.

The example uses the HTTPRedirectDeflateEncoder to encode, sign and redirect which makes everything way easier, especially considering encoding and signing the AuthnRequest.

//IPR Ergogroup AS
public void doAuthenticationRedirect(final HttpServletResponse response, final HttpSession currentSession, final String gotoURL, final SAMLMetaData metaData) throws IllegalArgumentException, SecurityException, IllegalAccessException {
  AuthnRequest authnRequest = generateAuthnRequest(metaData);

  SAMLUtil.logSAMLObject(authnRequest);

  // Save the request ID to session for future validation
  currentSession.setAttribute("AuthnRequestID", authnRequest.getID());
  currentSession.setAttribute("goto", gotoURL);

  HttpServletResponseAdapter responseAdapter = new HttpServletResponseAdapter(response, true);
  BasicSAMLMessageContext<SAMLObject, AuthnRequest, SAMLObject> context = new BasicSAMLMessageContext<SAMLObject, AuthnRequest, SAMLObject>();  
  context.setPeerEntityEndpoint(getEndpointFromMetaData());
  context.setOutboundSAMLMessage(authnRequest);
  context.setOutboundSAMLMessageSigningCredential(getSigningCredential());
  context.setOutboundMessageTransport(responseAdapter);

  HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder();

  try {
   encoder.encode(context);
  } catch (MessageEncodingException e) {
   logger.error(e.getMessage(), e);
  }
 }

 private AuthnRequest generateAuthnRequest(final SAMLMetaData metaData) throws IllegalArgumentException, SecurityException, IllegalAccessException {

  AuthnRequest authnRequest = SAMLUtil.buildSAMLObjectWithDefaultName(AuthnRequest.class);

  authnRequest.setForceAuthn(true);
  authnRequest.setIsPassive(false);
  authnRequest.setIssueInstant(new DateTime());
  for (SingleSignOnService sss : metaData.getIdpEntityDescriptor().getIDPSSODescriptor(SAMLConstants.SAML20P_NS).getSingleSignOnServices()) {
   if (sss.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) {
    authnRequest.setDestination(sss.getLocation());
   }
  }
  authnRequest.setProtocolBinding(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);

  String deployURL = getDeployURL();
  if (deployURL.charAt(deployURL.length() - 1) == '/') {
   deployURL = deployURL.substring(0, deployURL.length() - 1);
  }
  authnRequest.setAssertionConsumerServiceURL(deployURL + SAMLMetaData.CONSUMER_PATH);

  authnRequest.setID(SAMLUtil.getSecureRandomIdentifier());

  Issuer issuer = SAMLUtil.buildSAMLObjectWithDefaultName(Issuer.class);
  issuer.setValue(getSPEntityId());
  authnRequest.setIssuer(issuer);

  NameIDPolicy nameIDPolicy = SAMLUtil.buildSAMLObjectWithDefaultName(NameIDPolicy.class);
  nameIDPolicy.setSPNameQualifier(getSPEntityId());
  nameIDPolicy.setAllowCreate(true);
  nameIDPolicy.setFormat("urn:oasis:names:tc:SAML:2.0:nameid-format:transient");

  authnRequest.setNameIDPolicy(nameIDPolicy);

  RequestedAuthnContext requestedAuthnContext = SAMLUtil.buildSAMLObjectWithDefaultName(RequestedAuthnContext.class);
  requestedAuthnContext.setComparison(AuthnContextComparisonTypeEnumeration.MINIMUM);

  AuthnContextClassRef authnContextClassRef = SAMLUtil.buildSAMLObjectWithDefaultName(AuthnContextClassRef.class);
  authnContextClassRef.setAuthnContextClassRef("urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport");

  requestedAuthnContext.getAuthnContextClassRefs().add(authnContextClassRef);
  authnRequest.setRequestedAuthnContext(requestedAuthnContext);

  return authnRequest;
 }

Resulting AuthnRequest

my-alias


urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport



The signature is put in the URL with the request.

Further reading

This is not a book specifically on OpenSAML but I do recommend it as i does have some chapters on SAML in general.

15 comments:

  1. Interesting stuff but what is the SAMLUtil and SAMLMetaData classes? If they are yours can you post them?

    ReplyDelete
  2. The SAMLMetaData is just a class I use to store my EntityDecriptor for the IDP. It also contains a constant pointing to the URL of my consumer servlet. The SAMLUtil is a collection of static utility methods. I will explain som convieniex methods in post later today

    ReplyDelete
  3. http://mylifewithjava.blogspot.com/2011/04/convenience-methods-for-opensaml.html

    ReplyDelete
    Replies
    1. Hi can u please share Samlutil class

      Delete
    2. The methods are here http://mylifewithjava.blogspot.no/2011/04/convenience-methods-for-opensaml.html

      Delete
    3. Not all of them are there. Do you have a GitHub or something, where you place your code? Also, the code is all messed up around the generic types, your editor must have confused it with HTML tags

      Delete
  4. What do you mean when you say signature is put in URL with the request?
    We have a SP initiated Web SSO and a spring mvc application on the server side,we create authrequest and set it in the request parameter as SAMLRequest. We then a do forward to a jsp page which has a form that submits the form to the IDP as the HTTP POST.My question is does HTTPRedirectDeflateEncoder allows to just get the encoded,signed authn request? My challenge has been to sign the authnrequest? Any inputs are highly appreciated.

    ReplyDelete
    Replies
    1. The HTTPRedirectDeflateEncoder sign deflates and redirects the request. I puts both the SAML request and the signature in the URL as URL parameters. Not sure what your asking. Do you want to sign is without redirecting it?

      Delete
  5. Where do you specify the relaystate?

    ReplyDelete
  6. How final redirect URI looks like?
    Can you provide more details about what does the code below does.

    HTTPRedirectDeflateEncoder encoder = new HTTPRedirectDeflateEncoder();
    encoder.encode(context);

    ReplyDelete
    Replies
    1. It encodes the context as an URL. In this case it uses the PeerEntityEndpoint as host in the URL, uses DEFLATE to encode the OutboundSAMLMessage and puts it in a URL paramter, signs the samlmessage with OutboundSAMLMessageSigningCredential and puts it as a URL parameter.

      Delete
  7. Hi,

    I tried to implement the same on a SAML Authn Reques but the encode method is returning NullPointerException on removeSignature method call. The stacktrace is as below

    I am not signing the authn request so how can I avoid calling this method context.setOutboundSAMLMessageSigningCredential(getSigningCredential());

    Thanks in advance
    Rajesh

    java.lang.NullPointerException
    [5/22/13 15:30:49:521 IST] 00000023 SystemErr R at org.opensaml.saml2.binding.encoding.HTTPRedirectDeflateEncoder.removeSignature(HTTPRedirectDeflateEncoder.java:119)
    [5/22/13 15:30:49:521 IST] 00000023 SystemErr R at org.opensaml.saml2.binding.encoding.HTTPRedirectDeflateEncoder.doEncode(HTTPRedirectDeflateEncoder.java:99)
    [5/22/13 15:30:49:521 IST] 00000023 SystemErr R at org.opensaml.ws.message.encoder.BaseMessageEncoder.encode(BaseMessageEncoder.java:51)

    ReplyDelete
    Replies
    1. I dont understand, if you want to avoid to call context.setOutboundSAMLMessageSigningCredential(getSigningCredential()), just dont do it. Delete the line

      Delete