I have used zkSForce in my iOS application to log in to SalesForce. Now I need to access the same data in sandbox environment for the same application.
But when I am trying to log in I am getting the following error.
Error Domain=APIError Code=0 "The operation couldn’t be completed. (APIError error 0.)" UserInfo=0xad3ef20
{faultstring=INVALID_LOGIN: Invalid username, password, security token; or user locked out., faultcode=INVALID_LOGIN}
But when I am trying to connect to production environment I am able to log in successfully. Is there any specific set of instructions to follow to connect to Sandbox using zkSForce? Thanks in advance.
--EDIT--
I am using the following method to log in.
[SFAccountManager setLoginHost:#"test.salesforce.com"];
[SFAccountManager setClientId:CLIENTID];
[SFAccountManager setRedirectUri:REDIRECTURI];
[SFAccountManager setCurrentAccountIdentifier:USERNAME];
[SFAccountManager sharedInstance].coordinator.credentials.instanceUrl=[NSURL URLWithString:#"https://test.salesfoce.com"];
[[FDCServerSwitchboard switchboard] loginWithUsername:username password:passwordToken target:self selector:#selector(loginResult:error:)];
and the result is handled by the following method
- (void)loginResult:(ZKLoginResult *)result error:(NSError *)error{
if (result && !error){
[[SFAccountManager sharedInstance].coordinator.credentials setAccessToken:nil];
[SFAccountManager sharedInstance].coordinator.credentials.accessToken = result.sessionId;
}
else if (error){
UIAlertView *myAlertView = [[UIAlertView alloc] initWithTitle:#"Login failed" message:#"Error" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles: nil];
[myAlertView show];
}
}
Login requests for Sandbox need to goto a different endpoint than the default one for production, so you need to call setLoginProtocolAndHost: on the client object before calling login, e.g.
ZKSforceClient *c = [[ZKSforceClient alloc] init];
[c setLoginProtocolAndHost:#"https://test.salesforce.com"];
[c login:self.username password:self.password];
...
I am not sure if this is the perfect solution but adding the following line worked for me.
[[FDCServerSwitchboard switchboard] setApiUrlFromOAuthInstanceUrl:LOGINHOSTURL];
where LOGINHOSTURL is you host URL.
Thanks #superfell for giving me the hint
Related
GTMOAuth 2.0 seems like an excellent tool for OAuth 2.0 verification on iOS. I am trying to retrieve the full name and email of a Google user by implementing GTMOAuth-2 in Xcode but am having a bit of trouble. Based on this answer: Retrieve User email using GTM OAuth2 for iOS, it should be as easy as calling auth.userEmail. However, the problem is that calling auth.userEmail in the following code segment always return null:
- (void)viewController:(GTMOAuth2ViewControllerTouch * )viewController
finishedWithAuth:(GTMOAuth2Authentication * )auth
error:(NSError * )error
{
NSLog(#"finished");
NSLog(#"auth access token: %#", auth.accessToken);
[self.navigationController popToViewController:self animated:NO];
if (error != nil) {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Error Authorizing with Google"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
} else {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Success Authorizing with Google"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
NSLog(#"email: %#",auth.userEmail);
}
The code runs successfully and retrieves an access token, but auth.userEmail is always null. Do I need to make a request to the Google email endpoint using GTMOAuth 2.0's Fetcher object, or otherwise send an additional HTTP GET request to retrieve the user's email using auth.accessToken?
I recently worked on Google OAuth2 for logging user in with gmail
by following tutsplus's tutorial and It gave me the desired results.I
would recommend you to follow this link. This provides methods to
login and logout and also email address of logged in user. Google
OAuth2
. And to get email address of logged in user, add this in scopes
https://www.googleapis.com/auth/userinfo.email. and code will look
like this
[_googleOAuth authorizeUserWithClienID:#"YOUR CLIENT ID"
andClientSecret:#"SECRET"
andParentView:self.view
andScopes:[NSArray arrayWithObjects:#"https://www.googleapis.com/auth/userinfo.profile",#"https://www.googleapis.com/auth/userinfo.email", nil]];
And for GTM OAuth 2.0, add this
scopehttps://www.googleapis.com/auth/userinfo.email .Hope this helps
you.
I am working on an iOS chat app where user login to app. I've downloaded XMPPFramework from GitHub XMPPFramework. I am trying to connect XMPP framework with Openfire server by following this tutorial. Here is my code to connect XMPP to openfire.
- (BOOL)connect {
[self setupStream];
[xmppStream setHostName:#"192.168.1.5"];
[xmppStream setHostPort:5222];
NSString *jabberID = [[NSUserDefaults standardUserDefaults] stringForKey:#"userID"];
NSString *myPassword = [[NSUserDefaults standardUserDefaults] stringForKey:#"userPassword"];
if (![xmppStream isDisconnected])
return YES;
if (jabberID == nil || myPassword == nil)
return NO;
[xmppStream setMyJID:[XMPPJID jidWithString:jabberID]];
password = myPassword;
NSError *error = nil;
if (![xmppStream isConnected])
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:[NSString stringWithFormat:#"Can't connect to server %#", [error localizedDescription]]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
return NO;
}
return YES;
}
The problem is when I run the app, it shows the alert can't connect to server. I have checked many questions on StackOverflow and tried googling but couldn't find any relevant solution. How to connect it to the Openfire serve? If I am doing anything wrong in my code please suggest me with a snippet of code or a tutorial to make this happen.
A host of possibilities.
Try adding break points at xmppStreamDidConnect and xmppStreamDidAuthenticate.
If xmppStreamDidConnect isn't reached, the connection is not established; you've to rectify your hostName.
If xmppStreamDidAuthenticate isn't reached, the user is not authenticated; you've to rectify your credentials i.e. username and/or password.
One common mistake is omitting of #domainname at the back of username i.e. username#domainname e.g. keithoys#openfireserver where domain name is openfireserver.
Hope this still relevant, if not, hopefully it will help others.
There are some issues with your code:
I don't see the call to connect, you should add something like this:
NSError *error = nil;
if (![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error]) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error connecting"
message:#"Msg"
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];
}
Most of the XMPP API is asynchronous.
You have to set the stream delegate in order to receive events.
Check out XMPPStreamDelegate and XMPPStream#addDelegate
If you don't want to go through the code yourself XMPPStream.h
, you can implement all methods of XMPPStreamDelegate and log the events. This will help you understand how the framework works.
Hope this helps, Yaron
I have an app where the user can authenticate with Instapaper. They need an Instapaper subscription to be able to do this, however, so if they try to log in with an account that isn't subscribed to Instapaper, I want to display an error to them.
But when they try to log in, AFNetworking sees it as successful, then displays this error to the console:
Error: Error Domain=AFNetworkingErrorDomain Code=-1011 "Expected
status code in (200-299), got 400" UserInfo=0x8374840
{NSLocalizedRecoverySuggestion=[{"error_code": 1041, "message":
"Subscription account required", "type": "error"}],
AFNetworkingOperationFailingURLRequestErrorKey=https://www.instapaper.com/api/1/bookmarks/list>,
NSErrorFailingURLKey=https://www.instapaper.com/api/1/bookmarks/list,
NSLocalizedDescription=Expected status code in (200-299), got 400,
AFNetworkingOperationFailingURLResponseErrorKey=}
All I'm using is AFXAuthClient which is a modification of AFNetworking. I subclassed it to create a custom Instapaper API client that looks like this:
#import "AFInstapaperClient.h"
#import "AFJSONRequestOperation.h"
#implementation AFInstapaperClient
+ (AFInstapaperClient *)sharedClient {
static AFInstapaperClient *sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedClient = [[AFInstapaperClient alloc] initWithBaseURL:[NSURL URLWithString:#"https://www.instapaper.com/"]
key:#"..."
secret:#"..."];
});
return sharedClient;
}
- (id)initWithBaseURL:(NSURL *)url {
if (self = [super initWithBaseURL:url]) {
[self registerHTTPOperationClass:[AFJSONRequestOperation class]];
[self setDefaultHeader:#"Accept" value:#"application/json"];
}
return self;
}
#end
And when they log in, the following code is executed:
- (IBAction)doneButtonPressed:(UIBarButtonItem *)sender {
[[AFInstapaperClient sharedClient] authorizeUsingXAuthWithAccessTokenPath:#"/api/1/oauth/access_token"
accessMethod:#"POST"
username:self.loginBox.text
password:self.passwordBox.text
success:^(AFXAuthToken *accessToken) {
// Save the token information into the Keychain
[UICKeyChainStore setString:accessToken.key forKey:#"InstapaperKey"];
[UICKeyChainStore setString:accessToken.secret forKey:#"InstapaperSecret"];
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Login Successful"
message:#"Your articles are being downloaded now and will appear in your queue."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles: nil];
[alert show];
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"IsLoggedInToInstapaper"];
[self dismissViewControllerAnimated:YES completion:nil];
}
failure:^(NSError *error) {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Login Failed."
message:#"Are you connected to the internet? Instapaper may also be down. Try again later."
delegate:nil
cancelButtonTitle:#"Okay"
otherButtonTitles: nil];
[alert show];
}];
}
But the code never goes into the failure block. How could I modify my code so that it would allow me to tell them they need an Instapaper subscription account?
Based on your situation, I don't think you will ever trigger the failure block because your request isn't failing. You are getting a response from the web service. In my experience the failure block only executes if you fail to get a response because of something like network availability or something like it.
Therefore, you need to handle the account error in the success block. One way you could do it is to read the status code that is returned in the response. If the status code is 400 like your console is showing then alert the user.
You can follow the method used here "https://stackoverflow.com/q/8469492/2670912"
It seems with this implementation, as WeekendCodeWarrior said, it will deem it successful even though they won't be able to make further requests. The code that spat out the error was actually an NSLog that I did (whoops, didn't realize it was my code outputting that) after making a request as I assumed all was fine.
My solution was just to make a request to the API in that success block, check the result of that request (which does have a response object returned) and then act accordingly on the response object.
I'm trying to post userid and password and retrieve user information using HttpRequest. I'm getting expected result, but client had reported the app is crashing after authentication. He had sent a video which shows how the app crashes. From that i concluded the app is crashing after displaying nserror message in uialertview from
- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError
The error message i'm getting is "The operation couldn't be completed. (NSXMLParserErrorDomain error 39.)"
After showing this message in alert view the app is crashing suddenly. I'm not facing this crash anyhow. Can anyone help me to sort out this. Thanks in advance.
The code i have used in parseErrorOccured is
NSString *errorMessage = [error localizedDescription];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Connection Error!" message:errorMessage
delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
[alertView release];
From experience, [error localizedDescription] or [error localizedFailureReason] may return a nil NSString.
In your code, you should check to see if errorMessage is nil, if it is try setting errorMessage to [error localizedFailureReason]. I'd check for a NULL value again.
SHORT STORY
Using gtm-oauth2 for iOS and FOSOAuthServerBundle in Symfony2 to implement my own Oauth2 server I am not getting the callBack finishedSelector to be invoked.
This is where the "special" ViewController is created:
GTMOAuth2ViewControllerTouch * viewController;
viewController = [[GTMOAuth2ViewControllerTouch alloc] initWithAuthentication:myAuth
authorizationURL:authURL
keychainItemName:nil
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
What are the reasons that might make finishedSelector, (the implemented method viewController:finishedWithAuth:error) not to be invoked?
The behavior I get is that the login page is properly rendered, but it acts as the starting point of the whole web application, rendering the rest of the pages once it is logged-in instead of returning the control to the finishedSelector and, finally, to the view controller that has to manage the continuation of the APP workflow.
LONG STORY
Using gtm-oauth2 and FOSOAuthServerBundle in Symfony2, I am experiencing problems trying to make the arquitecture to catch the login and load the authenticated session from my iOS APP.
I am following the instructions described in the gtm-oauth2 documentation, particularly the Signing in to non-Google Services part.
Doing what it is described there, I have this method for creating the auth object:
- (GTMOAuth2Authentication * ) authForMyAPP
{
//This URL is defined by the individual 3rd party APIs, be sure to read their documentation
NSString * url_string = #"http://myHost/oauth/v2/token";
NSURL * tokenURL = [NSURL URLWithString:url_string];
// We'll make up an arbitrary redirectURI. The controller will watch for
// the server to redirect the web view to this URI, but this URI will not be
// loaded, so it need not be for any actual web page. This needs to match the URI set as the
// redirect URI when configuring the app.
NSString * redirectURI = #"http://myHost/oauth/v2/falseCallBack";
GTMOAuth2Authentication * myAuth;
myAuth = [GTMOAuth2Authentication authenticationWithServiceProvider:#"MyAPP"
tokenURL:tokenURL
redirectURI:redirectURI
clientID:kMyClientID
clientSecret:kMyClientSecret
];
//[myAuth setTokenType:#"Bearer"];
return myAuth;
}
And then, this method creates the "special" viewController that should handle the render of the login page and returning the control when the login is performed:
- (void)signInToMyAPP()
{
GTMOAuth2Authentication *myAuth = [self authForMyAPP];
NSString* auth_string = #"http://127.0.0.1/~pgbonino/Symfony/web/app.php/oauth/v2/auth";
NSURL * authURL = [NSURL URLWithString:auth_string];
// Display the authentication view
// Creates the "special" viewController passing the `auth` object, the authorization URL and the finishedSelector
GTMOAuth2ViewControllerTouch * viewController;
viewController = [[GTMOAuth2ViewControllerTouch alloc] initWithAuthentication:myAuth
authorizationURL:authURL
keychainItemName:nil
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
[self.navigationController pushViewController:viewController animated:YES];
}
Finally, I have the method used for that finishedSelector. It should be called once the login is properly performed and the authentication has succeeded (or an error has come). THAT IS WHAT I AM NOT GET DONE:
- (void)viewController:(GTMOAuth2ViewControllerTouch *)viewController
finishedWithAuth:(GTMOAuth2Authentication *)myAuth
error:(NSError *)error
{
if (error != nil)
{
// Authentication failed
UIAlertView *alertView = [ [UIAlertView alloc] initWithTitle:#"Authorization Failed"
message:[error localizedDescription]
delegate:self
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
else
{
// Authentication succeeded
// Assign the access token to the instance property for later use
//self.accessToken = myAuth.accessToken;
[myAuth setShouldAuthorizeAllRequests:YES];
[[Singleton sharedSingleton] setAuth:myAuth];
// Display the access token to the user
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Authorization Succeeded"
message:[NSString stringWithFormat:#"Access Token: %#", myAuth.accessToken]
delegate:self
cancelButtonTitle:#"Dismiss"
otherButtonTitles:nil];
[alertView show];
}
}
This all is supposed to render my login page in a web view and catch the successful login to call the viewController:finishedWithAuth:error and save the session in some shared object.
Nevertheless, the behavior I am getting is that I get rendered the login in the web view, I correctly login and, instead oF the delegated selector gets invoked, it just normally logs in the application and the next page is loaded in the web view, as if it was in a normal browser. So the callback is not performed.
Why am I not getting the selector to be called? Any idea?
IMPORTANT NOTE: the Oauth2 server works perfectly: if I call the token URL and the callBack url from Safari, everything works well. Tokens and auths codes are correctly saved in database.
Forget it.
It was just me.
OAuth2 won't work with Symfony2 and FOSUserBundle while this parameter is set to true in config.yml:
always_use_default_target_path: false