I’ve started developing a text-only client for Twitter. The impulse came from getting fed up with all the auto-playing promoted tweets that were clogging my timeline. I looked around and found a library to handle all the Twitter API calls and started coding. The example code was all a bit bare-bones, but mostly it was straightforward. The one thing so far that I think could use a bit more explanation is the login process, and that’s the point of this post.
There are tons of tutorials for how to do OAuth in a web app, but there are precious few that deal with doing authentication from within a desktop application without resorting to switching out to a web browser. This method gets around that problem by using the built-in web browser component in JavaFX, the WebView.
First, I created a simple FXML file with a GridPane that contains a WebView inside a ScrollPane, a TextField, and a Button. The WebView is for displaying the Twitter OAuth screens.
In the controller class, I initialize the WebView with the request for access:
@Override public void initialize(URL location, ResourceBundle resources) { try { // get the initial OAuth request token _twitter = TwitterFactory.getSingleton(); _requestToken = _twitter.getOAuthRequestToken(); // fetch the auth URL (in a WebView, to let the user login and then grant authorization to the app) _view.getEngine().load(_requestToken.getAuthorizationURL()); } catch (Exception e) { __l.error("Exception trying to perform OAuth login", e); } }
It turns out that when the user grants access, Twitter displays a multi-digit number. What needs to happen is, the user copies the number into the TextField and clicks the Ok button. Here’s that handler:
@FXML @SuppressWarnings("unused") public void handleOK(ActionEvent evt) { String oauthPIN = _pin.getText(); AccessToken accessToken; try { if (!oauthPIN.isEmpty()) { accessToken = _twitter.getOAuthAccessToken(_requestToken, oauthPIN); } else { accessToken = _twitter.getOAuthAccessToken(); } // now we have an AccessToken, we send it to the callback for application use and storage _tokenConsumer.accept(accessToken, _stage); } catch (TwitterException te) { if (401 == te.getStatusCode()) { __l.error("Unable to get the access token.", te); } else { __l.error("Exception getting access token", te); } } }
The callback gets registered during setup; it’s a method on the main window that accepts the AccessToken and persists it into the app’s preferences, and closes the login window.