package org.pac4j.oauth.profile.facebook;
import com.github.scribejava.core.builder.api.DefaultApi20;
import com.github.scribejava.core.exceptions.OAuthException;
import com.github.scribejava.core.model.*;
import org.pac4j.core.exception.HttpAction;
import org.pac4j.core.exception.HttpCommunicationException;
import org.pac4j.core.util.CommonHelper;
import org.pac4j.oauth.client.FacebookClient;
import org.pac4j.oauth.config.OAuth20Configuration;
import org.pac4j.oauth.profile.creator.OAuth20ProfileCreator;
import org.pac4j.oauth.profile.definition.OAuth20ProfileDefinition;
import java.io.IOException;
/**
* A specific Facebook profile creator.
*
* @author Jerome Leleu
* @since 2.0.0
*/
public class FacebookProfileCreator extends OAuth20ProfileCreator<FacebookProfile> {
private static final String EXCHANGE_TOKEN_URL = "https://graph.facebook.com/v2.8/oauth/access_token?grant_type=fb_exchange_token";
private static final String EXCHANGE_TOKEN_PARAMETER = "fb_exchange_token";
public FacebookProfileCreator(final OAuth20Configuration configuration) {
super(configuration);
}
@Override
protected FacebookProfile retrieveUserProfileFromToken(final OAuth2AccessToken accessToken) throws HttpAction {
final OAuth20ProfileDefinition<FacebookProfile> profileDefinition = (OAuth20ProfileDefinition<FacebookProfile>) configuration.getProfileDefinition();
final FacebookClient client = (FacebookClient) configuration.getClient();
final String profileUrl = profileDefinition.getProfileUrl(accessToken, configuration);
String body = sendRequestForData(accessToken, profileUrl, Verb.GET);
if (body == null) {
throw new HttpCommunicationException("Not data found for accessToken: " + accessToken);
}
final FacebookProfile profile = profileDefinition.extractUserProfile(body);
addAccessTokenToProfile(profile, accessToken);
if (profile != null && client.isRequiresExtendedToken()) {
String url = CommonHelper.addParameter(EXCHANGE_TOKEN_URL, OAuthConstants.CLIENT_ID, configuration.getKey());
url = CommonHelper.addParameter(url, OAuthConstants.CLIENT_SECRET, configuration.getSecret());
url = addExchangeToken(url, accessToken);
final OAuthRequest request = createOAuthRequest(url, Verb.GET);
final long t0 = System.currentTimeMillis();
final Response response = request.send();
final int code = response.getCode();
try {
body = response.getBody();
} catch (IOException ex) {
throw new HttpCommunicationException("Error getting body:" + ex.getMessage());
}
final long t1 = System.currentTimeMillis();
logger.debug("Request took: " + (t1 - t0) + " ms for: " + url);
logger.debug("response code: {} / response body: {}", code, body);
if (code == 200) {
logger.debug("Retrieve extended token from {}", body);
final OAuth2AccessToken extendedAccessToken;
try {
extendedAccessToken = ((DefaultApi20) configuration.getApi()).getAccessTokenExtractor().extract(response);
} catch (IOException | OAuthException ex) {
throw new HttpCommunicationException("Error extracting token: " + ex.getMessage());
}
logger.debug("Extended token: {}", extendedAccessToken);
addAccessTokenToProfile(profile, extendedAccessToken);
} else {
logger.error("Cannot get extended token: {} / {}", code, body);
}
}
return profile;
}
/**
* Adds the token to the URL in question. If we require appsecret_proof, then this method
* will also add the appsecret_proof parameter to the URL, as Facebook expects.
*
* @param url the URL to modify
* @param accessToken the token we're passing back and forth
* @return url with additional parameter(s)
*/
protected String addExchangeToken(final String url, final OAuth2AccessToken accessToken) {
final FacebookProfileDefinition profileDefinition = (FacebookProfileDefinition) configuration.getProfileDefinition();
final FacebookClient client = (FacebookClient) configuration.getClient();
String computedUrl = url;
if (client.getUseAppSecretProof()) {
computedUrl = profileDefinition.computeAppSecretProof(computedUrl, accessToken, configuration);
}
return CommonHelper.addParameter(computedUrl, EXCHANGE_TOKEN_PARAMETER, accessToken.getAccessToken());
}
}