[OpenSAML] HttpPostEncoder and HttpPostDecoder

2,035 views
Skip to first unread message

rangeli nepal

unread,
May 16, 2010, 11:22:22 PM5/16/10
to mace-open...@internet2.edu
Hello Everybody,

In order to test Post Binding I wrote a small test program using HttpPostEncoder. In oder to decode on server end I am using HttpPostDecoder. However I get following error:

23:15:22,938 DEBUG [Configuration] VM using JAXP parser org.apache.xerces.jaxp.DocumentBuilderFactoryImpl
23:15:22,949 DEBUG [BaseMessageDecoder] Beginning to decode message from inbound transport of type: org.opensaml.ws.transport.http.HttpServletRequestAdapter
23:15:22,950 DEBUG [HTTPPostDecoder] Decoded SAML relay state of: null
23:15:22,950 DEBUG [HTTPPostDecoder] Getting Base64 encoded message from request
23:15:22,950 ERROR [HTTPPostDecoder] Request did not contain either a SAMLRequest or SAMLResponse paramter.  Invalid request for SAML 2 HTTP POST binding.
23:15:22,952 ERROR [STDERR] org.opensaml.ws.message.decoder.MessageDecodingException: No SAML message present in request

am I doing something wrong.? Your input will be highly appreciated.
Thank you.
rn


Encoding end looks like following:

public FooHTTPPostEncoder( String templateId,String key,String cert, HttpServletResponse response ) throws Exception
{
   velocityEngine = new VelocityEngine();
       velocityEngine.setProperty(RuntimeConstants.ENCODING_DEFAULT, "UTF-8");
       velocityEngine.setProperty(RuntimeConstants.OUTPUT_ENCODING, "UTF-8");
       velocityEngine.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
       velocityEngine.setProperty("classpath.resource.loader.class",
               "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
       velocityEngine.init();
       this.setTemplateString(templateId);
   
   ................................
        ............................
                < build AuthnRequest using available builders>
 
        authnRequest=samlAuthnRequest.buildAuthnRequest();
 this.basicSAMLMessageContext=new BasicSAMLMessageContext();
 Endpoint endPoint=samlAuthnRequest.getEndPoint(this.actionURL);
 
 this.basicSAMLMessageContext.setPeerEntityEndpoint(endPoint);
 this.basicSAMLMessageContext.setOutboundSAMLMessage(authnRequest);
  
 HttpServletResponseAdapter adapter = new HttpServletResponseAdapter(response,false);
 this.basicSAMLMessageContext.setOutboundMessageTransport(adapter);
 this.basicSAMLMessageContext.setOutboundSAMLMessageSigningCredential(this.samlAuthnRequest.getSamlCommon().getCredential());
       
}
     public String getTemplateString()
{
return templateString;
}
public void setTemplateString( String templateString)
{
this.templateString = templateString;
}
public void encode() throws MessageEncodingException{
     HTTPPostEncoder encoder = new HTTPPostEncoder(velocityEngine,
         this.templateString);
         encoder.encode(this.basicSAMLMessageContext);
     }

Decoding:

                HTTPPostDecoder decode = new HTTPPostDecoder( new BasicParserPool() );
       HttpServletRequestAdapter adapter = new HttpServletRequestAdapter(request);
       context = new BasicSAMLMessageContext();
        context.setInboundMessageTransport(adapter);
                 decode.decode(context);
     
      samlRequest = (AuthnRequest) context.getInboundMessage();

Brent Putman

unread,
May 17, 2010, 5:31:54 PM5/17/10
to mace-open...@internet2.edu


On 5/16/10 11:22 PM, rangeli nepal wrote:
>>
>
> 23:15:22,950 DEBUG [HTTPPostDecoder] Getting Base64 encoded message from
> request
> 23:15:22,950 ERROR [HTTPPostDecoder] Request did not contain either a
> SAMLRequest or SAMLResponse paramter. Invalid request for SAML 2 HTTP
> POST binding.
> 23:15:22,952 ERROR [STDERR]
> org.opensaml.ws.message.decoder.MessageDecodingException: No SAML
> message present in request
>
> am I doing something wrong.? Your input will be highly appreciated.
> Thank you.
> rn
>


I don't see anything immediately wrong in the code, but the basic
problem is that the form that's being submitted to the decoder doesn't
contain the field for the SAML message. What template are you using in
the encoder? The one shipped in OpenSAML would be a template ID of
"/templates/saml2-post-binding.vm", using the classpath resoure loader.

To really diagnose it, just turn off Javascript in your browser so that
the form doesn't auto-submit, then take a look at the form with your
browser's view source. That will show you what the encoder is actually
generating and should give you an idea of what is wrong.





Brent Putman

unread,
May 17, 2010, 5:35:24 PM5/17/10
to mace-open...@internet2.edu

> What template are you using in
> the encoder? The one shipped in OpenSAML would be a template ID of
> "/templates/saml2-post-binding.vm", using the classpath resoure loader.
>


Which btw looks like this:


http://svn.middleware.georgetown.edu/view/java-opensaml2/branches/REL_2/src/main/resources/templates/saml2-post-binding.vm?revision=1303&view=markup


You can use your own Velocity template if you like, but it must contain
the Velocity variables named like so:



## action - String - the action URL for the form
## RelayState - String - the relay state for the message
## SAMLRequest - String - the Base64 encoded SAML Request
## SAMLResponse - String - the Base64 encoded SAML Response



rangeli nepal

unread,
May 17, 2010, 5:43:45 PM5/17/10
to mace-open...@internet2.edu
 When Message is sent as httppost it does URL encoding. so if I do url decode  and then feed to base64 decoding It starts to work.

Brent Putman

unread,
May 17, 2010, 6:13:11 PM5/17/10
to mace-open...@internet2.edu


On 5/17/10 5:43 PM, rangeli nepal wrote:
> When Message is sent as httppost it does URL encoding. so if I do url
> decode and then feed to base64 decoding It starts to work.
>


Based on the error msg from the decoder, the problem is that there is
literally no HTTP form parameter named either 'SAMLRequest' or
'SAMLResponse'.

The code that performs this check is pretty simple:

String encodedMessage = transport.getParameterValue("SAMLRequest");
if (DatatypeHelper.isEmpty(encodedMessage)) {
encodedMessage = transport.getParameterValue("SAMLResponse");
}

if (DatatypeHelper.isEmpty(encodedMessage)) {
log.error("Request did not contain either a SAMLRequest or "
+ "SAMLResponse paramter. Invalid request for SAML 2 HTTP
POST binding.");
throw new MessageDecodingException("No SAML message present in
request");
}





So it sounds like something about the form as produced by the encoder
and posted to the decoder endpoint is screwed up. What does the encoded
form look like?

This encoder/decoder code is known to work correctly, so something must
be off in the variable inputs that you are supplying on one side or the
other.


rangeli nepal

unread,
May 17, 2010, 8:37:41 PM5/17/10
to mace-open...@internet2.edu
I was under similar impression. But two things proved me wrong:
 
1. I could see the post data in firefox plugin.
2. If I take Inputstream of httpRequestservlet and convert to string I was able to get SAMLRequest-"....."

Brent Putman

unread,
May 18, 2010, 6:15:04 PM5/18/10
to mace-open...@internet2.edu

On 5/17/10 8:37 PM, rangeli nepal wrote:
> I was under similar impression. But two things proved me wrong:



Well, I don't know what else to suggest to you. The form that you are
posting to the decoder does not contain those HTTP form parameters in
the form expected. Either the form is corrupted or malformed in some
way, or else you are not actually posting what you think you are to the
actual decoder endpoint.

Could there be a redirect in there somewhere such that the form POST is
getting turned into a redirected GET, and losing the form data in the
process?

Or if you constructed your own template (you never answered my original
question) perhaps the form that's being submitted doesn't actually
contain the fields themselves (e.g. perhaps there are multiple <form>
elements and you are submitting the wrong one).



>
> 1. I could see the post data in firefox plugin.
> 2. If I take Inputstream of httpRequestservlet and convert to string I
> was able to get SAMLRequest-"....."
>


I'm assuming that's just a casual typo, but if the form literally has
"SAMLRequest-" (dash on the end) as the hidden <input> name, that is the
source of your problems, b/c that's not the correct field name, it's
"SAMLRequest".

Also, you should be able to validate this on the recipient side (where
the form is posted and the decoder runs) by simply calling:

HttpServletRequest#getParameter("SAMLRequest")

If that returns null, or empty, then that should prove that something is
wrong with the posted data.

rangeli nepal

unread,
May 18, 2010, 9:31:49 PM5/18/10
to mace-open...@internet2.edu
Thank  you Brent for all these ideas. I am using saml2-post-binding.vm that comes with open saml.

I think your suspicions could be true. I am using CXF and restful APIS on top of SAML.
It could be possible that CXF does something that hinders the proper functioning of getParameter function of
HttpServletRequest. I do not have depth in CXF internals but this comments come from that fact that in documentation
of getParameter function itself  has been metioned:

" if the parameter data was sent in the request body, such as occurs with an HTTP POST request,
then reading the body directly via getInputStream() or getReader() can interfere with the execution of this method."

"SAMLRequest-" Dash at the end was my typo.

Is there a reason why url decoding is not done before base64 decoding.? I am under the impression that form posted
data will be url encoded.  I think it is harmless too.

Thanks again for your time
rn

Brent Putman

unread,
May 19, 2010, 4:44:20 PM5/19/10
to mace-open...@internet2.edu


On 5/18/2010 9:31 PM, rangeli nepal wrote:
 I am using saml2-post-binding.vm that comes with open saml.


Ok.  That template is known to work just fine, it's what we use in the Shibboleth IdP.  Just wanted to eliminate something obvious.

 

I think your suspicions could be true. I am using CXF and restful APIS on top of SAML.
It could be possible that CXF does something that hinders the proper functioning of getParameter function of
HttpServletRequest. I do not have depth in CXF internals but this comments come from that fact that in documentation
of getParameter function itself  has been metioned:

" if the parameter data was sent in the request body, such as occurs with an HTTP POST request,
then reading the body directly via getInputStream() or getReader() can interfere with the execution of this method."




Ok, that does make sense.  If CXF or some other framework code is doing things with the HttpServletRequest before you get access to it (e.g. reading from the InputStream or Reader) and pass it to the decoder (through the HttpServletRequestAdapter), that could account for the issue you are seeing.  You could confirm like I suggested previously, by calling getParameter(String) on the HttpServletRequest before you attempt to use the decoder, and see whether it's null or empty.

If that proves to be the case, then offhand I can think of 2 possible solutions (short of just not using the framework in front of the decoder in the first place):

1) it *might* work to call reset() on the HttpServletRequest's InputStream or Reader before you pass it to the decoder.  Some streams and readers support reset(), others don't. And here the InputStream impl is supplied by the container, as a ServletInputStream. Can't hurt to try, but I have no idea whether that will work.  Might also not be portable between servlet containers.

2) Create your own impl of our HTTPInTransport, probably by just extending our HttpServletRequestAdapter, and pass to its constructor both the HttpServletRequest and whatever CXF or framework data structure(s) provide access to the HTTP POST parameters, and then override the transport/adapter methods to pull the HTTP parameters from the latter rather than the servlet request itself.




Is there a reason why url decoding is not done before base64 decoding.? I am under the impression that form posted
data will be url encoded.  I think it is harmless too.


You mean the values of the individual HTTP POST parameters?  IIRC the HttpServletRequest#getParameter(String) automatically URL decodes the parameter value that it returns.  I may be wrong on that, but I had the same question once and looked it up and thought that's what I discovered.



Reply all
Reply to author
Forward
0 new messages