Hot questions for Spring Social Facebook

Top 10 Java Open Source / Spring / Spring Social Facebook

Question:

Earlier today, the Facebook login flow of our web application stopped working for some users. When we try to fetch the current profile, an error is returned. It claims that the access token we just generated by redirecting the user to the OAuth login flow has been rejected.

The reason given is:

The access token is invalid since the user hasn't engaged the app in longer than 90 days

To me, this makes no sense since we do not store the access token anywhere except for the current session and recreate it every time the user logs in with Facebook.

The stacktrace from Spring Social for the GET /me call looks like this:

ERR c.s.f.v.resource.AuthenticationResource Exception when connecting with Facebook
org.springframework.social.RevokedAuthorizationException: The authorization has been revoked. Reason: The access token is invalid since the user hasn't engaged the app in longer than 90 days.
        at org.springframework.social.facebook.api.impl.FacebookErrorHandler.handleFacebookError(FacebookErrorHandler.java:85)
        at org.springframework.social.facebook.api.impl.FacebookErrorHandler.handleError(FacebookErrorHandler.java:59)
        at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
        at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:775)
        at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:728)
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:702)
        at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:350)
        at org.springframework.social.facebook.api.impl.FacebookTemplate.fetchObject(FacebookTemplate.java:220)
        at org.springframework.social.facebook.api.impl.FacebookTemplate.fetchObject(FacebookTemplate.java:215)

The issue is probably related to changes in the Facebook API, but I do not see how this affects the short lived access tokens we create on every login.


Answer:

I ran into this issue when our integration tests logged in with a test user - the following JSON came back from the Graph API:

{
    "error": {
        "message": "The access token is invalid since the user hasn't engaged the app in longer than 90 days.",
        "type": "OAuthException",
        "code": 190,
        "error_subcode": 493,
        "fbtrace_id": "F/1z2AsTRx8"
    },
    "timestamp_microsecond": "2018-05-30 11:22:01.353949"
}

That was a bigger problem as our test users don't "engage" with the app as such. To fix this I had to:

  • Log into the FB developer site
  • Find the app in question
  • Look under Roles -> Test Users to find the right user
  • Click on the Edit button for the user and then click "Login as this test user"
  • Once I'd logged in, go to Settings -> Apps & Websites
  • Find the App in the "Expired" tab for apps the user had not interacted with for longer than 90 days
  • Click the "View & Edit" button on the expired app
  • Click "Renew Access" in the popup

Once I'd done all those steps my test user (and integration tests) worked again.

Question:

I used version 2.0.3.RELEASE of spring-social-facebook and Facebook app api v2.8. I called Facebook login but returned this message. "(#12) bio field is deprecated for versions v2.8 and higher" How can i fix this?


Answer:

I got the same error, 2.0.3.RELEASE of spring-social-facebook seems to be not compatible with v2.8 Facebook API version (released yesterday). Reading from facebook changelog for the v2.8 (https://developers.facebook.com/docs/apps/changelog):

User Bios - The bio field on the User object is no longer available. If the bio field was set for a person, the value will now be appended to the about field.

I think we have to wait a new release of spring-social-facebook library. In the release 2.0.3 (in the interface org.springframework.social.facebook.api.UserOperations) there is the "bio" field in the PROFILE_FIELDS constant and it is not supported in the v2.8 facebook API version.

UPDATE: I found a workaround in my case:

BEFORE:

Connection<Facebook> connection = facebookConnectionFactory.createConnection(accessGrant);
Facebook facebook = connection.getApi();
User userProfile = facebook.userOperations().getUserProfile();//raises the exception caused by the "bio" field.

AFTER

Connection<Facebook> connection = facebookConnectionFactory.createConnection(accessGrant);
Facebook facebook = connection.getApi();
String [] fields = { "id", "email",  "first_name", "last_name" };
User userProfile = facebook.fetchObject("me", User.class, fields);

Here a complete list of field you could use:

{ "id", "about", "age_range", "birthday", "context", "cover", "currency", "devices", "education", "email", "favorite_athletes", "favorite_teams", "first_name", "gender", "hometown", "inspirational_people", "installed", "install_type", "is_verified", "languages", "last_name", "link", "locale", "location", "meeting_for", "middle_name", "name", "name_format", "political", "quotes", "payment_pricepoints", "relationship_status", "religion", "security_settings", "significant_other", "sports", "test_group", "timezone", "third_party_id", "updated_time", "verified", "video_upload_limits", "viewer_can_send_gift", "website", "work"}

Question:

I am trying to use Spring Social on my application and I noticed while debugging that the original 'OAuth2' state parameter is always null on my app.

See Spring Social source code for org.springframework.social.connect.web.ConnectSupport below:

private void verifyStateParameter(NativeWebRequest request) {
    String state = request.getParameter("state");
    String originalState = extractCachedOAuth2State(request);//Always null...
    if (state == null || !state.equals(originalState)) {
        throw new IllegalStateException("The OAuth2 'state' parameter is missing or doesn't match.");
    }
}

private String extractCachedOAuth2State(WebRequest request) {
    String state = (String) sessionStrategy.getAttribute(request, OAUTH2_STATE_ATTRIBUTE);
    sessionStrategy.removeAttribute(request, OAUTH2_STATE_ATTRIBUTE);
    return state;       
}

Can anyone please help?

edit: I do see the state parameter being passed back by facebook:

Request URL:https://www.facebook.com/v2.5/dialog/oauth?client_id=414113641982912&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Fconnect%2Ffacebook&scope=public_profile&state=0b7a97b5-b8d1-4f97-9b60-e3242c9c7eb9
Request Method:GET
Status Code:302 
Remote Address:179.60.192.36:443

edit 2: By the way, the exception I get is the following:

Exception while handling OAuth2 callback (The OAuth2 'state' parameter is missing or doesn't match.). Redirecting to facebook connection status page.

Answer:

It turned out that the issue was caused by the fact that I was relying on headers - as opposed to cookies - to manage the session.

By commenting out the following spring session configuration bean:

@Bean
public HttpSessionStrategy sessionStrategy(){
    return new HeaderHttpSessionStrategy();
}

The oauth2 state parameter issue was sorted.

P.S. Now I have got to find a way to get Spring Social to work with my current configuration of Spring Session...

Edit: I managed to keep the HeaderHttpSessionStrategy (on the spring session side) and get it to work by implementing my own SessionStrategy (on the spring social side) as follows:

public class CustomSessionStrategy implements SessionStrategy {

    public void setAttribute(RequestAttributes request, String name, Object value) {
        request.setAttribute(name, value, RequestAttributes.SCOPE_SESSION);
    }

    public Object getAttribute(RequestAttributes request, String name) {
        ServletWebRequest servletWebRequest = (ServletWebRequest) request;
        return servletWebRequest.getParameter(name);
    }

    public void removeAttribute(RequestAttributes request, String name) {
        request.removeAttribute(name, RequestAttributes.SCOPE_SESSION);
    }
}

Question:

I have a problem using Spring Social, when I tried to login to Facebook, I get the following error:

org.springframework.social.facebook.api.User["video_upload_limits"]->org.springframework.social.facebook.api.VideoUploadLimits["size"])

com.fasterxml.jackson.databind.JsonMappingException: Numeric value (2505397589) out of range of int

Thanks in advance for any help.

maven dependencies:

Spring 4.2.0.RELEASE

    <!-- Spring Social -->
    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-core</artifactId>
        <version>1.1.2.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-web</artifactId>
        <version>1.1.2.RELEASE</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.social</groupId>
        <artifactId>spring-social-facebook</artifactId>
        <version>2.0.1.RELEASE</version>
    </dependency>

    <!-- jason -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.6.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.6.2</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.6.2</version>
    </dependency>

    ==> same error when version up to 2.7.0-rc1(jackson)

Code :

String authorizationCode = request.getParameter("code");

FacebookConnectionFactory connectionFactory = new FacebookConnectionFactory(this.appId, this.appSecret);

OAuth2Operations oauthOperations = connectionFactory.getOAuthOperations();

AccessGrant accessGrant = oauthOperations.exchangeForAccess(authorizationCode, RequestUtil.getRedirectionURLForSNSSignUp(request, provider), null);

// error line
Connection<Facebook> connection = connectionFactory.createConnection(accessGrant);

User user = connection.getApi().userOperations().getUserProfile();

this full exception:

org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: Numeric value (2505397589) out of range of int
 at [Source: java.io.ByteArrayInputStream@3de52269; line: 1, column: 4064] (through reference chain: org.springframework.social.facebook.api.User["video_upload_limits"]->org.springframework.social.facebook.api.VideoUploadLimits["size"])
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:224)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.read(AbstractJackson2HttpMessageConverter.java:208)
    at org.springframework.web.client.HttpMessageConverterExtractor.extractData(HttpMessageConverterExtractor.java:96)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:594)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:567)
    at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:275)
    at org.springframework.social.facebook.api.impl.FacebookTemplate.fetchObject(FacebookTemplate.java:214)
    at org.springframework.social.facebook.api.impl.FacebookTemplate.fetchObject(FacebookTemplate.java:209)
    at org.springframework.social.facebook.api.impl.UserTemplate.getUserProfile(UserTemplate.java:53)
    at org.springframework.social.facebook.api.impl.UserTemplate.getUserProfile(UserTemplate.java:49)
    at org.springframework.social.facebook.connect.FacebookAdapter.setConnectionValues(FacebookAdapter.java:43)
    at org.springframework.social.facebook.connect.FacebookAdapter.setConnectionValues(FacebookAdapter.java:31)
    at org.springframework.social.connect.support.AbstractConnection.setValues(AbstractConnection.java:175)
    at org.springframework.social.connect.support.AbstractConnection.initKey(AbstractConnection.java:137)
    at org.springframework.social.connect.support.OAuth2Connection.<init>(OAuth2Connection.java:75)
    at org.springframework.social.connect.support.OAuth2ConnectionFactory.createConnection(OAuth2ConnectionFactory.java:93)

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Numeric value (2505397589) out of range of int
 at [Source: java.io.ByteArrayInputStream@3de52269; line: 1, column: 4064] (through reference chain: org.springframework.social.facebook.api.User["video_upload_limits"]->org.springframework.social.facebook.api.VideoUploadLimits["size"])
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:210)
    at com.fasterxml.jackson.databind.JsonMappingException.wrapWithPath(JsonMappingException.java:177)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.wrapAndThrow(BeanDeserializerBase.java:1474)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:463)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:376)
    at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1099)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:294)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:131)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:520)
    at com.fasterxml.jackson.databind.deser.impl.FieldProperty.deserializeAndSet(FieldProperty.java:101)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:256)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:125)
    at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3731)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2808)
    at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.readJavaType(AbstractJackson2HttpMessageConverter.java:221)
    ... 78 more

Caused by: com.fasterxml.jackson.core.JsonParseException: Numeric value (2505397589) out of range of int
 at [Source: java.io.ByteArrayInputStream@3de52269; line: 1, column: 4064]
    at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1581)
    at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:533)
    at com.fasterxml.jackson.core.base.ParserBase.convertNumberToInt(ParserBase.java:890)
    at com.fasterxml.jackson.core.base.ParserBase._parseIntValue(ParserBase.java:828)
    at com.fasterxml.jackson.core.base.ParserBase.getIntValue(ParserBase.java:656)
    at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:307)
    at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:287)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:520)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:461)
    ... 89 more

Answer:

Update

It looks like a new release of spring-social-facebook has been created and is available already on the github page. It should be available through Maven shortly.

<dependency>
    <groupId>org.springframework.social</groupId>
    <artifactId>spring-social-facebook</artifactId>
    <version>2.0.3.RELEASE</version>
</dependency>

Old answer

As long as there is no update of the library, you can also just use reflections and remove the "video_upload_limit" from UserOperations.java. So we do not need to extend/rewrite any code or have to compile our own version of the Spring Social Facebook lib.

Use the following code:

@PostConstruct
private void init() {
    // hack for the facebook login
    try {
        String[] fieldsToMap = {
                "id", "about", "age_range", "address", "bio", "birthday", "context", "cover", "currency", "devices", "education", "email",
                "favorite_athletes", "favorite_teams", "first_name", "gender", "hometown", "inspirational_people", "installed", "install_type",
                "is_verified", "languages", "last_name", "link", "locale", "location", "meeting_for", "middle_name", "name", "name_format",
                "political", "quotes", "payment_pricepoints", "relationship_status", "religion", "security_settings", "significant_other",
                "sports", "test_group", "timezone", "third_party_id", "updated_time", "verified", "viewer_can_send_gift",
                "website", "work"
        };

        Field field = Class.forName("org.springframework.social.facebook.api.UserOperations")
                .getDeclaredField("PROFILE_FIELDS");
        field.setAccessible(true);

        Field modifiers = field.getClass().getDeclaredField("modifiers");
        modifiers.setAccessible(true);
        modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
        field.set(null, fieldsToMap);

    } catch (Exception ex) {
        ex.printStackTrace();
    }
}

In my case I use a configuration class for social media and I have placed a @PostConstruct method with this code in it.

Issue on github

Question:

I'm trying to get user location in Spring Social Facebook:

Facebook fb = ((Facebook) connection.getApi());
Page pg = fb.pageOperations().getPage(fb.userOperations().getUserProfile().getLocation().getId());

The problem is that pg.getLocation() returns null.

I also tried with

fb.fetchObject(fb.userOperations().getUserProfile().getLocation().getId(), org.springframework.social.facebook.api.Location.class)

but it does populate only the name, not country or other fields.

Trying on Graph API Explorer /v2.6/112286918797929?fields=id,name,location returns as expected:

{
  "id": "112286918797929",
  "name": "Sacele",
  "location": {
    "city": "Sacele",
    "country": "Romania",
    "latitude": 45.6167,
    "longitude": 25.6833
  }
}

Is this an issue with Spring Social Facebook?

I'm using Spring Social 1.1.4 and Spring Social Facebook 2.0.3.

I also tried Spring Social for Facebook - get user location, but unfortunately not all the places follow the name convention City, Country and some of them include in the name only the city, not the country too. Plus how can you get the geo-coordinates?


Answer:

I finally got it. First of all it doesn't work with PageOperations, but it works with GraphApi.

And the code is

UserProfile userProfile = fb.userOperations().getUserProfile();
Page pg = fb.fetchObject(userProfile.getLocation().getId(), Page.class, "location");

The trick is specifying the third parameter, which represents the fields (you can specify multiple fields as strings). Now the page is populated and you can get the country like pg.getLocation().getCountry().

Question:

I'm trying to use Facebook sign in as described in https://github.com/spring-guides/gs-accessing-facebook

When I'm trying to create JdbcUsersConnectionRepository, I need spring security in class path. And when I add spring security I receive

"java.lang.IllegalStateException: Unable to get a ConnectionRepository: no user signed in"

when trying to receive Connection

Connection<Facebook> connection = connectionRepository.findPrimaryConnection(Facebook.class);

or checking

if (!facebook.isAuthorized())

All this happens only when spring security is in the class path


Answer:

Social Connection should be correspond to any auth user. It seems user should login through username/password or Service Provider. Try look at http://docs.spring.io/spring-social/docs/1.0.x/reference/html/signin.html

"java.lang.IllegalStateException: Unable to get a ConnectionRepository: no user signed in"

It happens because AuthenticationNameUserIdSource used by default

Internally, Spring Social’s configuration support will use the UsersConnectionRepository to create a request-scoped ConnectionRepository bean. In doing so, it must identify the current user. Therefore, we must also override the getUserIdSource() to return an instance of a UserIdSource.

In this case, we’re returning an instance of AuthenticationNameUserIdSource. This implementation of the UserIdSource interface assumes that the application is secured with Spring Security. It uses the SecurityContextHolder to lookup a SecurityContext, and from that return the name property of the Authentication object.

If your application isn’t secured with Spring Security, you’ll need to implement the UserIdSource interface as approprate for your application’s security mechanism. The UserIdSource interface looks like this:

package org.springframework.social;
public interface UserIdSource {
    String getUserId();
}

The getUserId() method simply returns a String that uniquely identifies the current user.

More info here

Question:

I use spring-social-facebook to interact with Graph API (1.0.3.RELEASE if it's metter). And I can't find any operation to retrieve profile's image url. I found only operations which return array of bytes and they are not very convenient for the implementation. Does any kind of operation which retrieves image url exist in Spring Social? If not, is there any 'tidy' workaround for this? Thanks!


Answer:

Finally, I didn't find any mention of profile picture url in spring social My solution: Initially I planned to extend UserOperations (FacebokTemplate.userOperations) class and add new method. But it's a package-level class and doens't visible outside. So I decided to create my own template class extending FacebookTemplate and implement the method in this way:

public String fetchPictureUrl(String userId, ImageType imageType) {
    URI uri = URIBuilder.fromUri(GRAPH_API_URL + userId + "/picture" +
            "?type=" + imageType.toString().toLowerCase() + "&redirect=false").build();

    JsonNode response = getRestTemplate().getForObject(uri, JsonNode.class);
    return response.get("data").get("url").getTextValue();
}