You are here

How to sign POST requests with OAuth 1.0 using oauth-signpost and HttpURLConnection

Not so frequently asked questions and stuff: 

Situation

Signpost's documentation states that the library cannot be used along with HttpURLConnection to sign standard POST requests. This is not entirely true.

Method setAdditionalParameters() can be used to add a few parameters to the OAuth base string. The trick is to add them already percent-encoded.

Code sample: signing standard URL-encoded POST requests

package test_oauth;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.Scanner;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.http.HttpParameters;

public class TestOAuth {

 public static void main(String[] args) throws IOException, OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException {
      final String endpointUrl = "http://some-api.example.org/index.php/api/v1/end/point";

        //Create an HttpURLConnection and add some headers
      URL url = new URL(endpointUrl);
     HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
     urlConnection.setRequestProperty("Accept", "application/json");
     urlConnection.setRequestMethod("POST");
     urlConnection.setDoOutput(true);

        //Build the list of parameters
      HttpParameters params =  new HttpParameters();
      params.put("param1", "49607,49608,49609");
      params.put("param2", "1396430420");
     params.put("param3", "coucou");
     
        //Sign the request
      //The key to signing the POST fields is to add them as additional parameters, but already percent-encoded; and also to add the realm header.
        OAuthConsumer dealabsConsumer = new DefaultOAuthConsumer ("5314c6fbe7437", "5314c6fbe7bfd");
        HttpParameters doubleEncodedParams =  new HttpParameters();
        Iterator iter = params.keySet().iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            doubleEncodedParams.put(key, OAuth.percentEncode(params.getFirst(key)));
        }
        doubleEncodedParams.put("realm", endpointUrl);
      dealabsConsumer.setAdditionalParameters(doubleEncodedParams);
        dealabsConsumer.sign(urlConnection);
        
        //Create the POST payload
        StringBuilder sb = new StringBuilder();
        iter = params.keySet().iterator();
        for (int i = 0; iter.hasNext(); i++) {
            String param = iter.next();
            if (i > 0) {
                sb.append("&");
            }
            sb.append(param);
            sb.append("=");
            sb.append(OAuth.percentEncode(params.getFirst(param)));
        }
        
        //Send the payload to the connection
        String formEncoded = sb.toString();;
        OutputStreamWriter outputStreamWriter = null;
        try {
            outputStreamWriter = new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8");
            outputStreamWriter.write(formEncoded);
        } finally {
            if (outputStreamWriter != null) {
                outputStreamWriter.close();
            }
        }
        
        //Send the request and read the output
       try {
           System.out.println("Response: " + urlConnection.getResponseCode() + " " + urlConnection.getResponseMessage());
          InputStream in = new BufferedInputStream(urlConnection.getInputStream());
           String inputStreamString = new Scanner(in,"UTF-8").useDelimiter("\\A").next();
            System.out.println(inputStreamString);
      }
       finally {
           urlConnection.disconnect();
     }
   }

}

This code was tested on Java 7 on a PC, and on Android.

Code sample: signing a request using a raw JSON (or anything else) payload

Added on 2014-09-10. Asked by someone by email.

package test_oauth;
 
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;
 
import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.http.HttpParameters;
 
public class TestOAuth {
 
    public static void main(String[] args) throws IOException, OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException {
        final String endpointUrl = "http://some-api.example.org//api/v1/give/me/some/json";
 
        final String JSONPayload = "{\"this\": \"is\", \"some\": \"JSON\"}";
        
        //Create an HttpURLConnection and add some headers
        URL url = new URL(endpointUrl);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestProperty("Accept", "application/json");
        urlConnection.setRequestProperty("Content-Type", "application/json; charset=utf8");
        urlConnection.setRequestMethod("POST");
        urlConnection.setDoOutput(true);
        
        //Sign the request
        OAuthConsumer dealabsConsumer = new DefaultOAuthConsumer ("5314c6fbe7437", "5314c6fbe7bfd");
        HttpParameters doubleEncodedParams =  new HttpParameters();
        doubleEncodedParams.put("realm", endpointUrl);
        dealabsConsumer.setAdditionalParameters(doubleEncodedParams);
        dealabsConsumer.sign(urlConnection);

        //Send the payload to the connection
        OutputStreamWriter outputStreamWriter = null;
        try {
            outputStreamWriter = new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8");
            outputStreamWriter.write(JSONPayload);
        } finally {
            if (outputStreamWriter != null) {
                outputStreamWriter.close();
            }
        }
         
        //Send the request and read the output
        try {
            System.out.println("Response: " + urlConnection.getResponseCode() + " " + urlConnection.getResponseMessage());
            InputStream in = new BufferedInputStream(urlConnection.getInputStream());
            String inputStreamString = new Scanner(in,"UTF-8").useDelimiter("\\A").next();
            System.out.println(inputStreamString);
        }
        finally {
            urlConnection.disconnect();
        }
    }
 
}

Code sample: signing a request using a mixed multipart content

Added on 2014-10-23.

When mixing classic text fields and file uploads, the key is to add to the signer only the text fields, and build your multipart request normally.

package test_oauth;
  
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.Scanner;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.http.HttpParameters;
  
public class TestOAuth {
  
    public static void main(String[] args) throws IOException, OAuthMessageSignerException, OAuthExpectationFailedException, OAuthCommunicationException {
     final String endpointUrl = "http://some-api.example.org/upload/some/file/and/information";
  
        final String BOUNDARY = "ThisShouldBeRandomAndComplicated";
        final String BOUNDARY_SPLIT = "--";

        final String SuperfileName = "filename.bin";
        final String SuperfileContent = "This is the content of the file. It can contains anything and can contains anything.";
        final String SuperfileContentType = "application/octet-stream";
         
        //Create an HttpURLConnection and add some headers
        URL url = new URL(endpointUrl);
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestProperty("Accept", "application/json");
        urlConnection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
        urlConnection.setRequestMethod("POST");
        urlConnection.setDoOutput(true);

        //Build the list of parameters
        HttpParameters params =  new HttpParameters();
        params.put("param1", "49607,49608,49609");
        params.put("param2", "1396430420");
        params.put("param3", "coucou");
        
        //Sign the request
        //The key to signing the POST fields is to add them as additional parameters, but already percent-encoded; and also to add the realm header.
        OAuthConsumer dealabsConsumer = new DefaultOAuthConsumer ("5314c6fbe7437", "5314c6fbe7bfd");
        HttpParameters doubleEncodedParams =  new HttpParameters();
        Iterator iter = params.keySet().iterator();
        while (iter.hasNext()) {
            String key = iter.next();
            doubleEncodedParams.put(key, OAuth.percentEncode(params.getFirst(key)));
        }
        doubleEncodedParams.put("realm", endpointUrl);
        dealabsConsumer.setAdditionalParameters(doubleEncodedParams);
        dealabsConsumer.sign(urlConnection);
 
        //Send the payload to the connection
        PrintWriter OutputStream = null;
        try {
            OutputStream = new PrintWriter(new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8"), true);

            //Send the POST variables
            Iterator iterator = params.keySet().iterator();
         while (iterator.hasNext()) {
                String key = iterator.next();
   
                OutputStream.append(BOUNDARY_SPLIT).append(BOUNDARY).append("\r\n");
              OutputStream.append("Content-Disposition: form-data; name=\"").append(key).append("\"").append("\r\n");
             OutputStream.append("Content-Type: ").append("text/plain; charset=UTF-8").append("\r\n").append("\r\n");
                OutputStream.append(params.getFirst(key)).append("\r\n")
              .flush();
           }
           
            //Send the file
         OutputStream.append(BOUNDARY_SPLIT).append(BOUNDARY).append("\r\n");
          OutputStream.append("Content-Disposition: form-data; name=\"upload_field_name\"; filename=\""+SuperfileName+"\"").append("\r\n");
         OutputStream.append("Content-Type: ").append(SuperfileContentType).append("\r\n").append("\r\n");
           OutputStream.append(SuperfileContent).append("\r\n");

         //Add one final boundary
            OutputStream.append(BOUNDARY_SPLIT).append(BOUNDARY).append(BOUNDARY_SPLIT).append("\r\n");
        }
        finally {
            if (OutputStream != null) {
              OutputStream.close();
            }
        }
        
        //Send the request and read the output
        try {
            System.out.println("Response: " + urlConnection.getResponseCode() + " " + urlConnection.getResponseMessage());
            InputStream in = new BufferedInputStream(urlConnection.getInputStream());
            String inputStreamString = new Scanner(in,"UTF-8").useDelimiter("\\A").next();
            System.out.println(inputStreamString);
        }
        finally {
            urlConnection.disconnect();
        }
    }
  
}