Issues using Retrofit2 to call GitHub REST API to update existing file - java

I'm attempting to use Retrofit to call the GitHub API to update the contents of an existing file, but am getting 404s in my responses. For this question, I'm interested in updating this file. Here is the main code I wrote to try and achieve this:
GitHubUpdateFileRequest
public class GitHubUpdateFileRequest {
public String message = "Some commit message";
public String content = "Hello World!!";
public String sha = "shaRetrievedFromSuccessfulGETOperation";
public final Committer committer = new Committer();
private class Committer {
Author author = new Author();
private class Author {
final String name = "blakewilliams1";
final String email = "blake#blakewilliams.org";
}
}
}
**GitHubUpdateFileResponse **
public class GitHubUpdateFileResponse {
public GitHubUpdateFileResponse() {}
}
GitHubClient
public interface GitHubClient {
// Docs: https://docs.github.com/en/rest/reference/repos#get-repository-content
// WORKS FINE
#GET("/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json")
Call<GitHubFile> getConfigFile();
// https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents
// DOES NOT WORK
#PUT("/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json")
Call<GitHubUpdateFileResponse> updateConfigFile(#Body GitHubUpdateFileRequest request);
}
Main Logic
// Set up the Retrofit client and add an authorization interceptor
UserAuthInterceptor interceptor =
new UserAuthInterceptor("blake#blakewilliams.org", "myActualGitHubPassword");
OkHttpClient.Builder httpClient =
new OkHttpClient.Builder().addInterceptor(interceptor);
Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.client(httpClient.build()).build();
client = retrofit.create(GitHubClient.class);
// Now make the request and process the response
GitHubUpdateFileRequest request = new GitHubUpdateFileRequest();
client.updateConfigFile(request).enqueue(new Callback<GitHubUpdateFileResponse>() {
#Override
public void onResponse(Call<GitHubUpdateFileResponse> call, Response<GitHubUpdateFileResponse> response) {
int responseCode = response.code();
// More code on successful update
}
#Override
public void onFailure(Call<GitHubUpdateFileResponse> call, Throwable t) {
Log.e("MainActivity", "Unable to update file" + t.getLocalizedMessage());
}
});
What currently happens:
Currently, the success callback is triggered, but with a response code of 404 like so:
Response{protocol=http/1.1, code=404, message=Not Found, url=https://api.github.com/repos/blakewilliams1/blakewilliams1.github.io/contents/qr_config.json}
Has anyone else encountered this? I first thought it was a problem with including '/content/' in the URL but I do the same thing for reading the file contents request and it works fine (also uses same URL just a GET instead of PUT).

For anyone interested in doing this in the future, I figured out the solution.
I needed to revise the request object structure
Rather than using an authentication interceptor, I instead added an access token to the header. Here is where you can create access tokens for Github, you only need to grant it permissions to the 'repos' options for this use case to work.
This is what my updated request object looks like:
public class GitHubUpdateFileRequest {
public String message;
public String content;
public String sha;
public final Committer committer = new Committer();
public GitHubUpdateFileRequest(String unencodedContent, String message, String sha) {
this.message = message;
this.content = Base64.getEncoder().encodeToString(unencodedContent.getBytes());
this.sha = sha;
}
private static class Committer {
final String name = "yourGithubUsername";
final String email = "email#yourEmailAddressForTheUsername.com";
}
}
Then from my code, I would just say:
GitHubUpdateFileRequest updateRequest = new GitHubUpdateFileRequest("Hello World File Contents", "This is the title of the commit", shaOfExistingFile);
For using this reqest, I updated the Retrofit client implementation like so:
// https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents
#Headers({"Content-Type: application/vnd.github.v3+json"})
#PUT("/repos/yourUserName/yourRepository/subfolder/path/to/specific/file/theFile.txt")
Call<GitHubUpdateFileResponse> updateConfigFile(
#Header("Authorization") String authorization, #Body GitHubUpdateFileRequest request);
And I call that interface like this:
githubClient.updateConfigFile("token yourGeneratedGithubToken", request);
And yes, you do need the prefix "token ". You could hardcode that header into the interface, but I pass it in so that I can store it in locations outside of my version control's reach for security reasons.

Related

Passing a BODY of a POST request to the server

I have built a Restful-API Java(SpringBoot) and created the needed requests.
The following request is a POST Request to add new Category.
I have tested the POST request by POSTMAN, and it working as expected.
I am building the client-side in ASP.NET 5.x.x.
Now the problem appear when I am calling the post request, it seems the API doesn't receive the data (#RequestBody category) that has been send from the client.
Here is a code simple of how I have created them
Server Side:
#ResponseStatus(HttpStatus.CREATED)
#PostMapping(value = "/add", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public CategoryDTO create(#RequestBody CategoryDTO category) {
log.info("Adding new Category Name: " + category.getName());
return categoryMapper.asCategoryDTO(categoryService.save(categoryMapper.asCategory(category)));
}
Client Side
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Category category)
{
Category newCategory = new Category();
// Serialize the concrete class into a JSON String
var stringPayload = JsonConvert.SerializeObject(category);
// Wrap the JSON inside a StringContent which then can be used by the HttpClient class
StringContent content = new StringContent(stringPayload);
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.PostAsync("http://localhost:8080/category/add", content))
{
string apiResponse = await response.Content.ReadAsStringAsync();
newCategory = JsonConvert.DeserializeObject<Category>(apiResponse);
}
}
return RedirectToAction("Index");
}
I don't know what is wrong there, could anybody help!
EDIT--
Here is the request via postman
EDIT
I have created another POST request but as a RequestParam instead of RequestBody
#ResponseStatus(HttpStatus.CREATED)
#PostMapping(value = "/add", produces = MediaType.APPLICATION_JSON_VALUE)
public CategoryDTO addCategory(#RequestParam(name = "categoryName") String categoryName){
return categoryMapper.asCategoryDTO(categoryService.addCategory(categoryName));
}
and created in the client side the request as following
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(Category category)
{
Category newCategory = new Category();
var parameters = new Dictionary<string, string> { { "categoryName", category.Name } };
var encodedContent = new FormUrlEncodedContent(parameters);
using (var httpClient = new HttpClient())
{
using (var response = await httpClient.PostAsync("http://localhost:8080/category/add", encodedContent))
{
string apiResponse = await response.Content.ReadAsStringAsync();
newCategory = JsonConvert.DeserializeObject<Category>(apiResponse);
}
}
return RedirectToAction("Index");
}
And It's works fine!
So the problem is how to pass the data via the httpClient, which need to be of type RequestBody (the data in the body not in the header!) also as a application/json.
So how to pass the data?
I suppose that your spring boot application just blocks POST request because you didn't provide instruction how to handle requests. Try to disable csrf protection like it did here: https://stackoverflow.com/a/48935484/13314717
It might be a problem in the naming convention. Starting with capital letters in properties in C#, and starting with lowercase in Java.
If your class looks like this in C#
class Category {
int Id;
string Name;
...
}
And like this in Java
class Category {
int id;
string name;
...
}
It is not going to work so you have to match the property names. So make either both id or both Id.

How to make a POST call using Scribe and fetch data having OAuth 1.0 Authentication in Java?

I have a requirement to make a post-call to a URL which has OAuth 1.0 authentication. I am pretty new to all these. From my research, I got to know about Scribe in Java, but I can find only Get calls using Scribe. I already have consumerKey and consumerSecret key for OAuth 1.0 authentication. Are there any suggestions on how to achieve this successfully.
With postman I am able to fetch the data successfully, but I want to achieve it using Java.
I have tried something like this
I tried this way
public String getSmartCommPDF(#RequestBody Model model) throws IOException {
OAuthService service = new ServiceBuilder().provider(ModelAPI.class).apiKey(consumerKey)
.apiSecret(consumerSecret).build();
OAuthRequest request = new OAuthRequest(Verb.POST, url);
ObjectMapper mapper = new ObjectMapper();
request.addHeader("Content-Type", "application/json;charset=UTF-8");
request.addPayload(mapper.writeValueAsString(model));
Token accessToken = new Token("", ""); // not required for context.io
service.signRequest(accessToken, request);
Response response = request.send();
System.out.println("Response = " + response.getBody());
return "Success";
}
This is my ModelAPI class
public class ModelAPI extends DefaultApi10a {
#Override
public String getRequestTokenEndpoint() {
return "https://domain/one/oauth1/api/v6/job";
}
#Override
public String getAccessTokenEndpoint() {
return "https://domain/one/oauth1/api/v6/job";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return "https://domain/one/oauth1/api/v6/job";
}
}
This part of code is not throwing any error but, the response body is empty. Where I am going wrong, any one has any idea?
Thank you.
The data was coming back in the input stream. So, I used
response.getStream();
and write it to a file and use it.

Retrieve a list of a given user's tweets using Twitter API 1.1 and Retrofit

I'm trying to obtain a list of a user's tweets and I've run into some trouble when trying to authenticate my call to the API. I currently get a 401 when executing the code below:
public interface TwitterApi {
String API_URL = "https://api.twitter.com/1.1";
String CONSUMER_KEY = "<CONSUMER KEY GOES HERE>";
String CONSUMER_SECRET = "<CONSUMER SECRET GOES HERE>";
String ACCESS_TOKEN = "<ACCESS TOKEN GOES HERE>";
String ACCESS_TOKEN_SECRET = "<ACCESS TOKEN SECRET GOES HERE>";
#GET("/statuses/user_timeline.json")
List<Tweet> fetchUserTimeline(
#Query("count") final int count,
#Query("screen_name") final String screenName);
}
The following throws a 401 Authorisation error when calling fetchUserTimeline()
RetrofitHttpOAuthConsumer consumer = new RetrofitHttpOAuthConsumer(TwitterApi.CONSUMER_KEY, TwitterApi.CONSUMER_SECRET);
consumer.setTokenWithSecret(TwitterApi.ACCESS_TOKEN, TwitterApi.ACCESS_TOKEN_SECRET);
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(TwitterApi.API_URL)
.setClient(new SigningOkClient(consumer))
.build();
TwitterApi twitterApi = restAdapter.create(TwitterApi.class)
tweets = twitterApi.fetchUserTimeline(2, screenName);
I've also included the relevant code from the signpost-retrofit plugin:
public class SigningOkClient extends OkClient {
private final RetrofitHttpOAuthConsumer mOAuthConsumer;
public SigningOkClient(RetrofitHttpOAuthConsumer consumer) {
mOAuthConsumer = consumer;
}
public SigningOkClient(OkHttpClient client, RetrofitHttpOAuthConsumer consumer) {
super(client);
mOAuthConsumer = consumer;
}
#Override
public Response execute(Request request) throws IOException {
Request requestToSend = request;
try {
HttpRequestAdapter signedAdapter = (HttpRequestAdapter) mOAuthConsumer.sign(request);
requestToSend = (Request) signedAdapter.unwrap();
} catch (OAuthMessageSignerException | OAuthExpectationFailedException | OAuthCommunicationException e) {
// Fail to sign, ignore
e.printStackTrace();
}
return super.execute(requestToSend);
}
}
The signpost-retrofit plugin can be found here: https://github.com/pakerfeldt/signpost-retrofit
public class RetrofitHttpOAuthConsumer extends AbstractOAuthConsumer {
private static final long serialVersionUID = 1L;
public RetrofitHttpOAuthConsumer(String consumerKey, String consumerSecret) {
super(consumerKey, consumerSecret);
}
#Override
protected HttpRequest wrap(Object request) {
if (!(request instanceof retrofit.client.Request)) {
throw new IllegalArgumentException("This consumer expects requests of type " + retrofit.client.Request.class.getCanonicalName());
}
return new HttpRequestAdapter((Request) request);
}
}
Any help here would be great. The solution doesn't have to include the use of signpost but I do want to use Retrofit. I also do not want to show the user an 'Authenticate with Twitter' screen in a WebView - I simply want to display a handful of relevant tweets as part of a detail view.
Are you certain the signpost-retrofit project works for twitter oauth? I've used twitter4j successfully in the past - and if you don't want the full library you can use their code for reference. twitter4j

Using absolute URLs with Retrofit

I have a HAL API that I'm working with and in many cases I need to send request (with different methods) to a URL that I get back from API. Meaning I don't want to hardcode the path of URL in my retrofit api interface but what I want is just sending a simple request using retrofit to that URL.
I'm using Volley now and I know that I can use OkHttp for this purpose but I was wondering if there is a nice way of doing such thing in Retrofit?
Recently Square has released the Retrofit v2.0.0 BETA and it has a built-in support for dynamic URLs. Even though the Library is in Beta, based on what Jake Wharton told us in DroidCon NYC 2015, all the apis are stable and will not change. I'm personally adding it to my production so its up to you.
You will find the following links useful if you decide to do the upgrade:
Jake Wharton presentation # DroidCon NYC 2015
A very good guide on the changes
In simple word, you can now use the api annotations (like #GET or #POST and others) without any path and then you pass in a #URL to your api method that the method will use to call.
----------------Retrofit 1.x
I figured out a nice way for doing this and would like to share it.
The trick is to use the dynamic URL as your End Point in the creation of RestAdapter and then have a empty path on your API interface.
Here is how I did it:
public RestAdapter getHostAdapter(String baseHost){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(baseHost)
.setRequestInterceptor(requestInterceptor)
.build();
return restAdapter;
}
I build my restAdapter using this method and then I have this in my interface: (this will not work if your URL has query parameters added to it. See next answer for solution to that case)
public interface General {
#GET("/")
void getSomething(Callback<SomeObject> callback);
}
and finally using them like this:
getHostAdapter("YOUR_DYNAMIC_URL").create(General.class)
.getSomething(new Callback<SomeObject>(){
...
})
Hope it helps.
In case that your URL has query parameters on it, the above solution will not work since it will add the '/' at the end of your base URL. for example if your URL is
https://www.google.com/?q=test
then the above solution will try to send the request to
https://www.google.com/?q=test/
which will fail because of mall format.
What we can do is one extra step and parsing the url. By parsing I mean just taking out all URL parameters and sending them in a QueryMap.
Here is how:
We should have the same structure describe above with a little change to our interface
public interface General {
#GET("/")
void getSomething(#QueryMap Map<String,String> queryMap, Callback<SomeObject> callback);
}
I just added a QueryMap to the above interface and now we can use this parser method:
public static void getSomething(#NonNull String urlString, #NonNull Callback<SomeObject> callback){
Uri uri = Uri.parse(urlString);
Set<String> queryParameterNames = uri.getQueryParameterNames();
String host = uri.getHost();
HashMap<String,String> queryMap = new HashMap<>();
Iterator<String> iterator = queryParameterNames.iterator();
while(iterator.hasNext()){
String queryName = iterator.next();
String queryParameter = uri.getQueryParameter(queryName);
queryMap.put(queryName, queryParameter);
}
getHostAdapter(host)
.create(General.class)
.getSomething(queryMap, callback);
}
now you can call this method like this:
getSomething("https://www.google.com/?q=test");
Enjoy coding.
Note: QueryMap was added on Retrofit v1.4.0
I also need a path on my URL, so I did this:
#GET("/{path}")
void getMatcherUrl(#Path(value = "path", encode = false) String path, #QueryMap Map<String, String> queryMap, RestCallback<MatcherResponse> matcherResponse);
/**
* Need to create a custom method because i need to pass a absolute url to the retrofit client
*
* #param urlString
* #param matcherResponse
*/
public void getMatcherUrl(#NonNull String urlString, #NonNull RestCallback<MatcherResponse> matcherResponse) {
Uri uri = Uri.parse(urlString);
Set<String> queryParameterNames = uri.getQueryParameterNames();
String host = uri.getHost();
String path = (uri.getPath().startsWith("/")) ? uri.getPath().substring(1) : uri.getPath();
HashMap<String, String> queryMap = new HashMap<>();
Iterator<String> iterator = queryParameterNames.iterator();
while (iterator.hasNext()) {
String queryName = iterator.next();
String queryParameter = uri.getQueryParameter(queryName);
queryMap.put(queryName, queryParameter);
}
getApiCoreService(host)
.getMatcherUrl(path, queryMap, matcherResponse);
}
public ApiCoreService getApiCoreService(String host) {
if (StringUtils.isEmpty(host))
this.endpoint = new RestEndpoint(RemoteConfigurationManager.getInstance().getApiCore(), "ApiCore");
else
this.endpoint = new RestEndpoint(host, "ApiCore");
return apiCoreService;
}
Adding to above two answers, Here is a working class that makes use of Queryparam and fires the absolute URL
public class VideoClient {
private static final String TAG = "VideoCLient";
private final RestAdapter restAdapter;
private General apiService;
private String hostName;
private LinkedHashMap<String, String> queryMap;
private String Url_Path;
public VideoClient(String BASE_URL) {
Log.d(TAG,"Base url is "+BASE_URL);
hostName =getHostNameAndGenerateQueryMap(BASE_URL);
Gson gson = new GsonBuilder()
.create();
RequestInterceptor interceptor = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {}
};
restAdapter = new RestAdapter.Builder()
.setLogLevel(RestAdapter.LogLevel.FULL)
.setEndpoint("http://"+hostName)
.setClient(new OkClient())
.setConverter(new GsonConverter(gson))
.setRequestInterceptor(interceptor)
.build();
private String getHostNameAndGenerateQueryMap(String urlString) {
Uri uri = Uri.parse(urlString);
Url_Path = (uri.getPath().startsWith("/")) ? uri.getPath().substring(1) : uri.getPath();
Set<String> queryParameterNames = uri.getQueryParameterNames();
String host = uri.getHost();
queryMap = new LinkedHashMap<>();
Iterator<String> iterator = queryParameterNames.iterator();
while (iterator.hasNext()) {
String queryName = iterator.next();
String queryParameter = uri.getQueryParameter(queryName);
Log.d(TAG,"query name "+queryName +" query param "+queryParameter);
queryMap.put(queryName, queryParameter);
}
return host;
}
public interface General {
/*void getVideo(#Path("auth_token") String authId,
#Query("et") String systemTime,#Query("service_id") String serviceID,
#Query("protocol") String scheme,#Query("play_url") String url,
#Query("us") String us,Callback<String> callback);
*/
#GET("/{path}")
getVideo(#Path(value="path", encode=false)String path,#QueryMap LinkedHashMap<String, String> queryMap);
}
public void getVideoDetails() {
Log.i("firing", "getVideoApi");
Log.d(TAG, "firing " + Url_Path + " function");
restAdapter.create(General.class).getVideo(Url_Path,queryMap, new Callback<Object>() {
#Override
public void success( Object o, Response response) {
Log.d(TAG," Success Response is "+response );
}
#Override
public void failure(RetrofitError error) {
Log.d(TAG, "Failure " + "Internal Error" + error);
}
});
}

Spring Security & Facebook OAuth 2.0 Integration with Graph API

Please, at least pseudo (but from working environment not "maybe this should work") application context and controller/filter that will authenticate and/or auto-register Facebook users.
This link: http://blog.kadirpekel.com/2009/11/09/facebook-connect-integration-with-spring-security/ will not do. Actually I will put minus point to anyone who will post it as answer. I spend 2 hours with the thing and I didn't get it to work. I ended bit more bolder and feeling more stupid than usual after this endeavor :-(
I would really like to see OAuth 2.0 solution for facebook connect. And restrict the use of Facebook JavaScript API to absolute minimum.
Following link shows about what I need:
http://www.richardnichols.net/2010/06/implementing-facebook-oauth-2-0-authentication-in-java/
Please post only code to this question. I already got all the advice I can handle.
UPDATE
I have servlet solution and posted answer here if anyone is interested:
Facebook Connect example in JSP (tomcat)
Here's an MVC implementation of facebook OAuth 2.0
The code's in C# and hopefully its similarity with java helps you out.
Controller(Entry point):Controller(in MVC) is the point in the code where the control reaches after someone clicks on the login link.
public ActionResult Authenticate()
{
var oauthFacebook = new FacebookOAuth();
if (Request["code"] == null)
{
//Redirect the user to Facebook for authorization.
Response.Redirect(oauthFacebook.AuthorizationLinkGet());
}
else
{
//Get the access token and secret.
oauthFacebook.AccessTokenGet(Request["code"]);
if (oauthFacebook.Token.Length > 0)
{
//We can now make our api calls
var user = oauthFacebook.GetAttributes();
}
}
}
FacebookOAuth Class
public class FacebookOAuth : Oauth
{
public FacebookOAuth()
{
Authorize = "https://graph.facebook.com/oauth/authorize";
AccessToken = "https://graph.facebook.com/oauth/access_token";
CallbackUrl = "http://<YourURLHere>/Authenticate";
AttributesBaseUrl = "https://graph.facebook.com/me/?access_token=";
ConsumerKey = ConfigurationManager.AppSettings["FacebookConsumerKey"];//Ur Consumer Key goes here
ConsumerSecret = ConfigurationManager.AppSettings["FacebookConsumerSecret"];//Ur Consumer secret goes here
Provider = "Facebook";
}
public override string AuthorizationLinkGet()
{
return
string.Format(
"{0}?client_id={1}&redirect_uri={2}&scope=email,user_education_history,user_location,user_hometown",
Authorize, ConsumerKey, CallbackUrl);
}
public User GetAttributes()
{
string attributesUrl = string.Format("{0}{1}", AttributesBaseUrl, Token);
string attributes = WebRequest(Method.Get, attributesUrl, String.Empty);
var FacebookUser = new JavaScriptSerializer().Deserialize<FacebookUser>(attributes);
return new User()
{
FirstName = FacebookUser.first_name,
MiddleName = FacebookUser.middle_name,
LastName = FacebookUser.last_name,
Locale = FacebookUser.locale,
UserEmail = FacebookUser.email,
AuthProvider = Provider,
AuthToken=Token
};
}
}
OAuth baseclass(Class from which FacebookOAuth derives)
public abstract class Oauth
{
#region Method enum
public enum Method
{
Get,
Post,
Delete
} ;
#endregion
protected string AccessToken;
protected string AttributesBaseUrl;
protected string Authorize;
protected string CallbackUrl;
protected string ConsumerKey;
protected string ConsumerSecret;
public string Provider { get; protected set; }
public string Token { get; set; }
public virtual string AuthorizationLinkGet()
{
return
string.Format(
"{0}?client_id={1}&redirect_uri={2}&scope=publish_stream,email,user_education_history,user_location",
Authorize, ConsumerKey, CallbackUrl);
}
public void AccessTokenGet(string authToken)
{
Token = authToken;
string accessTokenUrl = string.Format("{0}?client_id={1}&redirect_uri={2}&client_secret={3}&code={4}",
AccessToken, ConsumerKey, CallbackUrl, ConsumerSecret, authToken);
string response = WebRequest(Method.Get, accessTokenUrl, String.Empty);
if (response.Length > 0)
{
//Store the returned access_token
NameValueCollection qs = HttpUtility.ParseQueryString(response);
if (qs["access_token"] != null)
{
Token = qs["access_token"];
}
}
}
public string WebRequest(Method method, string url, string postData)
{
StreamWriter requestWriter;
string responseData = string.Empty;
var webRequest = System.Net.WebRequest.Create(url) as HttpWebRequest;
if (webRequest != null)
{
webRequest.Method = method.ToString();
webRequest.ServicePoint.Expect100Continue = false;
webRequest.Timeout = 20000;
if (method == Method.Post)
{
webRequest.ContentType = "application/x-www-form-urlencoded";
//POST the data.
requestWriter = new StreamWriter(webRequest.GetRequestStream());
try
{
requestWriter.Write(postData);
}
finally
{
requestWriter.Close();
}
}
responseData = WebResponseGet(webRequest);
}
return responseData;
}
public string WebResponseGet(HttpWebRequest webRequest)
{
StreamReader responseReader = null;
string responseData;
try
{
responseReader = new StreamReader(webRequest.GetResponse().GetResponseStream());
responseData = responseReader.ReadToEnd();
}
finally
{
if (webRequest != null) webRequest.GetResponse().GetResponseStream().Close();
if (responseReader != null) responseReader.Close();
}
return responseData;
}
}
I actually just finished my non-javascript, implementation of the Facebook Graph API Authentication last night. I was a gargantuan pain in the a**, but it works and it's working fairly well.
I used the example from the link you posted above as a starting point, as well as, the code from here as a starting point. I had to write my own implementation of their FacebookGraphAuthenticationProvider and their FacebookGraphAuthenticationFilter, but now it works the way I want it to.
You need to create implementations of both of these files, put your filter in the filter chain, and create a implementation of the Spring Security UserDetailsService that the Provider can use to manage your user account information. I have some code on my machine at home that I can send you via email if you like.
Here are the steps I had to use to get the authentication to work:
Get an "code" for a user, this is done by making the following call: https://www.facebook.com/dialog/oauth?client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&scope=email,read_stream (The scope is all the permissions you want to request from FB). This call will create an "authentication code" which will then be sent back to your "redirect_uri" (which I stated as http://{my fb app registered domain}/j_spring_security_authentication_check.
Once you have this "code", you need to make a call within your AuthenticationProvider that will retrieve an access_token for your user's session: this URL looks like: https://graph.facebook.com/oauth/access_token? client_id=YOUR_APP_ID&redirect_uri=YOUR_URL&client_secret=YOUR_APP_SECRET&code=THE_CODE_FROM_ABOVE. You have to make sure your "redirect_uri" is the same as the one you did in #1. You'll make the above call using something like Apache's HttpClient, or the like.
Now with this access_token (which comes in the body of above response), you can get your user's profile information with the following URL: https://graph.facebook.com/me?access_token={ACCESS_TOKEN from above). The response will be in JSON. You can also use the access_token with all of the graph API to post status, pictures, etc.
I have some code at home that has my full implementation that I would be happy to share.
I hope this helps at least a bit. I suggest using the Spring Social app to get started with posting status, pictures, wall stuff, etc. This will be a good place to start looking at FB-Spring interaction.

Categories

Resources