How do get a new Facebook access token invisibly when my current one expires? (Windows 8 App C#) - windows-8

Within my app (following various documentations) the user is able to login in to facebook via a dedicated login page. From this, the access token is given and the I save it. The thing is to get the token this way (using await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, loginUri, new Uri(redirectUri));, which seems the automatically show a login screen from facebook, I honestly have no idea how it appears...) means the user has to type their username and password again.
I don't want this to be the case, since I think if the user has logged in once, they shouldn't need to keep typing in their details again and again. However no matter what I've tried I can seem to get a new access token without going through this login screen again.
I've tried methods using the FacebookClient.Get() method, however it just doesn't exist in this version of the SDK. I have no idea what to use in its place, or what I need to send to Facebook to get a response.
So far this is what I have to log in for the first time (pretty much what is on the documentation)
string redirectUrl = "https://www.facebook.com/connect/login_success.html";
try
{
var loginUrl = fb.GetLoginUrl(new
{
client_id = FacebookHandler.FacebookAppId,
redirect_uri = redirectUrl,
scope = FacebookHandler.Permissions,
display = "touch",
response_type = "token"
});
Uri endUri = new Uri(redirectUrl);
WebAuthenticationResult webAuthRes = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, loginUrl, endUri);
if (webAuthRes.ResponseStatus == WebAuthenticationStatus.Success)
{
var callbackUri = new Uri(webAuthRes.ResponseData.ToString());
var facebookOAuthResult = fb.ParseOAuthCallbackUrl(callbackUri);
var accessToken = facebookOAuthResult.AccessToken;
if (String.IsNullOrEmpty(accessToken))
{
// User is not logged in, they may have canceled the login
}
else
{
// User is logged in and token was returned
FacebookHandler.AccessToken = accessToken;
LoginSucceeded();
}
}
else if (webAuthRes.ResponseStatus == WebAuthenticationStatus.ErrorHttp)
{
Debug.WriteLine("Error: " + "HTTP Error returned by AuthenticateAsync() : " + webAuthRes.ResponseErrorDetail.ToString());
}
else
{
Debug.WriteLine("Error: " + "Error returned by AuthenticateAsync() : " + webAuthRes.ResponseStatus.ToString());
}
}
catch (Exception ex)
{
//
// Bad Parameter, SSL/TLS Errors and Network Unavailable errors are to be handled here.
//
Debug.WriteLine(ex);
throw ex;
}
On successive launches of the app, after the user as entered their details for the first time, the saved access token could be used, but it soon expires, which throws a FacebookOAuthException. This is where I have tried to invisibly get a new access token, and where its not working.
try
{
await fb.PostTaskAsync(url, argList);
}
catch (FacebookOAuthException exc)
{
Debug.WriteLine(exc.Message);
ValidateLoginUrl(GetLoginUrl());
//Problem is with the ValidateLoginUrl(), which would get an access token from the login url
}
One of the things I've tried was to use TryParseOAuthCallbackUrl(), on the loginUrl that I can get by using FacebookClient.GetLoginUrl() (which is successful), but when I try to parse it it just comes up with an InvalidOperationException, and otherwise no explanation.
What do I have to do in order to get a new access token without the user having to login in again? I have my app ID and secret, and I could probably save the users ID from the initial login for use later(?). Would any of that help do this?

Related

How to make friends Facebook Log in code with Google log in code

I have two snippets of code that are each responsible for logging in from their social networks are Facebook and Google.
//GOOGLE
if(isset($_GET['code'])) {
$token = $google_client->fetchAccessTokenWithAuthCode($_GET["code"]);
//This condition will check there is any error occur during geting authentication token. If there is no any error occur then it will execute if block of code/
if (!isset($token['error'])) {
//Set the access token used for requests
$google_client->setAccessToken($token['access_token']);
//Store "access_token" value in $_SESSION variable for future use.
$_SESSION['access_token'] = $token['access_token'];
//Create Object of Google Service OAuth 2 class
$google_service = new Google_Service_Oauth2($google_client);
//Get user profile data from google
$data = $google_service->userinfo->get();
if (!empty($data['sub'])) {
$_SESSION['user_id'] = $data['sub'];
}
if (!empty($data['given_name'])) {
$_SESSION['user_name'] = $data['given_name'] . " " . $data['family_name'];
}
if (!empty($data['email'])) {
$_SESSION['user_email_address'] = $data['email'];
}
if (!empty($data['picture'])) {
$_SESSION['user_image'] = $data['picture'];
}
}
}
//FACEBOOK
$facebook_helper = $facebook->getRedirectLoginHelper();
if(isset($_GET['code'])) {
if (isset($_SESSION['access_token'])) {
$access_token = $_SESSION['access_token'];
} else {
$access_token = $facebook_helper->getAccessToken();
$_SESSION['access_token'] = $access_token;
$facebook->setDefaultAccessToken($_SESSION['access_token']);
}
$graph_response = $facebook->get("/me?fields=name,email", $access_token);
$facebook_user_info = $graph_response->getGraphUser();
if (!empty($facebook_user_info['id'])) {
$_SESSION['user_image'] = 'http://graph.facebook.com/' . $facebook_user_info['id'] . '/picture';
}
if (!empty($facebook_user_info['id'])) {
$_SESSION['user_id'] = $facebook_user_info['id'];
}
if (!empty($facebook_user_info['name'])) {
$_SESSION['user_name'] = $facebook_user_info['name'];
}
if (!empty($facebook_user_info['email'])) {
$_SESSION['user_email_address'] = $facebook_user_info['email'];
}
} else {
// Get login url
$facebook_permissions = ['email']; // Optional permissions
$facebook_login_url = $facebook_helper->getLoginUrl('https://2goe.com/demo/'.$lang.'/home/', $facebook_permissions);
}
When they are together, then:
When you click Google log in, redirectURL responds with server error 500.
And Facebook does not return user data, which is requested in the code.
But if, for example, you delete the code of one of the social networks, it individually works fine. I myself tried to somehow paint 2 codes into one code, but to no avail. I also split them into different files, but this also did not bring any results.
Can you please help me somehow combine them correctly so that there are no such conflicts between them.
The issue you are having is that you are setting things with the same session names. $_GET['code'] could be facebook or google there is no way for you to know which one it is.
The easiest solution would be to run the google code first. Then alter the if statement for Facebook a little.
If you do something like this the code for facebook will look for a code find a code but it will also look for an error from Google. If google spit back an error then you try the code with facebook. If it did not return an error then you know the code was most likely used by Google.
if(isset($_GET['code'] && isset($token['error'])) {

Custom User login in AppEngine

I have a Java Servlet backend with a datastore connected to my app; I am trying to implement a login system using the Android Studio LoginActivity template, using the user's email and password (not the PlusBaseActivity handling the Google Account login), but I don't know how to proceed from here:
How can you say that a User is logged in? and how can I make it so persistently using my datastore? I've read here: How to login User using UserService on AppEngine Java that I just need to call the method resp.sendRedirect(userService.createLoginURL(req.getRequestURI())), and I've done so:
#Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
int size = checkDatastore(); // 0 if empty, > 0 if not empty
if(size==0){
populateDatastore();
}
String asyncMessage = req.getParameter("order");
if(asyncMessage.equals("login")){
mail = req.getParameter("email");
psw = req.getParameter("password");
UserService userService = UserServiceFactory.getUserService();
User user = userService.getCurrentUser();
String message="";
resp.setContentType("text/plain");
PrintWriter out = resp.getWriter();
if(user == null) {
//Sends a temporary redirect response to the client using the
// specified redirect location URL and clears the buffer.
String uri = userService.createLoginURL(req.getRequestURI());
resp.sendRedirect(uri);
User user1 = userService.getCurrentUser();
message="No one is logged in!\n" + "Sent from App Engine at " + new Date();
out.println(message);
out.flush();
}if(user !=null) {
// login(user);
message = "Hello, " + user.getEmail() +
", "+user.getNickname()+"!" + "\nSent from App Engine at "+ new Date();
out.println(message);
out.flush();
}
}
}
but the sendRedirect() method only gives me a URI. What for?
Moreover, the User user = userService.getCurrentUser() always returns null. How come?
That's because the resp.sendRedirect(userService.createLoginURL(req.getRequestURI()))
of UserService only works when integrating the Login with Google Accounts as shown in this documentation.
If you want to implement a personalised login system you can do that in many ways. Surely you will need a Servlet checking new users' data and a datastore to persistently store new account registrations.

google account login for the first time displays error

I am trying to login the user with google account in my application. I am having a problem when the user is logged in for the first time with google account in my app it shows this error:
Argument 1 passed to Illuminate\Auth\Guard::login() must implement interface Illuminate\Auth\UserInterface, null given
In my controller I have this:
public function loginWithGoogle() {
// get data from input
$code = Input::get('code');
// get google service
$googleService = Artdarek\OAuth\Facade\OAuth::consumer("Google");
if (!empty($code)) {
// This was a callback request from google, get the token
$token = $googleService->requestAccessToken($code);
// Send a request with it
$result = json_decode($googleService->request('https://www.googleapis.com/oauth2/v1/userinfo'), true);
$user = User::whereEmail($result['email'])->first(['id']);
if (empty($user)) {
$data = new User;
$data->Username = $result['name'];
$data->email = $result['email'];
$data->google_id = $result['id'];
$data->first_name = $result['given_name'];
$data->last_name = $result['family_name'];
$data->save();
}
Auth::login($user);
return Redirect::to('/');
}
// if not ask for permission first
else {
// get googleService authorization
$url = $googleService->getAuthorizationUri();
// return to facebook login url
return Redirect::to((string) $url);
}
}
I know the problem is with Auth::login($user); as insert is performed at the same time with Auth::login($user); and it doesn't find data from database for the first time, but I don't know how to avoid this error and instead redirects to the main page even when the user is logged in for the first time. After this error the user is logged in, but how to avoid this?
Without knowing whether the rest of the code works, you definitely have a problem here:
if (empty($user)) {
$data = new User;
(...)
$data->save();
}
Auth::login($user);
When you're done creating your user, the $user variable is still empty. Your user is actually called $data. You should either rename the variable, or do the login with $data instead. Hopefully, that's enough to make the code work. :)

Meteor.http.get issue with Twitter API

I am using Meteor and the Twitter API for a project. I want to get information on a user from Twitter. I wrote a function that for example returns only the location of a user from Twitter. I believe this is the proper way to do a request on Meteor. Here it is :
Meteor.methods({getTwitterLocation: function (username) {
Meteor.http.get("https://api.twitter.com/1/users/show.json?screen_name="+ username +"&include_entities=true", function(error, result) {
if (result.statusCode === 200) {
var respJson = JSON.parse(result.content);
console.log(respJson.location);
console.log("location works");
return (respJson.location)
}else {
return ( "Unknown user ")
}
});
}});
Now this function will log what's in the console on my Git Bash. I get someones Location by doing a Meteor.call. But I want to post what that function returns on a page. In my case, I want to post in on a user's profile. This doesn't work. But the console.log(respJson.location) returns the location in my Git Bash but it won't display anything on the profile page. This is what I did on my profile page:
profile.js :
Template.profile.getLocation= function(){
return Meteor.call("getTwitterLocation","BillGates");
}
profile.html :
<template name="profile">
from {{getLocation}}
</template>
With that I get "Seattle, WA" and " "location works" on my Git Bash but nothing on the profile page. If anyone knows what I can do, that'd be really appreciated. Thanks.
Firstly when data is returned from the server you need to use a synchronous call, as the callback will return the data when the server already thinks the meteor method has completed. (the callback will be fired at a later time, when the data is returned from the server, by which time the meteor client would have already got a response)
var result = Meteor.http.get("https://api.twitter.com/1/users/show.json?screen_name="+ username +"&include_entities=true");
if (result.statusCode === 200) {
var respJson = JSON.parse(result.content);
console.log(respJson.location);
console.log("location works");
return (respJson.location)
}else {
return ( "Unknown user ")
}
The second is you need to use a Session hash to return the data from the template. This is because it will take time to get the response and the getLocation would expect an instant result (without a callback). At the moment client side javascript can't use synchronous api calls like on the server.
Template.profile.getLocation= function(){
return Session.get("twitterlocation");
}
Use the template created event to fire the meteor call:
Template.profile.created = function() {
Meteor.call("getTwitterLocation","BillGates", function(err,result) {
if(result && !err) {
Session.set("twitterlocation", result);
}
else
{
Session.set("twitterlocation", "Error");
}
});
});
Update:
Twitter has since updated its API to 1.1 a few modifications are required:
You now need to swap over to the 1.1 api by using 1.1 instead of 1. In addition you need to OAuth your requests. See https://dev.twitter.com/docs/auth/authorizing-request. Below contains sample data but you need to get proper keys
var authkey = "OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog",
oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp=""+(new Date().getTime()/1000).toFixed(0)+"",
oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
oauth_version="1.0"";
Be sure to remove the newlines, I've wrapped it to make it easy to read.
var result = Meteor.http.get("https://api.twitter.com/1.1/users/show.json?screen_name="+ username +"&include_entities=true",{headers:{Authorization : authkey});
If you find this a bit troublesome it might be easier to just use a package like https://github.com/Sewdn/meteor-twitter-api via meteorite to OAuth your requests for you.

Authenticating with Facebook for Mobile Services in Azure

I am having trouble with facebook authentication for Mobile Services in Azure.
To be more specific, I already have an application that is using Facebook C# SDK and it works fine. I can log on, fetch list of my friends and so. I want to keep using this SDK, but I also want to authenticate for Azure Mobile Service.
So, my plan was, log on with Facebook C# SDK (as I already do today), get the authentication token, and pass it to the MobileServiceClient.LoginAsync() - function. That way, I can still have all the nice features in Facebook C# SDK, and also use the built in authentication system in Mobile Services for Azure.
var client = new FacebookClient();
dynamic parameters = new ExpandoObject();
parameters.client_id = App.FacebookAppId;
parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
parameters.response_type = "token";
parameters.display = "popup";
var loginUrl = client.GetLoginUrl(parameters);
WebView.Navigate(loginUrl);
When load is complete, followin is executed:
FacebookOAuthResult oauthResult;
if (client.TryParseOAuthCallbackUrl(e.Uri, out oauthResult) && oauthResult.IsSuccess)
{
var accessToken = oauthResult.AccessToken;
var json = JsonObject.Parse("{\"authenticationToken\" : \"" + accessToken + "\"}");
var user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook, json);
}
However, I get this exception when I call the last line of code above:
MobileServiceInvalidOperationException, "Error: The POST Facebook login request must specify the access token in the body of the request."
I cannot find any information on how to format the accesstoken, I have tried a lot of different keys (instead of "authenticationToken" as you see in my sample). I also have tried just to pass the accesstoken string, but nothing seem to work.
Also, if I use the MobileServiceClient.LoginAsync() for making a brand new login, it works just fine, but it seem silly to force users to log on twice.
Any help is greatly appreciated!
The format expected for the object is {"access_token", "the-actual-access-token"}. Once the login is completed using the Facebook SDK, the token is returned in the fragment with that name, so that's what the Azure Mobile Service expects.
BTW, this is a code which I wrote, based on your snippet, which works. It should handle failed cases better, though, but for the token format, this should be enough
private void btnLoginFacebookToken_Click_1(object sender, RoutedEventArgs e)
{
var client = new Facebook.FacebookClient();
dynamic parameters = new ExpandoObject();
parameters.client_id = "MY_APPLICATION_CLIENT_ID";
parameters.redirect_uri = "https://www.facebook.com/connect/login_success.html";
parameters.response_type = "token";
parameters.display = "popup";
var uri = client.GetLoginUrl(parameters);
this.webView.LoadCompleted += webView_LoadCompleted;
this.webView.Visibility = Windows.UI.Xaml.Visibility.Visible;
this.webView.Navigate(uri);
}
async void webView_LoadCompleted(object sender, NavigationEventArgs e)
{
AddToDebug("NavigationMode: {0}", e.NavigationMode);
AddToDebug("Uri: {0}", e.Uri);
string redirect_uri = "https://www.facebook.com/connect/login_success.html";
bool close = (e.Uri.ToString().StartsWith(redirect_uri));
if (close)
{
this.webView.LoadCompleted -= webView_LoadCompleted;
this.webView.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
string fragment = e.Uri.Fragment;
string accessToken = fragment.Substring("#access_token=".Length);
accessToken = accessToken.Substring(0, accessToken.IndexOf('&'));
JsonObject token = new JsonObject();
token.Add("access_token", JsonValue.CreateStringValue(accessToken));
try
{
var user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.Facebook, token);
AddToDebug("Logged in: {0}", user.UserId);
}
catch (Exception ex)
{
AddToDebug("Error: {0}", ex);
}
}
}