27

How can I specify the username and password for making Basic-Auth requests with App Engine's URLFetch service (in Java)?

It seems I can set HTTP headers:

URL url = new URL("http://www.example.com/comment");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("X-MyApp-Version", "2.7.3");        

What are the appropriate headers for Basic-Auth?

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • 1
    Is this really an App Engine question, then? Just look up the HTTP RFCs to see how to do basic auth (hint - 'Authorization'). – Nick Johnson Aug 27 '09 at 15:54
  • 3
    I was hoping that there might be a convenience wrapper for App Engine similar to Apache HttpClient so that I do not have to set (and base64-encode) the Authorization header manually. – Thilo Aug 27 '09 at 22:38

6 Answers6

30

This is a basic auth header over http:

Authorization: Basic base64 encoded(username:password)

eg:

GET /private/index.html HTTP/1.0
Host: myhost.com
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

You will need to do this:

URL url = new URL("http://www.example.com/comment");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Authorization",
"Basic "+codec.encodeBase64String(("username:password").getBytes());

And to do that you will want to get a base64 codec api, like the Apache Commons Codec

Luke Francl
  • 31,028
  • 18
  • 69
  • 91
Zombies
  • 25,039
  • 43
  • 140
  • 225
  • 1
    This is what I ended up doing. And since I put username:password as a property in appengine-web.xml, I just put the encoded version directly in there, so I do not even need a codec. (You can use the openssl commandline: echo -n "username:password" | openssl enc -base64 ) – Thilo Sep 03 '09 at 03:03
  • +1 this also solved a problem I was about to have with HTTP Authentication. Thanks. Oh, and getBytes() needs another ) to close setRequestProperty. – Chris K Sep 03 '09 at 13:54
14

For those interested in doing this in Python (as I was), the code looks like this:

result = urlfetch.fetch("http://www.example.com/comment",
                        headers={"Authorization": 
                                 "Basic %s" % base64.b64encode("username:pass")})
Luke Francl
  • 31,028
  • 18
  • 69
  • 91
  • I believe the code should be as follows: result = urlfetch.fetch( "http://yourserver.com/page/init.php, headers = { "Authorization": "Basic %s" % base64.encodestring("username:password")[:-1] }); – Matt McCormick Oct 23 '10 at 20:11
  • Updated to use base64.b64encode. I know it works because it's what I was actually using in my real code. – Luke Francl Jan 26 '11 at 01:09
6

You set up an Authenticator before you call openConnection() like this,

Authenticator.setDefault(new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(username, password.toCharArray());
    }
});

Since there is only one global default authenticator, this doesn't really work well when you have multiple users doing the URLFetch in multiple threads. I would use Apache HttpClient if that's the case.

EDIT: I was wrong. App Engine doesn't allow Authenticator. Even if it's allowed, we would have the multi-thread issue with a global authenticator instance. Even though you can't create threads, your requests may still get served in different threads. So we just add the header manually using this function,

import com.google.appengine.repackaged.com.google.common.util.Base64;
    /**
     * Preemptively set the Authorization header to use Basic Auth.
     * @param connection The HTTP connection
     * @param username Username
     * @param password Password
     */
    public static void setBasicAuth(HttpURLConnection connection,
            String username, String password) {
        StringBuilder buf = new StringBuilder(username);
        buf.append(':');
        buf.append(password);
        byte[] bytes = null;
        try {
            bytes = buf.toString().getBytes("ISO-8859-1");
        } catch (java.io.UnsupportedEncodingException uee) {
            assert false;
        }

        String header = "Basic " + Base64.encode(bytes);
        connection.setRequestProperty("Authorization", header);
    }
ZZ Coder
  • 74,484
  • 29
  • 137
  • 169
  • There is only a single thread on App Engine ... (by the way, is there any documentation that this is guaranteed to be the case?) – Thilo Aug 30 '09 at 00:19
  • We had some problem with this, I thought it was threading. I will check with the person who implemented this when I get back to office next week. – ZZ Coder Aug 30 '09 at 01:24
  • Just talked with the guy who ported our app to Google. See my edits. – ZZ Coder Sep 03 '09 at 15:20
4

Using HttpURLConnection gave me some problems (for some reason the server I was trying to connect to didn't accept auth credentials), and finally I realized that it's actually much easier to do using GAE's low-level URLFetch API (com.google.appengine.api.urlfetch) like so:

URL fetchurl = new URL(url);

String nameAndPassword = credentials.get("name")+":"+credentials.get("password");
String authorizationString = "Basic " + Base64.encode(nameAndPassword.getBytes());

HTTPRequest request = new HTTPRequest(fetchurl);
request.addHeader(new HTTPHeader("Authorization", authorizationString));

HTTPResponse response = URLFetchServiceFactory.getURLFetchService().fetch(request);
System.out.println(new String(response.getContent()));

This worked.

Joe McBride
  • 3,789
  • 2
  • 34
  • 38
  • This should be the accepted answer, you can also set the timeout: FetchOptions fetchOptions = FetchOptions.Builder.withDeadline(10); HTTPRequest req = new HTTPRequest( fetchurl, HTTPMethod.GET, fetchOptions); – ZiglioUK May 16 '16 at 02:20
3

There is a wrapper on Apache HttpClient for App Engine

please go through the post http://esxx.blogspot.com/2009/06/using-apaches-httpclient-on-google-app.html

http://peterkenji.blogspot.com/2009/08/using-apache-httpclient-4-with-google.html

Rahul Garg
  • 8,410
  • 8
  • 33
  • 28
1

Note on the first answer: setRequestProperty should get the property name without the colon ("Authorization" rather than "Authorization:").