Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Browser Authentication via HttpURLConnection

Currently I am working on the implementation of TMDb API. There is a method called User Authentication. I have successfully implemented the Step 1

Step 1: Generate a Request Token

Start by making an API call to the new token method. This will return a new request token that will be valid for 60 minutes. The request token is not authorized by the user at this stage. Request tokens are API account specific and are the tie between your application and the user in step 2.

For the step 1 I have the following code:

URL url = new URL("http://api.themoviedb.org/3/authentication/token/new?api_key=the_key");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringWriter writer = new StringWriter();
String line;
while ((line = reader.readLine()) != null) {
    writer.write(line);
}
reader.close();
Map<String, List<String>> headerFields = connection.getHeaderFields();
String callBackUrl = null;
for(Map.Entry<String, List<String>> entry : headerFields.entrySet()) {
    if(entry.getKey() != null && entry.getKey().equals("Authentication-Callback")) {
        callBackUrl = entry.getValue().get(0);
    }
}

It is printing the callback url in the console along with the Request Token (if I convert the writer.toString() into Json object).

But the second part is the user authentication by their username and password. The callback url redirects user to the login page of TMDb. I have tested it by copy-pasting the callback url from console to the browser.

The Step 2 states that:

Step 2: Request Authorization From the User

Once you have a valid request token, your application needs to open a web browser and send them to TMDb. The HTTP response when generating a new token will include a Authentication-Callback header that you can easily use for the redirect.

If the user is not logged in to TMDb, they will be redirected to the login page before being asked to grant your application permission to use their account. Once the user has granted your application permission to use their account, the browser-based part of this process is over and you can return them to your application.

Just like the request for a new token, the approved response will include a Authentication-Callback header which again, is a convenient way to redirect your application back to the API and generate the real session id.

Now my question is: if I have the username and password, can I authenticate that user by HttpURLConnection or any other way?

I tried this:

url = new URL(callBackUrl);
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");        
BASE64Encoder encoder = new BASE64Encoder();
String usernamepassword = "myusername" + ":" + "mypassword";
String encodedAuthorization = encoder.encode(usernamepassword.getBytes());
connection.setRequestProperty("Authorization", "Basic "+ encodedAuthorization);
headerFields = connection.getHeaderFields();

for(Map.Entry<String, List<String>> entry : headerFields.entrySet()) {
    System.out.println(entry.getKey() + " : " +entry.getValue());
}

But in console I got:

null : [HTTP/1.1 404 Not Found]
Status : [404 Not Found]
X-Frame-Options : [sameorigin]
Date : [Tue, 28 Feb 2012 08:30:17 GMT]
Vary : [Accept-Encoding]
X-Cascade : [pass]
Content-Length : [7835]
X-XSS-Protection : [1; mode=block]
Set-Cookie : [tmdb.session=BAh7CUkiD3Nlc3Npb25faWQGOgZFRiJFNGRkMjc5ODYwMjJmYWYwZDlmOGE5%0AOTVjY2E0NWFjMzhhYTRiOGFjOGJiYjQ5ZGFhNzExNDdkMGM4MWNhZGUyMEki%0ADWxhbmd1YWdlBjsARkkiB2VuBjsARkkiC2xvY2FsZQY7AEZJIgd1cwY7AEZJ%0AIg5sb2dnZWRfaW4GOwBGRg%3D%3D%0A; path=/; expires=Thu, 29-Mar-2012 08:30:17 GMT; HttpOnly]
Content-Type : [text/html;charset=utf-8]
Connection : [keep-alive]
Server : [nginx]

As you can see:

Status : [404 Not Found]

So the last procedure is not fruitful.

Am I implementing the authentication in a wrong way?

I really appreciate your suggestion.

Thanks in advance.

like image 208
Tapas Bose Avatar asked Feb 28 '12 08:02

Tapas Bose


1 Answers

I'm not familiar with TmDB, but I've read that page on their user authentication process and I think you've misunderstood it.

They specifically say they do not want third-party applications to store a username/password credential, or to pass it in a request ("The benefit to this system is that we're never passing a users username or password through the air or requiring a 3rd party app to store it locally"). The page at callbackUrl is not something you, the third-party app, are supposed to post anything to; it's for human use. The user sees this page, which asks "Do you want to grant access to [name of third-party app]? If so, log in here". Your application doesn't get to control that process; it is intentionally separate from you, so that the user's credentials can never be intercepted or stored by you. Once the user has approved you, you'll be able to get an opaque token (session ID) that you use instead of the credentials.

This is basically the same idea as three-legged OAuth; the main difference is that OAuth requires some extra fields and signature computation, so this is simpler. But it has nothing to do with HTTP basicauth.

I believe what you want to do is this:

  1. Do step 1, just as you're doing. But don't just grab the Authentication-Callback header; also parse the JSON response and get the value of "request_token".

  2. Check whether the user has already authorized you, by calling the new session API, passing your API key again along with the previously acquired "request_token". If you get a successful response with a "session_id", you are already authorized and you can skip the rest of the steps.

  3. Otherwise, redirect the user (or open a browser if you're not already in one) to the URL specified in Authentication-Callback.

  4. Now, since the login/approval process is separate from your app, how do you know when it's finished? The documentation is unclear on this, and doesn't describe any way for you to get a notification about it (or to make TMDb redirect back to your app). It may be that you need to just poll for the result (that is, go back to step 2) at some reasonable interval.

like image 62
Eli Bishop Avatar answered Sep 21 '22 12:09

Eli Bishop