I'm trying to get a small twitter client running and I ran into a problem when testing API calls that require authentication.
My password has special characters in it, so when I try to use the following code it doesn't work.
NSString *post = [NSString stringWithFormat:#"status=%#", [status stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"http://%#:%##%#/statuses/update.json", username, password, TwitterHostname]];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLResponse *response;
NSError *error;
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
I started looking into base64 and putting the authentication into the headers. I found Dave Dribin's post on his base64 implementation, and it seemed to make sense. However when I tried to use it the compiler started complaining about how it couldn't find the openssl libraries. So I read that I needed to link in the libcrypto library, but it doesn't seem to exist for iphone.
I've also read people saying that apple won't allow apps that use crypto libraries, which doesn't make sense to me.
So now I'm kinda stuck and confused. What's the easiest way to get basic authentication into my app?
Cheers
Two things. Firstly you have to use the async methods rather than the synchronous/class method.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:req]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
The authentication is managed by implementing this method in your delegate:
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
And you'll probably also need to implement these methods too:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
Using the async method tends to give a better user experience anyway so despite the extra complexity is worth doing even without the ability to do authentication.
You can directly aslo write download the username & password in main URL i.e https://username:password#yoururl.com/
First of all you need to call NSURLConnection Delegate file:-
(BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
return YES;
}
and then call
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if ([challenge previousFailureCount] == 0)
{
NSURLCredential *newCredential;
newCredential = [NSURLCredential credentialWithUser:#"username"
password:#"password"
persistence:NSURLCredentialPersistenceForSession];
NSLog(#"NEwCred:- %# %#", newCredential, newCredential);
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
}
else
{
[[challenge sender] cancelAuthenticationChallenge:challenge];
NSLog (#"failed authentication");
}
}
Related
i am using bigcommerce api in iphone to fetch data from that so i am doing it with the help of xml parsing but to get the list of orders it is asking for the login into bigcommerce website and then parse the data if anyone help me in this then i will be very thankful , please tell me through xml parsing how we can send login credentials and then hit on the url to parse data.....
thankyou
i am writing this code
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Add the navigation controller's view to the window and display.
[self.window addSubview:navigationController.view];
[self.window makeKeyAndVisible];
// Override point for customization after application launch.
NSString *string=[NSString stringWithFormat:#"https://www.labradorhometraining.com/api/v2"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:string]];
// NSString *dataString = [NSString stringWithFormat:#"{\"screenName\":\"%#\",\"password\":\"%#\",\"pushToken\":\"%#\",\"deviceType\":\"%#\"}", Screentxtf.text,passtxtf.text, str, deviceType];
[request setRequestMethod:#"GET"];
[request appendPostData:[string dataUsingEncoding:NSUTF8StringEncoding]];
// Basic YWRtaW46cGFzc3dvcmQ=
[request addRequestHeader:#"Content-Type" value:#"application/xml"];
[request addRequestHeader:#"Authorization: Basic ZGVtb2tleTpkZW1vdG9rZW4= " value:[NSString stringWithFormat:#"%# %#",#"api", #"c275ab4076f87"]];
[request setUseSessionPersistence:NO];
[request setUseCookiePersistence:NO];
[request setCacheStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
[request setDelegate:self];
[request startAsynchronous];
return YES;
}
and this is in root view controller
-(void)gototselect{
NSString *string=[NSString stringWithFormat:#"https://www.labradorhometraining.com/api/v2/orders.xml"];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:string]];
// NSString *dataString = [NSString stringWithFormat:#"{\"screenName\":\"%#\",\"password\":\"%#\",\"pushToken\":\"%#\",\"deviceType\":\"%#\"}", Screentxtf.text,passtxtf.text, str, deviceType];
[request setRequestMethod:#"PUT"];
// [request appendPostData:[string dataUsingEncoding:NSUTF8StringEncoding]];
[request addRequestHeader:#"Authorization" value:[NSString stringWithFormat:#"%# %#",#"api", #"c2714076f87"]];
[request allowCompressedResponse];
[request setUseSessionPersistence:NO];
[request setUseCookiePersistence:NO];
[request setCacheStoragePolicy:ASICacheForSessionDurationCacheStoragePolicy];
[request setDelegate:self];
[request startAsynchronous];
}
I don't have any idea about ASIHTTPRequest API and also i never used it my career. But using plain objective-C i share some code for how do you authenticate http request,
// Setup NSURLConnection
NSURL *URL = [NSURL URLWithString:url];
NSURLRequest *request = [NSURLRequest requestWithURL:URL
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:30.0];
NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[connection start];
[connection release];
// NSURLConnection Delegates
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge previousFailureCount] == 0) {
NSLog(#"received authentication challenge");
NSURLCredential *newCredential = [NSURLCredential credentialWithUser:#"USER"
password:#"PASSWORD"
persistence:NSURLCredentialPersistenceForSession];
NSLog(#"credential created");
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
NSLog(#"responded to authentication challenge");
}
else {
NSLog(#"previous authentication failure");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
...
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
...
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
...
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
...
}
the above code i copied from NSURLConnection and Basic HTTP Authentication in iOS this link.
Also i want to share some more link with you. Refer apple doc for HTTP request http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/URLLoadingSystem/Articles/AuthenticationChallenges.html
And this post http://www.cocoanetics.com/2010/12/nsurlconnection-with-self-signed-certificates/ .. you will get lot of information and tactics about this work.
I am trying to login a server by sending my username and password by POST method by NSURLConnection on objective-c but I get this from my NSLog
NSLog:
NSLog:
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",string);
What it writes to the console:
404 Not Found
Not Found
Sorry!The page requested was not found.
Why does this happens? Why do I get Not Found page instead of the index page? What do I do wrong? By the way I am trying to connect https not http I don't know does Here is the source code:
#interface ProjectViewController ()
#property NSURLConnection * urlConnection;
#property NSMutableData * responseData;
#end
#implementation ProjectViewController
#synthesize responseData =_responseData;
- (IBAction)LoginButton:(UIButton *)sender {
NSMutableURLRequest *request = nil;
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"https://loginpage.com"]];
NSString *post = [NSString stringWithFormat:#"sid=%#&PIN=%#", #"username", #"password"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
[request setValue:[NSString stringWithFormat:#"%d", [postData length]] forHTTPHeaderField:#"Content-Length"];
[request setTimeoutInterval: 15];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:postData];
_urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
[_urlConnection start];
//[self performSegueWithIdentifier:#"LoginComplete" sender:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.responseData appendData:data];
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#",string);
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
//Oops! handle failure here
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
//NSDictionary *headerFields = [(NSHTTPURLResponse*)response allHeaderFields]; //This would give you all the header fields;
//NSLog(#"%#",headerFields);
}
}
#end
What I just recognized is if I put the part of POST request in to comment it prints the page source to the console but of course I can't send my username and password to the server. I just get the login page's source code but I want to get the source code after I login. Actually I want to redirect to another page by using this session.
This part:
NSString *post = [NSString stringWithFormat:#"sid=%#&PIN=%#", #"username", #"password"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
[request setValue:[NSString stringWithFormat:#"%d", [postData length]] forHTTPHeaderField:#"Content-Length"];
[request setTimeoutInterval: 15];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:postData];
is the certificate used on the website self signed? did you try to make the request in a webview to see if it works there? not in an nsurlrequest?
try this response maybe it will give you some hints.
I have trouble finding info on this topic. Please help me out here.
I need to pass arguments via POST or GET method to my web server and get a reply.
Basically, if using GET method, I want to do something like server.com/?user=john&password=smith and receive the dynamically generated HTML code that is done with my php script. All this without using the web browser on my app.
How is it usually done?
You'll want to look into NSMutableURLRequest and NSURLConnection.
For example, you could use them like this do to a GET request to your server:
- (void)loginUser:(NSString *)username withPassword:(NSString *)password {
// GET
NSString *serverURL = [NSString stringWithFormat:#"http://yourserver.com/login.php?user=%#&pass=%#", username, password];
NSURL *url = [NSURL URLWithString:serverURL];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:req delegate:self];
if (connection) {
connectionData = [[NSMutableData alloc] init];
}
}
This will send an asynchronous GET request to your server with the query string containing username and password.
If you want to send username and password using a POST request, the method would look something like this:
- (void)loginUser:(NSString *)username withPassword:(NSString *)password {
// POST
NSString *myRequestString = [NSString stringWithFormat:#"user=%#&pass=%#",username,password];
NSData *myRequestData = [NSData dataWithBytes: [myRequestString UTF8String] length: [myRequestString length]];
NSURL *url = [NSURL URLWithString:#"http://yourserver.com/login.php"];
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setHTTPMethod: #"POST"];
[req setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"content-type"];
[req setHTTPBody: myRequestData];
NSURLConnection *connection = [NSURLConnection connectionWithRequest:req delegate:self];
if (connection) {
connectionData = [[NSMutableData alloc] init];
}
}
In order to get the response from the server, you will need to implement the NSURLConnection delegate methods, for example:
#pragma mark -
#pragma mark NSURLConnection delegate methods
#pragma mark -
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
// Called if you have an .htaccess auth. on server
NSURLCredential *newCredential;
newCredential = [NSURLCredential credentialWithUser:#"your_username" password:#"your_password" persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:newCredential forAuthenticationChallenge:challenge];
}
-(void) connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[connectionData setLength: 0];
}
-(void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[connectionData appendData:data];
}
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[connectionData release];
}
-(void) connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *content = [[NSString alloc] initWithBytes:[connectionData bytes]
length:[connectionData length] encoding: NSUTF8StringEncoding];
// This will be your server's HTML response
NSLog(#"response: %#",content);
[content release];
[connectionData release];
}
References:
NSMutableURLRequest Class Reference
NSURLConnection Class Reference
Hope this helps :)
Usually this is done using a NSURLConnection. You can also use NSString's method stringWithContentsOfURL.
Can anyone tell me the way how I can make a synchronous call to the https server? I am able to do asynchronous request on https server using following delegate methods.
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
and
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
but I need to do synchronous.
//Encoding the request
NSData *postData = [xmlText dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
**//Calculating length of request**
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:requestUrlString]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/xml" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSURLResponse* response;
NSError* error = nil;
//Capturing server response
NSData* result = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
+ (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse **)response error:(NSError **)error
in NSUrlConnection should work just fine with https.
If you'd like to provide credentials, they need to be part of the url: (https://username:password#domain.tld/api/user.json).
There's no way to provide a NSURLConnection delegate, so if you need some nonstandard authentication handling you'll need to do it asynchronously.
That's how i did it:
instead of
[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]
I made the same method instance based, on the containing class, since we will need a delegate. And don't make it singleton, so every connection has its independent variables, because, if we don't, and two connections happen to be called before the other finishes, then the received data and the handling of the loops will be intertwined irrecoverably.
[[ClassNameHere new] sendSynchronousRequest:request returningResponse:&response error:&error]
This way i can create an NSUrl connection and handle it (in a synchronous way, we'll see how) so i don't have to change any of the previously written code.
- (NSData *)sendSynchronousRequest:(NSURLRequest *)request returningResponse:(NSURLResponse *__strong*)response error:(NSError *__strong*)error
{
_finishedLoading=NO;
_receivedData=[NSMutableData new];
_error=error;
_response=response;
NSURLConnection*con=[NSURLConnection connectionWithRequest:request delegate:self];
[con start];
CFRunLoopRun();
return _receivedData;
}
- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace {
return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
}
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
//handle the challenge
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
*_response=response;
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[_receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
*_error=error;
CFRunLoopStop(CFRunLoopGetCurrent());
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
CFRunLoopStop(CFRunLoopGetCurrent());
}
The trick was in the CFRunLoopRun() and CFRunLoopStop(CFRunLoopGetCurrent())
I hope it helps someone else in the futur.
I have a WCF service hosted and I'm trying to use it within an iPhone app as a JSON POST request. I plan on using the JSON serializer later, but this is what I have for the request:
NSString *jsonRequest = #"{\"username\":\"user\",\"password\":\"letmein\"}";
NSLog(#"Request: %#", jsonRequest);
NSURL *url = [NSURL URLWithString:#"https://mydomain.com/Method/"];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
NSData *requestData = [NSData dataWithBytes:[jsonRequest UTF8String] length:[jsonRequest length]];
[request setHTTPMethod:#"POST"];
[request setValue:#"application/json" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody: requestData];
[NSURLConnection connectionWithRequest:[request autorelease] delegate:self];
In my data received I'm just printing it to the console:
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSMutableData *d = [[NSMutableData data] retain];
[d appendData:data];
NSString *a = [[NSString alloc] initWithData:d encoding:NSASCIIStringEncoding];
NSLog(#"Data: %#", a);
}
And within this response I get this message:
Data: <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Request Error</title>
<style>
<!-- I've took this out as there's lots of it and it's not relevant! -->
</style>
</head>
<body>
<div id="content">
<p class="heading1">Request Error</p>
<p xmlns="">The server encountered an error processing the request. Please see the <a rel="help-page" href="https://mydomain.com/Method/help">service help page</a> for constructing valid requests to the service.</p>
</div>
</body>
</html>
Does anyone know why this happens and where I'm going wrong?
I've read about using ASIHTTPPost instead but I can't get the demo to run in iOS4 :(
Thanks
EDIT:
I've just used to CharlesProxy to sniff out what's happening when I call from the iPhone (Simulator) and this is what it gave me:
The only thing strange I've noticed is it says "SSL Proxying not enabled for this host: enable in Proxy Settings, SSL locations". Does anyone know what this means? (This also happens in my working windows client).
I've just ran this on my windows client and the only thing that is different is the Request and Response text which is encrypted. Am I missing something in order to use a secure HTTP connection to do this?
NEW EDIT:
This works with a non-HTTPS request such as:
http://maps.googleapis.com/maps/api/geocode/json?address=UK&sensor=true. So I guess the problem is getting the iPhone to send the POST properly over SSL?
I am also using these methods:
- (void) connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]
forAuthenticationChallenge:challenge];
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
- (void) connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
{
return YES;
}
}
This allows me to get this far. If I don't use them I get an error sent back saying:
The certificate for this server is invalid. You might be connecting to a server that is pretending to be “mydomain.com” which could put your confidential information at risk.
FIXED!
Changed:
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
to:
[request setValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
You also need these two methods:
- (void) connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
{
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
{
[challenge.sender useCredential:[NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]
forAuthenticationChallenge:challenge];
}
[challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
- (void) connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
if([[protectionSpace authenticationMethod] isEqualToString:NSURLAuthenticationMethodServerTrust])
{
return YES;
}
}
I don't know Objective C at all, but try setting the Accept header to application/json instead of application/jsonrequest.
change
NSData *requestData = [NSData dataWithBytes:[jsonRequest UTF8String] length:[jsonRequest length]];
to
NSData *requestData = [jsonRequest dataUsingEncoding:NSUTF8StringEncoding];
dic is NSMutable Dictionary with object and keys.
baseUrl is your server webservice url.
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic
options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSString *encodedString = [jsonString base64EncodedString];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#wsName",baseUrl]];
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url];
[theRequest addValue: #"text/xml; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[theRequest setHTTPMethod:#"POST"];
[theRequest setValue:encodedString forHTTPHeaderField:#"Data"];