This class exposes the public method getAuthToken which takes a user's Facebook OAuth token as an input parameter and queries Facebook for information about it - such as the application it is valid for, its expiry date, the user it belongs to etc.
The flow for this method is as follows:
- Uses the token passed into the method to construct a URL to be read from
- Reads the JSON response from Facebook into a String
- Deserializes this String into a new
FacebookAuthTokenobject - Returns this object
Once this object is returned it is possible to use the various public getter methods I have provided to query its attributes.
The main thing I don't like about this solution is that it uses two top-level classes - the AuthTokenDeserializer class as well as the primary FacebookAuthToken class. I tried to nest AuthTokenDeserializer within FacebookAuthToken but the compiler complained about trying to create a new AuthTokenDeserializer from within getAuthToken, which is a static context. Fair enough, I suppose.
Additionally I am not over the moon that getAuthToken throws an IOException - because now every invocation of this method within my application has to catch it. Perhaps it is better to catch this exception within the method and simply return null?
Apart from these issues, are there any other inefficiencies or elements of poor design in my code? I am new to Java so try to challenge everything I do (so that I get better).
class FacebookAuthToken {
private static final String APP_ID = "99999999999999";
// TODO: Refresh APP_TOKEN before Go-Live
private static final String APP_TOKEN = "999999999999";
private static final String URL_PLACEHOLDER =
"https://graph.facebook.com/debug_token?input_token=userToken&access_token=accessToken";
private static final String ACCESS_TOKEN = APP_ID + "|" + APP_TOKEN;
private String app_id;
private String application;
private int expires_at;
private boolean is_valid;
private int issued_at;
private String[] scopes;
private String user_id;
public static FacebookAuthToken getAuthToken(String userToken) throws IOException {
URL url = new URL(buildAuthTokenURL(userToken));
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
StringBuilder builder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
builder.append(line).append("\n");
}
reader.close();
String myJson = builder.toString();
Gson gson = new GsonBuilder().registerTypeAdapter(FacebookAuthToken.class,
new AuthTokenDeserializer()).create();
FacebookAuthToken authToken = gson.fromJson(myJson, FacebookAuthToken.class);
return authToken;
}
public String getAppId() {
return this.app_id;
}
public String getApp() {
return this.application;
}
public int getExpiry() {
return this.expires_at;
}
public boolean isValid() {
return this.is_valid;
}
public int getCreated() {
return this.issued_at;
}
public String[] getPermissions() {
return this.scopes;
}
public String getUserID() {
return this.user_id;
}
private static String buildAuthTokenURL(String userToken) {
String result = URL_PLACEHOLDER.replaceAll("userToken", userToken);
return result.replaceAll("accessToken", ACCESS_TOKEN);
}
}
/* Example response to be deserialized by AuthTokenDeserializer:
{
"data": {
"app_id": "999999999999",
"application": "MyApp",
"expires_at": 1455462874,
"is_valid": true,
"issued_at": 1450278874,
"scopes": [
"public_profile"
],
"user_id": "999999999999999"
}
}
*/
class AuthTokenDeserializer implements JsonDeserializer<FacebookAuthToken> {
@Override
public FacebookAuthToken deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext context) {
JsonElement data = jsonElement.getAsJsonObject().get("data");
return new Gson().fromJson(data, FacebookAuthToken.class);
}
}