Announcement Announcement Module
Collapse
No announcement yet.
Spring Social get underlying access tokens Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring Social get underlying access tokens

    Is it possible to get the underlying access tokens being managed by Spring Social for each of the connections?
    For example, imagine that I have a Facebook account added to a user through Spring Social, and I want to make Facebook API calls WITHOUT using Spring Social. How can I get the access token that Spring Social API would use while encapsulating Facebook API calls? Is there any API to get this? Or the only option is to directly query the database where the tokens are stored.

    For example, Spring Social Facebook has this class:
    http://static.springsource.org/sprin...i/Account.html

    But I haven't found any way to get such an object that would have a getAccessToken method.
    Another more generic class is the ConnectionData in Spring Social main project:
    http://static.springsource.org/sprin...ctionData.html

    But once again I don't know how to access it. Any ideas or tips?

    Thanks in advance.

  • #2
    There's currently no way to do this without going to the database. But, I welcome you to create an improvement issue at https://jira.springsource.org/browse/SOCIAL to address this.

    Out of curiosity...why do you want to work with the access token directly? Is it because the Facebook API binding doesn't support some operation you need? If so, then (1) please create an improvement or new feature issue at https://jira.springsource.org/browse/SOCIALFB to address the missing operation and (2) can you perform the operation you need through GraphApi or even the lower-level RestOperations?

    You can perform Graph API functions through GraphApi (which FacebookTemplate implements). You can also ask for a RestTemplate by calling restOperations() on a FacebookTemplate and get a Spring RestTemplate that is already prepared to place the Authorization header on any API call you make through it.

    I don't mind exposing the access token, if that's the right thing to do. But I want to understand why you need it to understand if it is the right thing to do or if there's a better way to solve your problem without exposing it. At the moment, the only reason that I can see as a valid reason for exposing the access token is if you intend to share the token with some Javascript code on the client side. But if it's all server-side work, then you should be able to do whatever you need either through GraphApi or through the RestTemplate returned from restOperations().

    Comment


    • #3
      Since today, I too have the requirement to gain access to access tokens. I need to elaborate a little to explain where this requirement comes from. TL;DR: Access tokens are required for Facebook batch requests.

      Background: When users access my Spring-Social-Facebook-enabled app, my app queries for their Facebook friends and some of their friends' data. A typical Facebook user may have 500 and more friends and likes on Facebook.

      Add to this that my app is accessed concurrently by several users - then this sometimes results in 500+ requests initiated by my app via Spring Social towards Facebook's servers in less than 2 minutes. I observed that in such moments of higher request rate, Facebook's servers return ambiguous error messages.

      For example, my app sometimes receives the following error message while querying Facebook:

      Code:
      15:17:11.843 container [qtp10134927-26] DEBUG o.s.web.client.RestTemplate - Created GET request for "https://graph.facebook.com/***redactedFacebookUserId***"
      15:17:11.843 container [qtp10134927-26] DEBUG o.s.web.client.RestTemplate - Setting request Accept header to [application/json]
      15:17:12.016 container [qtp10134927-26] WARN  o.s.web.client.RestTemplate - GET request for "https://graph.facebook.com/***redactedFacebookUserId***" resulted in 500 (Internal Server Error); invoking error handler
      For the next two minutes or so, this query kept causing 500 (Internal Server Error). A few minutes later, this query worked again. I observed that such kind or errors happen more often when my app is issuing a high number of requests in a short time. These errors ("fault" is on Facebook's side) produce unacceptable user experiences in my app and therefore need to be tackled.

      My hypothesis is: to remedy these kinds of errors, batching up requests towards Facebook's servers may reduce the numbers of errors, as the number of HTTP requests is reduced. As a further positive side effect of batching, queries to Facebook will significantly reduce lag in my app: Currently, Spring's RestTemplate (used by Spring Social) behaves synchronously. So currently, if there is a single-threaded for-loop over 500 Facebook likes, and one HTTP request-response for each friend takes around 180 milliseconds, then this whole loop takes 90 seconds, which I don't want my new users to wait until "their batch calculation" finishes.

      Facebook provides documentation about batching HTTP requests. A batch request contains one or more access tokens like this:

      Code:
      curl \
      -F 'access_token=aFallbackDefaultAccessTokenWhichIsUsedByABatchEntryIfThatBatchEntryIsntProvidedWithItsOwnAccessTokenXXXXXThereAlwaysHasToBeAFallbackDefaultAccessTokenOrElseFacebookWillComplain' \
      -F 'batch=[{"access_token":"UniqueAccessTokenToBeUsedForThisBatchEntry", "method":"GET", "relative_url":"someFacebookId?fields=id,first_name,middle_name,last_name,username,gender"}, {"method":"GET", "relative_url":"AnotherFacebookIdForWhichTheFallbackAccessTokenWillBeUsed?fields=id,first_name,middle_name,last_name,username,gender"}]' \
      https://graph.facebook.com
      As per the Facebook documentation, a batch request can contain up to 50 individual requests, and each request can have its own individual access token.

      My idea now is to have an @Service that collects Facebook request specifications. Also my idea is that there is an org.springframework.social.facebook.api.GraphApi implementation that, instead of using its own RestTemplate, delegates request specifications to aforementioned @Service.
      When a request specification is provided to this service, a timer starts to collect further request specifications for the next 1 or so seconds. A caller to this service returns immediately and is returned a Future. If either 50 request specifications are collected or 1 second is over, that service builds the actual bundled request, starts a thread dedicated to this bundled request, and then sends the request towards Facebook. Once Facebook has returned the "bundled response", the service takes care of assigning each separate response to its related Future.

      I feel this idea of a "Facebook batch request framework" is quite ambitious to be integrated into Spring Social's current architecture, but at least in my own app I see the need for a solution to the "many-HTTP-requests-towards-Facebook challenge" in order to have my app scale beyond "private beta" request numbers. API-level access to access tokens could make it easier to develop such a solution. In the meantime, org.springframework.social.connect.jdbc.JdbcConnec tionRepository provides insight on on how to retrieve access tokens directly from the database.

      Comment


      • #4
        Originally posted by habuma View Post
        I don't mind exposing the access token, if that's the right thing to do. But I want to understand why you need it to understand if it is the right thing to do or if there's a better way to solve your problem without exposing it. At the moment, the only reason that I can see as a valid reason for exposing the access token is if you intend to share the token with some Javascript code on the client side. But if it's all server-side work, then you should be able to do whatever you need either through GraphApi or through the RestTemplate returned from restOperations().
        Exactly. That's my idea... I have an Android application that is a companion to a website/service built with Spring (including Spring Social and Spring Security). My idea was to use the Spring Social access token on the client side instead of the user having to get a new one by asking the user. This would give a more seamless experience.

        Basically that's my motivation to get the underlying access token and I believe that there are may be other use cases that require such access. For example, the one presented by Eduard but also imagine that someone is used to/or has legacy code that interacts with Facebook APIs but uses a different library but wants to integrate it with Spring Social and therefore need an access token.

        I thinks that those are also pretty valid use cases. Having a direct/official away of getting the stored tokens doesn't even pose a security risk since you can also get them directly from the database, it just makes things easier and cleaner for the library's end-users. So I think this should be added to the library in the future, if not for 1.1 (not sure if the API is frozen or something like that) for a later release (e.g., 1.2+).

        Comment


        • #5
          Eduard and petersaints: Both of you have made compelling arguments for this. Therefore, I have created https://jira.springsource.org/browse/SOCIAL-367 to track this.

          But now that I've done this, I am starting to think that it *is* possible with Spring Social right now. In short, here's what you need to do:

          - Inject the request-scoped ConnectionRepository or the singleton-scoped UsersRequestRepository into wherever you need it.
          - If using the UsersRequestRepository, you'll need to obtain a ConnectionRepository through createConnectionRepository(userId)
          - From the ConnectionRepository, call getPrimaryConnection() or findPrimaryConnection() to get a connection for Facebook.
          - From the Connection, call createData() to get ConnectionData
          - You can get the access token and anything else you need from ConnectionData

          Yes, it's not direct, but I believe this will work. And, given the nature of the problem, I'm not inclined to expose the access token through the API binding (I was already leaning toward exposing it through the connection...and that's what this solution already offers).

          Let me know if this works for you.

          Comment

          Working...
          X