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.