Go to content

Retrieve Facebook profile data in Java: Apache Oltu

Published on

In previous blogposts I explained how you can get Facebook profile data using Scribe or Spring Social for the OAuth 2 handling. In this article you can read how to get the profile using Apache Oltu (formerly known as Apache Amber).

Update: browsing through the source of Apache Oltu I found an enum for the provider endpoints which wasn't mentioned in the documentation.

Setup

The Maven dependency for the Apache Oltu OAuth client:

<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.client</artifactId>
<version>0.31</version>
</dependency>

The setup for the controller:

@Controller
public class FacebookOltuAuthenticator {

private final String clientId;
private final String clientSecret;
private final String applicationHost;
private final ObjectMapper objectMapper;

@Autowired
public FacebookOltuAuthenticator(
@Value("#{properties['facebook.clientId']}")
String clientId,
@Value("#{properties['facebook.clientSecret']}")
String clientSecret,
@Value("#{properties['application.host']}")
String applicationHost) {
this.clientId = clientId;
this.clientSecret = clientSecret;
this.applicationHost = applicationHost;
this.objectMapper = new ObjectMapper();
this.objectMapper.registerModule(new AfterburnerModule());
}
}

Start the authentication

Scribe and Spring Social have classes that know the endpoint for the authorization. Apache Oltu has an enum OAuthProviderType for authorization and token endpoints of common OAuth 2 providers.

@RequestMapping("/auth/facebook")
public RedirectView startAuthentication(HttpSession session)
throws OAuthSystemException {
String state = UUID.randomUUID().toString();
session.setAttribute(OAuth.OAUTH_STATE, state);
OAuthClientRequest oAuthClientRequest = OAuthClientRequest
.authorizationProvider(OAuthProviderType.FACEBOOK)
.setClientId(clientId)
.setRedirectURI(applicationHost + "/auth/facebook/callback")
.setParameter(OAuth.OAUTH_STATE, state)
.buildQueryMessage();

return new RedirectView(oAuthClientRequest.getLocationUri());
}

Handle the callback

Apache Oltu has a helper class to get the authorization response from the HttpServletRequest, which means we don't have to define the code and state parameters in the method signature.

@RequestMapping("/auth/facebook/callback")
public RedirectView callback(HttpServletRequest request, HttpSession session)
throws IOException, OAuthSystemException, OAuthProblemException {
String stateFromSession = (String) session.getAttribute(OAuth.OAUTH_STATE);
session.removeAttribute(OAuth.OAUTH_STATE);

OAuthAuthzResponse oar = OAuthAuthzResponse.oauthCodeAuthzResponse(request);
String stateResponse = oar.getState();

if (StringUtils.isBlank(stateResponse) ||
!stateResponse.equals(stateFromSession)) {
return new RedirectView("/login");
}

OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient());
String accessToken = getAccessToken(oar, oAuthClient);

OAuthResourceResponse resourceResponse =
getFacebookProfileResponse(oAuthClient, accessToken);
if (resourceResponse.getResponseCode() != HttpServletResponse.SC_OK) {
return new RedirectView("/login");
}

String facebookUserId = getFacebookUserId(resourceResponse);
session.setAttribute("facebookUserId", facebookUserId);

return new RedirectView("/logged-in");
}

Retrieve the AccessToken

Apache Oltu has two different classes to parse the access token response. Facebook used to return a response that was not compliant to the final version of the OAuth 2 specification. Now it does comply to the specification. The compliant class to parse the response is OAuthJSONAccessTokenResponse.

private String getAccessToken(OAuthAuthzResponse oar, OAuthClient oAuthClient)
throws OAuthSystemException, OAuthProblemException {
String code = oar.getCode();
OAuthClientRequest oAuthClientRequest = OAuthClientRequest
.tokenProvider(OAuthProviderType.FACEBOOK)
.setGrantType(GrantType.AUTHORIZATION_CODE)
.setClientId(clientId)
.setClientSecret(clientSecret)
.setRedirectURI(applicationHost + "/auth/facebook/callback")
.setCode(code)
.buildQueryMessage();

OAuthJSONAccessTokenResponse oAuthResponse = oAuthClient.accessToken(
oAuthClientRequest, OAuthJSONAccessTokenResponse.class);
return oAuthResponse.getAccessToken();
}

Get the Facebook profile data

First we get the profile as JSON:

private OAuthResourceResponse getFacebookProfileResponse(
OAuthClient oAuthClient, String accessToken)
throws OAuthSystemException, OAuthProblemException {
OAuthClientRequest bearerClientRequest =
new OAuthBearerClientRequest("https://graph.facebook.com/me")
.setAccessToken(accessToken).buildQueryMessage();

return oAuthClient.resource(bearerClientRequest, OAuth.HttpMethod.GET,
OAuthResourceResponse.class);
}

Just like we did with Scribe, we use Jackson to extract the user id from the JSON response:

private String getFacebookUserId(OAuthResourceResponse resourceResponse) 
throws IOException {
String resourceResponseBody = resourceResponse.getBody();
JsonNode jsonNode = objectMapper.readTree(resourceResponseBody);
JsonNode idNode = jsonNode.get("id");
return idNode.asText();
}

Conclusion

As you can see, more knowledge of the OAuth 2 flow is needed to make this implementation with Apache Oltu. On the other hand it does help you understand better how the flow works. When you have implemented it once, you can add any other OAuth 2 provider by changing endpoints and the way the profile is parsed from the JSON response.