ios Facebook Login not working; - facebook-login

Sandbox Mode=on
Bundle ID =same FBid=Ok ..Anything else does not matter
-(void) askForPublishPermission
{ BOOL isSessionActive = [self isFacebookSessionActive];
//BOOL useUI = !isSessionActive;
//useUI = YES;
BOOL publishPermissionAvailable = NO;
if (isSessionActive)
{
NSArray* validPermission = [[FBSession activeSession] permissions];
for (int i=0; i<[validPermission count]; i++)
{
NSObject* permission = [validPermission objectAtIndex:i];
if ([permission isKindOfClass:[NSString class]])
{
NSString* validPermission = (NSString*)permission;
//NSLog(#"Valid Permissions = %#", validPermission);
if ([validPermission isEqualToString:#"publish_actions"])
{
publishPermissionAvailable = YES;
break;
}
}
}
}
if (publishPermissionAvailable == YES)
{
//NSLog(#"------------------ CALL AT TWO --------------");
//NSLog(#"Login Success");
}
else // Request for publish permission.
{
NSArray* permissionArray = [NSArray arrayWithObjects:
#"publish_actions",nil];
[FBSession openActiveSessionWithPublishPermissions:permissionArray
defaultAudience:FBSessionDefaultAudienceEveryone
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
}
}
and
-(void) askForPublishPermission
{
BOOL isSessionActive = [self isFacebookSessionActive];
//BOOL useUI = !isSessionActive;
//useUI = YES;
BOOL publishPermissionAvailable = NO;
if (isSessionActive)
{
NSArray* validPermission = [[FBSession activeSession] permissions];
for (int i=0; i<[validPermission count]; i++)
{
NSObject* permission = [validPermission objectAtIndex:i];
if ([permission isKindOfClass:[NSString class]])
{
NSString* validPermission = (NSString*)permission;
//NSLog(#"Valid Permissions = %#", validPermission);
if ([validPermission isEqualToString:#"publish_actions"])
{
publishPermissionAvailable = YES;
break;
}
}
}
}
if (publishPermissionAvailable == YES)
{
//NSLog(#"------------------ CALL AT TWO --------------");
//NSLog(#"Login Success");
}
else // Request for publish permission.
{
NSArray* permissionArray = [NSArray arrayWithObjects:
#"publish_actions",nil];
[FBSession openActiveSessionWithPublishPermissions:permissionArray
defaultAudience:FBSessionDefaultAudienceEveryone
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
}
}
But ..Not login..
error code see below:
Error = Error Domain=com.facebook.sdk Code=2 "The operation couldn’t be completed. (com.facebook.sdk error 2.){com.facebook.sdk:ErrorLoginFailedReason=com.facebook.sdk:SystemLoginCancelled, com.facebook.sdk:ErrorInnerErrorKey=Error Domain=com.apple.accounts Code=7 "The Facebook server could not fulfill this access request: The app must ask for a basic read permission like email at install time."
I'd spent a few months this issue..Please Help me

Your error message says:
The app must ask for a basic read permission like email at install time.
From the docs:
When someone connects with an app using Facebook login, the app can access their public profile and friend list, the pieces of information that are visible to everyone. To create this basic connection, apps must always request access to a person's basic profile information by asking for the basic_info permission.
Try adding basic_info to permissionArray array. If that doesn't work, try adding email as well.

I replaced :
[FBSession openActiveSessionWithPublishPermissions:#[#"publish_actions"] defaultAudience:FBSessionDefaultAudienceFriends allowLoginUI:YES completionHandler:stateHandler];
with
[FBSession openActiveSessionWithPublishPermissions:#[#"basic_info", #"publish_actions", #"email"] defaultAudience:FBSessionDefaultAudienceFriends allowLoginUI:YES completionHandler:stateHandler];
And now it's working
(permissions orders matter, basic_info have to be in first position)

Related

Invalid grant issue with Google OAuth authentication in Qt

I'm developing a Qt application and I want to use Google authentication for it. I created a Google API as explained in the following link: https://blog.qt.io/blog/2017/01/25/connecting-qt-application-google-services-using-oauth-2-0/ but I have a problem with it. It doesn't work in many cases and I get ProtocolInvalidOperationError(302) error for https://accounts.google.com/o/oauth2/token request URL in
QOAuthHttpServerReplyHandler::networkReplyFinished(QNetworkReply *reply)
method of Qt class.
Note that I override QOAuthHttpServerReplyHandler::networkReplyFinished(QNetworkReply *reply) to get this error, because it doesn't emit any signal in this case, and the return value for reply->readAll() is as below:
{
"error": "invalid_grant",
"error_description": "Malformed auth code."
}
My Login.cpp code is something as below:
Login::Login() {
google = new QOAuth2AuthorizationCodeFlow;
google->setScope("email");
google->setAuthorizationUrl("https://accounts.google.com/o/oauth2/auth");
google->setClientIdentifier(Utility::decrypt(encryptedClientId));
google->setAccessTokenUrl("https://accounts.google.com/o/oauth2/token");
google->setClientIdentifierSharedKey(Utility::decrypt(encryptedClientSecret));
connect(google, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser,
&QDesktopServices::openUrl);
connect(google,&QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived,[=](const QVariantMap data){
QString code(data["code"].toString());
if(!code2.isEmpty())
{
const QUrl redirectUri= "http://localhost:56413/cb";
QJsonObject postdata;
postdata.insert("code",code);
postdata.insert("client_id", Utility::decrypt(encryptedClientId));
postdata.insert("client_secret", Utility::decrypt(encryptedClientSecret));
postdata.insert("redirect_uri", redirectUri.toString());
postdata.insert("grant_type","authorization_code");
QString serviceURL = "oauth2/v4/token";
NetworkManager::GetInstance()->Post(postdata,serviceURL,"https://www.googleapis.com/",[=](int statusCode,int resultnumber, QJsonObject obj){
if (statusCode >= 200 &&
statusCode < 300)
{
// it's ok, do nothing
}
else {
//show error
}
});
}
});
}
void Login::googleLoginButtonPressed() {
int googlePort = 56413;
if(replyHandler == nullptr)
replyHandler = new QOAuthHttpServerReplyHandlerArio(googlePort, this);
google->setReplyHandler(replyHandler);
QObject::connect(replyHandler, &QOAuthHttpServerReplyHandler::tokensReceived, [=](const QVariantMap &map) {
googleToken = map["id_token"].toString();
connect(google, &QOAuth2AuthorizationCodeFlow::granted, [=]() {
auto reply = google->get(QUrl("https://www.googleapis.com/plus/v1/people/me"));
connect_reply = connect(reply, &QNetworkReply::finished, [=]() {
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (statusCode >= 200 &&
statusCode < 300)
{
//NOW register or login the user with email
QJsonDocument jsonResponse = QJsonDocument::fromJson(reply->readAll().data());
email = jsonResponse.object().value("emails").toArray()[0].toObject().value("value").toString();
reply->deleteLater();
}
else {
//error
}
});
});
});
google->grant();
}
what's the problem?
Thanks for your help.
We have posted a lengthy document describing how to authenticate with Google SSO and Qt and this is one of the problems we discuss. I suspect the reason is that the login code returned by Google is URL-encoded, and Qt does not decode it automatically for you. So before you set your replyHandler, you need to invoke setModifyParametersFunction to decode it, in the middle of the flow.
google->setModifyParametersFunction([](QAbstractOAuth::Stage stage, QVariantMap* parameters) {
// Percent-decode the "code" parameter so Google can match it
if (stage == QAbstractOAuth::Stage::RequestingAccessToken) {
QByteArray code = parameters->value("code").toByteArray();
(*parameters)["code"] = QUrl::fromPercentEncoding(code);
}
});

How to manage Facebook login to avoid authentication frequently in Xamarin.Forms?

I use following code for Facebook login and access user information like albums and pictures. I have set code to get access token using following code. Now, the problem is I need to get access token everytime when user open application. However, once user authenticate, application will not ask for authenticate until user close the application. But it will ask for authenticate again after user reopen application. This way user will frustrate if they will ask to authentication everytime they will try to access albums or any other things of facebook.
Is there anyway to skip this? I mean once user provided access of Facebook, application must not ask for login(authenticate). I will have access token but I don't know how to use to play with authentication. So, we can avoid authentication frequently.
My Code:
public class FacebookService : IFacebookService
{
private readonly string[] permissions = { "public_profile", "email", "user_birthday", "user_photos" };
public event EventHandler<FacebookUser> LoginCompleted;
public string Token => AccessToken.CurrentAccessToken.TokenString;
public void Logout()
{
LoginManager manager = new LoginManager();
manager.LogOut();
}
public void LogInToFacebook()
{
if (AccessToken.CurrentAccessToken == null)
{
ObtainNewToken(LogInToFacebook);
return;
}
var fields = new[] { "name", "email", "birthday", "gender", "picture" };
var query = $"/me?fields={string.Join(",", fields)}";
var token = AccessToken.CurrentAccessToken.TokenString;
var request = new GraphRequest(query, null, token, null, "GET");
request.Start((connection, result, error) =>
{
if (error != null)
{
HandleError(error.LocalizedDescription);
}
else
{
var userInfo = result as NSDictionary;
var id = userInfo["id"].ToString();
var email = userInfo["email"].ToString();
var name = userInfo["name"].ToString();
var birthday = userInfo["birthday"].ToString();
var gender = userInfo["gender"].ToString();
var picture = ((userInfo["picture"] as NSDictionary)["data"] as NSDictionary)["url"].ToString();
var args = new FacebookUser(id, email, name, birthday, gender, picture);
LoginCompleted?.Invoke(this, args);
}
});
}
public async System.Threading.Tasks.Task RequestAlbums(Action<FacebookAlbum[]> callback)
{
if (AccessToken.CurrentAccessToken == null)
{
ObtainNewTokenForAlbum(callback);
return;
}
using (HttpClient client = new HttpClient())
{
try
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
var host = "https://graph.facebook.com/";
var json = await client.GetStringAsync($"{host}me/albums");
var data = JObject.Parse(json).First.First.ToString();
var albums = JsonConvert.DeserializeObject<FacebookAlbum[]>(data);
var getPhotosTasks = new List<System.Threading.Tasks.Task>();
foreach (var album in albums)
getPhotosTasks.Add(System.Threading.Tasks.Task.Run(() => RequestPhotos(album)));
await System.Threading.Tasks.Task.WhenAll(getPhotosTasks.ToArray());
callback(albums);
}
catch (Exception ex1)
{
HandleError(ex1.Message);
}
}
}
private void ObtainNewTokenForAlbum(Action<FacebookAlbum[]> callback)
{
var login = new LoginManager();
login.LogInWithReadPermissions(permissions, null, (r, e) =>
{
if (e == null && !r.IsCancelled)
{
RequestAlbums(callback);
}
else
HandleError(e?.LocalizedDescription);
});
}
private async System.Threading.Tasks.Task RequestPhotos(FacebookAlbum album)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
try
{
var host = "https://graph.facebook.com/";
var json = await client.GetStringAsync($"{host}{album.Id}/photos?fields=source,picture");
var data = JObject.Parse(json)["data"].ToString();
album.Photos = JsonConvert.DeserializeObject<FacebookPicture[]>(data);
}
catch (Exception exc)
{
HandleError(exc.Message);
}
}
}
private void ObtainNewToken(Action callback)
{
var login = new LoginManager();
login.LogInWithReadPermissions(permissions, null, (r, e) =>
{
if (e == null && !r.IsCancelled)
callback?.Invoke();
else
HandleError(e?.LocalizedDescription);
});
}
private void HandleError(string messageDescription)
{
messageDescription = messageDescription ?? "Request was cancelled";
_notificationService.DisplayNotification(messageDescription, Colors.d8Red);
}
}
AppDelegate
public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions)
{
UAirship.TakeOff();
RegisterServices();
SetupFacebookSDK();
FFImageLoading.Forms.Touch.CachedImageRenderer.Init();
var dummy = new FFImageLoading.Forms.Touch.CachedImageRenderer();
Xamarin.Forms.Forms.Init();
LoadApplication(new App());
UIApplication.SharedApplication.StatusBarHidden = false;
UIApplication.SharedApplication.SetStatusBarStyle(UIStatusBarStyle.LightContent, false);
_networkManager = new NetworkManager();
OverrideDefaultListViewCustomActionsColors();
UAirship.Push.UserPushNotificationsEnabled = true;
new PhotoAccessChecker();
return ApplicationDelegate.SharedInstance.FinishedLaunching(uiApplication, launchOptions);
}
void SetupFacebookSDK()
{
FacebookProfile.EnableUpdatesOnAccessTokenChange(true);
FacebookSettings.AppID = "000000000049000";
FacebookSettings.DisplayName = "MyProduct";
}
public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
return ApplicationDelegate.SharedInstance.OpenUrl(application, url, sourceApplication, annotation);
}
I guess you forgot initialize FBSDK in AppDelegate.
Check your code if return ApplicationDelegate.SharedInstance.FinishedLaunching (application, launchOptions); has been executed in FinishedLaunching.
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
Settings.AppID = appId;
Settings.DisplayName = appName;
// ...
// This method verifies if you have been logged into the app before, and keep you logged in after you reopen or kill your app.
return ApplicationDelegate.SharedInstance.FinishedLaunching (application, launchOptions);
}
public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
// We need to handle URLs by passing them to their own OpenUrl in order to make the SSO authentication works.
return ApplicationDelegate.SharedInstance.OpenUrl (application, url, sourceApplication, annotation);
}

Accessing user profile picture from iCloud account

I'm attempting to pull a user's profile picture from their iCloud account. I'm using CloudKit and am verifying the user has an iCloud account as well as requesting discoverability permissions. I'm not sure this is possible but if it is I'd like to know how. Here is the relevant code:
To verify iCloud account:
[[CKContainer defaultContainer] accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) {
if (accountStatus == CKAccountStatusAvailable)
{
self.shouldLogin = YES;
}
else
{
self.shouldLogin = NO;
}
}];
To pull their profile image out of their iCloud account:
[[CKContainer defaultContainer] requestApplicationPermission:CKApplicationPermissionUserDiscoverability completionHandler:^(CKApplicationPermissionStatus applicationPermissionStatus, NSError * _Nullable error) {
if (applicationPermissionStatus == CKApplicationPermissionStatusGranted)
{
[[CKContainer defaultContainer] discoverUserInfoWithUserRecordID:self.dataStore.user.userID completionHandler:^(CKDiscoveredUserInfo * _Nullable userInfo, NSError * _Nullable error) {
NSData *imageData = userInfo.displayContact.imageData;
UIImage *profileImage = [UIImage imageWithData:imageData];
self.profileImageView.image = profileImage;
}];
}
}];
You can not get more than an unique ID plus the first and last name of an iCloud user. If you want more information, then you have to ask all users for that data.

Cannot logout from facebook in a windows8 phone app using phonegap. how can I solve this?

Hello guys I am implementing logout from facebook functionality in my windows 8 phone application. By using the given below code I am able to logout from the facebook but when I again click on the facebook login button, then it automatically logged in without asking for the email and password.
var redir_url1 = "http://www.facebook.com/connect/logout_success.html";
//redir_url1 is used to redirect it
alert("inside prototype logout");
//store the value of accesstoken locally in finalAccessTokens
var finalAccessToken1 = window.localStorage.getItem("finalAccessTokens");
alert("finalAccessToken1" + finalAccessToken1);
var authorize_url = "https://www.facebook.com/logout.php?confirm=1";
//alert("authorize_url" + authorize_url);
authorize_url += "next=" + redir_url1;
authorize_url += "&access_token=" + finalAccessToken1;
alert("logout url: " + authorize_url);
resetSession();
showWebPage1(authorize_url);
//call a function to open the webpage
}
function showWebPage1(loc) {
alert("logout loc" + loc);
// var locs=this.loc;
cordova.exec(success1, error1, "InAppBrowser", "ShowInAppBrowser", loc);
}
function success1(e) {
alert("logout success");
//var accessToken = window.localStorage.getItem("finalAccessTokens");
// var url = 'https://graph.facebook.com/me?access_token=' + accessToken;
//localStorage.removeItem(cookies);
//localStorage.removeItem(finalAccessTokens);
// closeAndClearTokenInformation;
//ClearInternetCacheAsync();
alert("After removing access token" + `enter code here`window.localStorage.getItem("finalAccessTokens"));
//finalAccessTokens is used to locally store the value of access token
window.localStorage.clear();
alert("success" + JSON.stringify(e));
var successLogout = JSON.stringify(e);
if ((successLogout.indexOf('https://www.facebook.com/home.php') != -1) &&
(successLogout.indexOf('loadstop') != -1)) {
alert("sss in close");
cordova.exec(null, null, "InAppBrowser", "close", []);
alert("after the handle is closed.....");
this.resetSession();
//to reset the session
}
}
function error1() {
alert("err");
}
FBConnect.prototype.resetSession = function () {
alert("session reset");
this.status = "unknown";
this.session = {};
alert("clear access token/////");
this.session.access_token = null;
alert(this.session.access_token);
this.session.expires = new Date().valueOf() - 1000;
this.session.secret = null;
this.session.session_key = null;
this.session.sig = null;
this.session.uid = null;
alert(this.session.uid);
}
You have to remove WebBrowser cookies after you logout. I am not sure how you can do that using PhoneGap, but in a C#/XAML app you can remove them like this:
await new WebBrowser().ClearCookiesAsync();

Adding downloaded NSImage into an NSArrayController - AWS iOS Asynchronous S3 S3GetObjectRequest

I have a NSTableview with three columns 'Thumbnail','FileName' and 'Datemodified'. The file name column and date modified column is filled with the output of S3ListObjectResponse. While the 'Thumbnail' column image is loaded from the local directory of the machine. If the file is missing, I download it from the S3 cloud. The problem is how to insert the data into the NSArrayController if the file is missing and I'm left with an empty cell for the thumbnail? Here is my work as of now :
- (IBAction)checkCloud:(id)sender {
AmazonS3Client *s3 = [AmazonClientManager s3];
S3ListObjectsRequest* listObjectsRequest = [[S3ListObjectsRequest alloc] initWithName:#"hello-testing"];
NSRange range = NSMakeRange(0, [[_fileListAC arrangedObjects] count]);
[_fileListAC removeObjectsAtArrangedObjectIndexes:[NSIndexSet indexSetWithIndexesInRange:range]];
#try {
S3ListObjectsResponse* response = [s3 listObjects:listObjectsRequest];
NSMutableArray* objectSummaries = response.listObjectsResult.objectSummaries;
//looping through the objSummary and add into the NSArrayController
for ( S3ObjectSummary* objSummary in objectSummaries ) {
NSImage *thumbnail = [[NSImage alloc] init ];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"];
NSDate *s3date = [dateFormatter dateFromString:[objSummary lastModified]];
NSArray *fileName = [[objSummary key] componentsSeparatedByString:#"/"];
if([[fileName objectAtIndex:0] localizedCaseInsensitiveCompare:#"Range"] == NSOrderedSame) {
NSLog(#"file name %# ",[fileName objectAtIndex:1]);
// Check for files in side the bucket's folders
if([[fileName objectAtIndex:1] length]){
NSString *thumbnailFile = [[fileName objectAtIndex:1] stringByAppendingString:#"_thumb.png"];
BOOL isDir;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *filePathPart1 = [#"/Users/" stringByAppendingString:[[NSHost currentHost] localizedName]];
NSString *filePath = [[[[ filePathPart1 stringByAppendingString:#"/Documents/Test/" ] stringByAppendingString:#"Range" ] stringByAppendingString:#"/Thumbnail/"] stringByAppendingString:thumbnailFile];
// If file exists then add into the thumbnail column
if([fileManager fileExistsAtPath:filePath isDirectory:&isDir]){
thumbnail = [[NSImage alloc] initWithContentsOfFile:filePath];
}else{
NSLog(#"file %# not found",thumbnailFile);
//Downloading from the S3 Cloud Asynchronously
doneDownload = NO;
AmazonS3Client *s3 = [AmazonClientManager s3];
S3GetObjectRequest *gor = nil;
#try {
gor = [[S3GetObjectRequest alloc] initWithKey:[[#"Range/Thumbnail/" stringByAppendingString:[fileName objectAtIndex:1]] stringByAppendingString: #"_thumb"] withBucket:#"thumbnail-pic" ];
gor.delegate = self;
[s3 getObject:gor];
}
#catch (AmazonClientException *exception) {
doneDownload = YES;
}
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!doneDownload);
gor.delegate = nil;
}
}else{
//If its the actual bucket folder and not file then load a default image
thumbnail = [NSImage imageNamed:#"Range.png"];
}
}
[_fileListAC addObject:[NSMutableDictionary dictionaryWithObjectsAndKeys:thumbnail,#"thumbnail", [objSummary key],#"Files",s3Date,#"Date Modified", nil]];
}
}
#catch (NSException *exception) {
NSLog(#"Cannot list S3 %#",exception);
}
//Display the table on loading the NSArrayController
[_cloudFileList makeKeyAndOrderFront:nil];
}
I have checked the delegate method
-(void)request:(AmazonServiceRequest *)request didCompleteWithResponse:(AmazonServiceResponse *)response{
NSData *imageData = [response body];
_coreDataImageView.image = [[NSImage alloc] initWithData:imageData];
doneDownload = YES;
}
coredataimageview is a test image well. The image is downloaded but I want to add it back to the array controller. Issue is what if I have 3 file missing from the local directory and I'm unable to insert into array controller for that file name and date modified values. Any help on this?
If you implement a delegate object (rather than using self) you could store these objects in an array and reference them later. The delegate object could track the additional pieces of metadata you are interested in and could be used to construct your table view.