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
FacebookAuthToken
object - 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);
}
}