I came across an issue the other day where my URL with inline authentication tokens was giving me a 401 Unauthorized error. For the unfamiliar, URL authentication isn’t very heavily used, but it works like this:
That username:password bit is known in Java as the User Info string, and Java handles it differently than web browsers do.
If you enter a URL with an authentication token in most modern browsers, the browser will add a Basic Authentication header to the request, which does all the heavy lifting. This header just contains a Base64-encoded version of the User Info string. For some reason Java doesn’t do this, so we have to generate the header ourselves:
URI uri = new URL("http://username:email@example.com/resource").toURI(); String userInfo = uri.getRawUserInfo(); if(userInfo != null && userInfo.length() > 0) userInfo = Base64.getEncoder().encodeToString(userInfo.getBytes()); HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection(); if(userInfo != null && userInfo.length() > 0) connection.setRequestProperty("Authorization", "Basic " + userInfo); connection.connect();
A few notes on the code:
- Using URL#getUserInfo() or URI#getUserInfo() may result in special characters being incorrectly encoded, but my tests are far from exhaustive.
URI#getRawUserInfo()seems like the safest method.
- Because of the if statement, it’s fine to leave this in whatever authentication class/method is at work, since if it’s passed a URL without authentication tokens, it will simply ignore it.
- Many servers will ignore the in-URL tokens and just handle the Basic Authentication header, but it doesn’t hurt to leave the User Info string in.
I recently answered this StackOverflow question regarding the Shopify API, which uses URL authentication in queries. It’s an issue that comes up once in a blue moon, so I figured sharing it here would be a good idea since it took me forever to figure out what the issue was.