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:
https://username:password@protected.domain.example/resource
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:password@protected.domain.example/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.