iOS Parse.com Facebook Login - valid email address error - ios

Using the sample code, I continually get a nil user and an error in the block for the login:
2015-11-09 17:39:16.017[90448:3746935] [Error]: You must use a valid email address. (Code: 142, Version: 1.9.1)
Printing description of error:
Error Domain=Parse Code=142 "You must use a valid email address." UserInfo=0x7fb403c39f20 {code=142, temporary=0, error=You must use a valid email address., NSLocalizedDescription=You must use a valid email address.}
Here's the code:
FBSDKAccessToken *accessToken = [FBSDKAccessToken currentAccessToken];
if (accessToken)
{
[PFFacebookUtils logInInBackgroundWithAccessToken:accessToken block:^(PFUser * _Nullable user, NSError * _Nullable error) {
if (!user) {
NSLog(#"Facebook login cancelled.");
} else {
NSLog(#"User now has publish permissions!");
[self loadFBAccountInfo];
}
}];
}
else
{
[PFFacebookUtils logInInBackgroundWithReadPermissions:#[ #"email", #"public_profile" ] block:^(PFUser *user, NSError *error) { // , #"publish_actions"
if (!user) {
NSLog(#"Facebook login cancelled.");
} else {
NSLog(#"User now has publish permissions!");
[self loadFBAccountInfo];
}
}];
}
Any help is appreciated!

Sorry, the error was in the cloud code which required an email address - when a user is first created via the Facebook route, it doesn't have an email address...
Parse.Cloud.beforeSave(Parse.User, function(request, response) {
var email = request.object.get('email');
if (!email) {
response.error('You must use a valid email address.');
} else {
response.success();
}
});

Related

iOS Quickblox error: Request failed: unauthorized (401)

I have been working on quickblox framework integration. I follow https://quickblox.com/developers/IOS link for integration, but unfortunately I stop at signup user step
Signup code
QBUUser *user=[[QBUUser alloc] init];
user.login=#"abc";
user.password=#"12345678";
[QBRequest signUp:user successBlock:^(QBResponse * _Nonnull response, QBUUser * _Nonnull tUser) {
NSLog(#"Successfully signup:-%#",tUser);
} errorBlock:^(QBResponse * _Nonnull response) {
NSLog(#"Error in signup:- %#",response);
}];
Every time it will return
error: Request failed: unauthorized (401)
reasons: {
errors = {
base = (
"Required session does not exist"
);
};
}
How can I resolve this?
Here is working code for (Swift 4)
let user = QBUUser()
user.login = "abc"
user.password = "12345678"
QBRequest.signUp(user, successBlock: { response, tUser in
print("Successfully signup:-\(tUser)")
}, errorBlock: { response in
print("Error in signup:- \(response)")
})
Output :

QuickBlox video chat: QBRequest.logInWithUserEmail vs QBChat.instance().connectWithUser

I have a simple QuickBlox chat app built by following the iOS tutorial:
http://quickblox.com/developers/Sample-webrtc-ios#Sources
I've successfully created a user and logged them in. However, I run into an error when I try to initiate a session: "You have to be logged in in order to use Chat API".
let newSession: QBRTCSession = QBRTCClient.instance().createNewSessionWithOpponents(["12498970"], withConferenceType: QBRTCConferenceType.Video)
I'm able to resolve this by adding QBChat.instance().connectWithUser each time I open it:
QBChat.instance().connectWithUser(user!) { (error) in
if error != nil {
print("error: \(error)")
}
else {
print("login to chat succeeded")
}
}
But somehow this seems weird because I have to either cache the password or prompt the user to enter their password each time the app opens. It seems strange that the QBSession.currentSession().currentUser is still valid, but the QBChat user has been invalidated. What is the best practice for accomplishing this? In all the samples, the passwords are hardcoded. This doesn't seem like a great solution.
I ended up following examples in Q-municate, which is an app the Quickblox folks built to basically demonstrate their whole package, as well as provide an actual solution for whatever your chat needs are. I have some other custom stuff and don't need a lot of the functionality so I'm still trying to dig through the details of how they implement it. The link to Q-municate:
http://quickblox.com/developers/Q-municate#1._Get_the_source_code.
In their login flow, they use the QMApi module written for Q-municate:
[[QMApi instance] loginWithEmail:email
password:password
rememberMe:weakSelf.rememberMeSwitch.on
completion:^(BOOL success)
{
[SVProgressHUD dismiss];
if (success) {
[[QMApi instance] setAutoLogin:weakSelf.rememberMeSwitch.on
withAccountType:QMAccountTypeEmail];
[weakSelf performSegueWithIdentifier:kTabBarSegueIdnetifier
sender:nil];
}
}];
In loginWithEmail, their settingsManager caches this login:
[weakSelf.settingsManager setLogin:email andPassword:password];
which is actually just a way to cache the password in SSKeyChain.
[SSKeychain setPassword:password forService:kQMAuthServiceKey account:login];
Later, when you return to the app, they call autologin:
if (!self.isAuthorized) {
if (self.settingsManager.accountType == QMAccountTypeEmail && self.settingsManager.password && self.settingsManager.login) {
NSString *email = self.settingsManager.login;
NSString *password = self.settingsManager.password;
[self loginWithEmail:email password:password rememberMe:YES completion:completion];
}
else if (self.settingsManager.accountType == QMAccountTypeFacebook) {
[self loginWithFacebook:completion];
}
else {
if (completion) completion(NO);
}
}
else {
if (completion) completion(YES);
}
where self.settingsManager.password pulls the password from SSKeychain:
NSString *password = [SSKeychain passwordForService:kQMAuthServiceKey account:self.login];
autoLogin is called when the main chat tab is loaded. That makes our classic call to connectToChat:
[[QMApi instance] autoLogin:^(BOOL success) {
if (!success) {
[[QMApi instance] logoutWithCompletion:^(BOOL succeed) {
//
[weakSelf performSegueWithIdentifier:#"SplashSegue" sender:nil];
}];
} else {
// subscribe to push notifications
[[QMApi instance] subscribeToPushNotificationsForceSettings:NO complete:^(BOOL subscribeToPushNotificationsSuccess) {
if (!subscribeToPushNotificationsSuccess) {
[QMApi instance].settingsManager.pushNotificationsEnabled = NO;
}
}];
[weakSelf connectToChat];
}
}];
So technically the docs are doing the right thing by logging in to chat every time the app opens and chat is no longer connected. There's just a much more complex but secure way to store that password so the user doesn't have to reenter it.
TLDR: The way it works in my code (and in swift) is:
On login:
QBRequest.logInWithUserEmail(email, password: password, successBlock: { (response, user) in
SSKeychain.setPassword(password, forService: "kMyAppLoginServiceKey", account: email)
}) { (errorResponse) in
print("Error: \(errorResponse)")
self.simpleAlert("Could not log in", defaultMessage: nil, error: nil)
}
Whenever the chat view loads:
if !QBChat.instance().isConnected() {
QBRTCClient.initializeRTC()
QBRTCClient.instance().addDelegate(self)
let user = QBSession.currentSession().currentUser
let password = SSKeychain.passwordForService("kMyAppLoginServiceKey", account: user?.email!)
user!.password = password
QBChat.instance().addDelegate(self)
QBChat.instance().connectWithUser(user!) { (error) in
if error != nil {
print("error: \(error)")
}
else {
print("login to chat succeeded")
}
}
}

iOS Parse validating user

I want to write code in AppDelegate.m that checks if PFUser.currentUser credentials are still valid. The reason I am doing this is for this scenario. Assume user logged in successfully and now currentUser has the basic information of that user. If the user changes the password at some time, when application launch, it should check if those credentials are up to date or not. If email & password doesn't match the one in table, it should log the user out.
I tried to do this but apparently PFUser.currentUser.password is always set to null while PFUser.currentUser.email has the actual value of email used to log in. How can I achieve this validation without the password being accessible?
Here is the code I have for guidance:
PFQuery *query = [PFQuery queryWithClassName:#"User"];
[query whereKey:#"objectId" equalTo: PFUser.currentUser.objectId];
[query whereKey:#"password" equalTo: PFUser.currentUser.password];
[query getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error) {
if (!object) {
/*Credentials changed! Logout user and request login again*/
NSLog(#"Credentials Changed!");
[PFUser logOut];
[[[navigationController viewControllers] objectAtIndex:0] performSegueWithIdentifier:#"loginSegue" sender:self];
}
else {
/*Credentials are still valid..proceed!*/
NSLog(#"Credentials Correct!");
[[[navigationController viewControllers] objectAtIndex:0] performSegueWithIdentifier:#"skipLoginSegue" sender:self];
}
}];
When this failed I tried to NSLog the password and got null so I understood that this was the problem.
I would write a cloud code function using the master key: Parse.Cloud.useMasterKey();
When you include this in your function you will be able to access/check the user's password and send back a "verified" variable or something of that sort.
Here is my cloud code function to modify a user, but you can easily modify it to verify a user's info. There are also many answers on the parse forums on this as well as lots of info in the docs.
Parse.Cloud.define("editUser", function(request, response) {
//var GameScore = Parse.Object.extend("SchoolHappening");
// Create a new instance of that class.
//var gameScore = new GameScore();
Parse.Cloud.useMasterKey();
var query = new Parse.Query(Parse.User);
query.get(request.params.myUser, {
success: function(myUser) {
// The object was retrieved successfully.
myUser.set("cabinetPosition", request.params.myPosition);
// Save the user.
myUser.save(null, {
success: function(myUser) {
// The user was saved successfully.
response.success("Successfully updated user.");
},
error: function(gameScore, error) {
// The save failed.
// error is a Parse.Error with an error code and description.
response.error("Could not save changes to user.");
}
});
},
error: function(object, error) {
// The object was not retrieved successfully.
// error is a Parse.Error with an error code and description.
}
});
});
If you want to verify your current password, you can store your password locally, then use loginWithUsernameInBackground: to verify. Something like,
[PFUser logInWithUsernameInBackground:[PFUser currentUser].username
password:password
block:^(PFUser *user, NSError *error) {
}];

How to get user email address via Twitter API in iOS?

I have tried multiple SDK's but was unable to get an email ID from any of the resources. I have tried FHSTwitterEngine for this purpose but I didn't get the solution.
FHSTwitterEngine *twitterEngine = [FHSTwitterEngine sharedEngine];
NSString *username = [twitterEngine loggedInUsername]; //self.engine.loggedInUsername;
NSString *key = [twitterEngine accessToken].key;
NSString *secrete = [twitterEngine accessToken].secret;
if (username.length > 0)
{
NSDictionary *userProfile = [[FHSTwitterEngine sharedEngine] getProfileUsername:username];
NSLog(#"userProfile: %#", userProfile);
EDIT
After Twitter has updated APIs, Now user can get Email using TWTRShareEmailViewController class.
// Objective-C
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController* shareEmailViewController = [[TWTRShareEmailViewController alloc] initWithCompletion:^(NSString* email, NSError* error) {
NSLog(#"Email %#, Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController animated:YES completion:nil];
} else {
// TODO: Handle user not signed in (e.g. attempt to log in or show an alert)
}
// Swift
if Twitter.sharedInstance().session {
let shareEmailViewController = TWTRShareEmailViewController() { email, error in
println("Email \(email), Error: \(error)")
}
self.presentViewController(shareEmailViewController, animated: true, completion: nil)
} else {
// TODO: Handle user not signed in (e.g. attempt to log in or show an alert)
}
NOTES:
Even if the user grants access to her email address, it is not guaranteed you will get an email address. For example, if someone signed up for Twitter with a phone number instead of an email address, the email field may be empty. When this happens, the completion block will pass an error because there is no email address available.
Twitter Dev Ref
PAST
There is NO way you can get email address of a twitter user.
The Twitter API does not provide the user's email address as part of the OAuth token negotiation process nor does it offer other means to obtain it.
Twitter Doc.
You will have to use Twitter framework. Twitter has provided a beautiful framework for that, you just have to integrate it in your app.
To get user email address, your application should be whitelisted. Here is the link. Go to use this form. You can either send mail to sdk-feedback#twitter.com with some details about your App like Consumer key, App Store link of an App, Link to privacy policy, Metadata, Instructions on how to log into our App etc..They will respond within 2-3 working days.
Here is the story how I got whitelisted by conversation with Twitter support team:
Send mail to sdk-feedback#twitter.com with some details about your App like Consumer key, App Store link of an App, Link to privacy policy, Metadata, Instructions on how to log into our App. Mention in mail that you want to access user email adress inside your App.
They will review your App and reply to you withing 2-3 business days.
Once they say that your App is whitelisted, update your App's settings in Twitter Developer portal. Sign in to apps.twitter.com and:
On the 'Settings' tab, add a terms of service and privacy policy URL
On the 'Permissions' tab, change your token's scope to request email. This option will only been seen, once your App gets whitelisted.
Put your hands on code
Use of Twitter framework:
Get user email address
-(void)requestUserEmail
{
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController *shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString *email, NSError *error) {
NSLog(#"Email %# | Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController
animated:YES
completion:nil];
} else {
// Handle user not signed in (e.g. attempt to log in or show an alert)
}
}
Get user profile
-(void)usersShow:(NSString *)userID
{
NSString *statusesShowEndpoint = #"https://api.twitter.com/1.1/users/show.json";
NSDictionary *params = #{#"user_id": userID};
NSError *clientError;
NSURLRequest *request = [[[Twitter sharedInstance] APIClient]
URLRequestWithMethod:#"GET"
URL:statusesShowEndpoint
parameters:params
error:&clientError];
if (request) {
[[[Twitter sharedInstance] APIClient]
sendTwitterRequest:request
completion:^(NSURLResponse *response,
NSData *data,
NSError *connectionError) {
if (data) {
// handle the response data e.g.
NSError *jsonError;
NSDictionary *json = [NSJSONSerialization
JSONObjectWithData:data
options:0
error:&jsonError];
NSLog(#"%#",[json description]);
}
else {
NSLog(#"Error code: %ld | Error description: %#", (long)[connectionError code], [connectionError localizedDescription]);
}
}];
}
else {
NSLog(#"Error: %#", clientError);
}
}
Hope it helps !!!
If you'd like a user's email address, you'll need to ask a user for it within the confines of your own application and service. The Twitter API does not provide the user's email address as part of the OAuth token negotiation process nor does it offer other means to obtain it.
In Swift 4.2 and Xcode 10.1
It's getting email also.
import TwitterKit
#IBAction func onClickTwitterSignin(_ sender: UIButton) {
TWTRTwitter.sharedInstance().logIn { (session, error) in
if (session != nil) {
let name = session?.userName ?? ""
print(name)
print(session?.userID ?? "")
print(session?.authToken ?? "")
print(session?.authTokenSecret ?? "")
let client = TWTRAPIClient.withCurrentUser()
client.requestEmail { email, error in
if (email != nil) {
let recivedEmailID = email ?? ""
print(recivedEmailID)
}else {
print("error--: \(String(describing: error?.localizedDescription))");
}
}
let storyboard = self.storyboard?.instantiateViewController(withIdentifier: "SVC") as! SecondViewController
self.navigationController?.pushViewController(storyboard, animated: true)
}else {
print("error: \(String(describing: error?.localizedDescription))");
}
}
}
Swift 3-4
#IBAction func btnTwitterAction(_ sender: Any) {
TWTRTwitter.sharedInstance().logIn(completion: { (session, error) in
if (session != nil) {
print("signed in as \(String(describing: session?.userName))");
if let mySession = session{
let client = TWTRAPIClient.withCurrentUser()
//To get User name and email
client.requestEmail { email, error in
if (email != nil) {
print("signed in as \(String(describing: session?.userName))");
let firstName = session?.userName ?? "" // received first name
let lastName = session?.userName ?? "" // received last name
let recivedEmailID = email ?? "" // received email
}else {
print("error: \(String(describing: error?.localizedDescription))");
}
}
//To get user profile picture
client.loadUser(withID: session?.userID, completion: { (userData, error) in
if (userData != nil) {
let fullName = userData.name //Full Name
let userProfileImage = userData.profileImageLargeURL //User Profile Image
let userTwitterProfileUrl = userData?.profileURL // User TwitterProfileUrl
}
})
}
} else {
print("error: \(error?.localizedDescription)");
}
})
}

Fetching Current User Profile using Objective C Google Plus Client Library

I am using the Google CLient Libraries for Objective C available here..
I have successfully been able to Authorize the user and get refresh token. (Using the GTMOAuthenticaion api embedded within).
In the Selector called after successful authorization I make the Get User Profile request as follows.. (I need the id of currently loggedin/authenticated user)
-(void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
if (error != nil) {
NSLog(#"Stop");
} else {
if ([auth canAuthorize]){
[Mediator plusService].authorizer = auth;
// Problematic Line
GTLQueryPlus *profileQuery = [GTLQueryPlus queryForPeopleGetWithUserId:#"me"]; // Notice the UserId Param
profileQuery.completionBlock = ^(GTLServiceTicket *ticket, id object, NSError *error) {
if (error == nil) {
self.mediator.gProfile = object;
} else {
NSLog(#"GPlus Service Error %#", error);
}
};
[[Mediator plusService] executeQuery:profileQuery completionHandler:
^(GTLServiceTicket *ticket, id result, NSError *error) {
if (error)
NSLog(#"Some Service Error %#", error);
}];
}
}
}
If I put "me" as parameter, I get invalid user ID error string in jSON response.
However, If I provide some userId like my own 113632923069489732066 it works perfectly fine and returns the appropriate jSON response..!!
The Example for Google Plus inside Examples folder also fails to get current user profile ending with following error.
Error Domain=com.google.GTLJSONRPCErrorDomain Code=400 "The operation couldn’t be completed. (Invalid user ID: {0})" UserInfo=0x7a670fa0 {NSLocalizedFailureReason=(Invalid user ID: {0}), GTLStructuredError=GTLErrorObject 0x7a67b130: {message:"Invalid user ID: {0}" code:400 data:[2]}, error=Invalid user ID: {0}}
P.S. My API Console application doesn't work with iOS option under installed app but needs be configured with "Other" option. When configured with iOS option, the oAuth fails with invalid_client error response.
My Mistake .. !! And a very silly one .. !!
I was signing in using a Gmail Account that was yet not associated with GPlus .. !! =/

Resources