HTTP Authentication Headers for IIS windows authentication - iphone

I have a web site hosted on IIS with windows authentication. I am trying to access it in one of my iPhone web application. Presently i am using this code, but it is not working.
NSString *authString = [[[NSString stringWithFormat:#"%#:%#", #"myusername", #"mypassword"]dataUsingEncoding:NSUTF8StringEncoding] base64Encoding];
authString = [NSString stringWithFormat: #"Basic %#", authString];
**[requestObj setValue:authString forHTTPHeaderField:#"Authorization"];**
my web app is hosted with windows authentication. but here i am using basic. can any one post what is the correct http header for it.
Thanks..

I think the main difference is you need to specify the domain you are authenticating against as well as the username and password. Something like this should work. I've used a synchronous request for brevity, ideally you should use an ASINetworkQueue or NSOperationQueue to perform the request.
NSString *username = #"test";
NSString *password = #"test";
NSString *domain = #"test";
NSURL *url = [NSURL URLWithString:#"http://myurl"];
ASIHTTPRequest *request = [[[ASIHTTPRequest alloc] initWithURL:url] autorelease];
[request setUseSessionPersistence:YES];
[request setUsername:username];
[request setPassword:password];
[request setDomain:domain];
[request start];
if ([request error]) {
if ([[request error] code] == ASIAuthenticationErrorType) {
//Authentication failed
}
} else {
NSLog([request responseString]);
}
I don't have access to a Windows server to test this, but I have tested NTLM in the past so it should work... :)

Windows authentication (NTLM) isn't as simple as basic authentication. NTLM requires more than one webrequest to negotiate the security so there isn't a static HTTP header you can send to log in.

You can use the third-party ASIHTTPRequest library to perform NTLM over HTTP authentication.

I'm not 100% sure it supports NTLM Authentication but have you investigated the connection:didReceiveAuthenticationChallenge method on the NSUrlConnection?

Related

how to compress request using ASIFormDataRequest?

I'm trying to to send some post data to a Apache server from iPad application using the ASIHttp library.
actually I need to send huge data to the server and that means I need to compress the request body so I write some code to send the data and compress the request BUT there are no parameters received on the server !!!
the iOS code is :
NSURL * URL = [NSURL URLWithString:myURL];
ASIFormDataRequest *ASIRequest = [ASIFormDataRequest requestWithURL:URL];
ASIRequest.shouldCompressRequestBody=YES;
ASIRequest setPostValue:data forKey:#"data"];
[ASIRequest startSynchronous];
NSError *error = [ASIRequest error];
if (!error) {
NSString *response = [ASIRequest responseString];
NSLog(#"response %#" , response);
}
PS: if I removed the ASIRequest.shouldCompressRequestBody=YES; everything works fine and I can see the data but when use it I see nothing on the server
the request can be seen on the server but with no parameter
noway to send such data over GET method.
the server configuration are fine.
any solution ? any comment or idea can help ?
By default, most web servers do not support compression on POSTs. The accepted answer here does a really good job explainining it: Why can't browser send gzip request?
According to official documentation, this feature has only been tested with Apache servers.
EDIT:
Here is a code snipt that compresses the actual post data:
if ([self shouldCompressRequestBody]) {
NSError *err = nil;
NSData *compressedBody = [ASIDataCompressor compressData:[self postBody] error:&err];
if (err) {
[self failWithError:err];
return;
}
[self setCompressedPostBody:compressedBody];
[self setPostLength:[[self compressedPostBody] length]];
}
Source: http://forums.three20.info/discussion/77/tturlrequest-vs-asihttprequest/p1

why can't my iphone app connect to my server?

i have a weird problem. so i've been working on an iphone app where a user can login, and upon logging in, the app would authorize the user by making an http GET request to the server. if the username and password is correct it return an authorization token (typical stuff).
the weird part is that when i test this locally, pointing the http request url to my local machine, it works fine. everything authorizes correctly and all data is returned correctly.
but today, i got the app stable enough that i decided to deploy rest api to my server, but when changed the http request url to my server url, the requests fails.
i thought maybe the server wasn't deployed correctly so i tested the http endpoints with the firefox rest client and everything seems to be working, authorization works, and data gets returned.
does anyone have any thoughts? i'm lost as to what the problem might be.
i'm using AFNetworking library to make the requests.
here's my code:
self.apiHttpClient = [[TKRHttpClient alloc] initWithBaseURL:[NSURL URLWithString:#"http://mywebsite.com/api"]];
--------------
NSString *username = [usernameField text];
NSString *password = [passwordField text];
TKRHttpClient *httpClient = [TKRHttpClient sharedInstance];
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:#"/user/auth" parameters:nil];
[request addValue:username forHTTPHeaderField:#"username"];
[request addValue:password forHTTPHeaderField:#"password"];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
if(response.statusCode == 200){
NSDictionary * headers = [response allHeaderFields];
NSString *token = [headers objectForKey:#"auth_token"];
NSLog(#"token was %#", token);
[[AppController sharedAppController] initializeDataForUserToken:token];
}else{
NSLog(#"upload failed!");
}
} failure:^(NSURLRequest *request , NSHTTPURLResponse *response , NSError *error , id JSON ) {
NSLog(#"code was: %i", response.statusCode);
if(response.statusCode == 401){
[errorMessage setText:#"Incorrect password or username"];
}
}];
[operation start];
i put in break points, and it seems that the request goes straight to the failure block. the line that says "NSLog(#"code was: %i", response.statusCode);" in the failure block prints out 200. but when i tail the logs on my server, no request was received.
any help would be greatly appreciated. can't figure out for the life of my why it would work against my local tomcat, but won't work when deployed to my server.
thanks in advance!
i found the reason for my problem thanks to #Brad's suggestion.
upon inspecting the request in charles, i found that the request was made as "http://mywebsite.com", then for path it was /user/auth. so the "/api" part was missing.
maybe this is the standard, i don't know, but it appears that AFHTTPClient's (my TKRHttpClient extends AFHTTPClient) initWithBaseURL really means the host URL. the "/api" part gets lopped off.
and the reason why it was working locally is because i deployed the app locally as root, so there was no "/api"
so two ways to fix this. 1. redeploy as app to server as root. or 2. change the line of code from:
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:#"/user/auth" parameters:nil];
TO:
NSMutableURLRequest *request = [httpClient requestWithMethod:#"GET" path:#"api/user/auth" parameters:nil];
hopefully this helps anyone using AFNetworking in the future.

Twitter API Rate Limit - Overcoming on an unauthenticated JSON Get with Objective C?

I see the rate limit is 150/hr per IP. This'd be fine, but my application is on a mobile phone network (with shared IP addresses).
I'd like to query twitter trends, e.g. GET /trends/1/json.
This doesn't require authorization, however what if the user first authorized with my application using OAuth, then hit the JSON API?
The request is built as follows:
- (void) queryTrends:(NSString *) WOEID {
NSString *urlString = [NSString stringWithFormat:#"http://api.twitter.com/1/trends/%#.json", WOEID];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self startImmediately:YES];
if (theConnection) {
// Create the NSMutableData to hold the received data.
theData = [[NSMutableData data] retain];
} else {
NSLog(#"Connection failed in Query Trends");
}
//NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
}
I have no idea how I'd build this request as an authenticated one however, and haven't seen any examples to this effect online.
I've read through the twitter OAuth documentation, but I'm still puzzled as to how it should work.
I've experimented with OAuth using Ben Gottlieb's prebuild library, and calling this in my first viewDidLoad:
OAuthViewController *oAuthVC = [[OAuthViewController alloc]
initWithNibName:#"OAuthTwitterDemoViewController" bundle:[NSBundle mainBundle]];
// [self setViewController:aViewController];
[[self navigationController] pushViewController:oAuthVC animated:YES];
This should store all the keys required in the app's preferences, I just need to know how to build the GET request after authorizing!
Maybe this just isn't possible? Maybe I'll have to proxy the requests through a server side application?
Any insight would be appreciated!
Authorizing through OAuth will provide you an authorization token, which you need to pass to each request you make later on.
Refer to Twitter docs, read about how authorization works.
Okay, after a lot of searching I've managed to figure how to construct a request to the JSON API programmatically in Xcode.
Firstly, you need to use the OAuth demo code to authenticate and authorize your application.
Then, you'll be retrieving the key by doing: [prefs stringForKey:#"authData"] - if this doesn't exist, you haven't been OAuth'd properly.
I had to reverse engineer this by looking through the code of the OAuth library, and while it's easy to use the library for stuff like sending a status update, it doesn't allow you to retrieve trends...:
#import "OAMutableURLRequest.h"
#import "MGTwitterHTTPURLConnection.h"
NSMutableString *dataString;
// Using OAuth:
OAConsumer *consumer = [[OAConsumer alloc] initWithKey:#"YOURCONSUMERKEY"
secret:#"YOURCONSUMERSECRET"];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *authData = [prefs stringForKey:#"authData"];
// [_engine
OAMutableURLRequest *theRequest = [[[OAMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://stream.twitter.com/1/statuses/filter.json"]
consumer:consumer
token: (authData) ? [[OAToken alloc] initWithHTTPResponseBody:authData] : nil
realm: nil
signatureProvider:nil] autorelease];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody: [httpBody dataUsingEncoding:NSUTF8StringEncoding]];
[theRequest setHTTPShouldHandleCookies:NO];
// Set headers for client information, for tracking purposes at Twitter.
[theRequest setValue:#"Trendy" forHTTPHeaderField:#"X-Twitter-Client"];
[theRequest setValue:#"1.0" forHTTPHeaderField:#"X-Twitter-Client-Version"];
[theRequest setValue:#"http://www.inisdesign.com" forHTTPHeaderField:#"X-Twitter-Client-URL"];
// Set the request body if this is a POST request.
[theRequest prepare];
// Create a connection using this request, with the default timeout and caching policy,
// and appropriate Twitter request and response types for parsing and error reporting.
MGTwitterHTTPURLConnection *connection;
connection = [[MGTwitterHTTPURLConnection alloc] initWithRequest:theRequest
delegate:self
requestType:MGTwitterFollowedTimelineRequest // Wrong type
responseType:MGTwitterStatuses]; // as above - doesnt seem to matter
if (!connection) {
return;
} else {
// [_connections setObject:connection forKey:[connection identifier]];
// [connection release];
dataString = [[NSMutableData data] retain];
[connection start];
}
}
The rest is implemented as a standard URL connection with didReceiveData methods etc..
I haven't verified this is alleviating my rate limiting problems, but hey.. It's a start if anybody has similar problems.

How to retrieve a response from server for the request sent?

I am developing an application that connects to a remote web server and exchanges data with the web server frequently. First screen of my application provides login screen that authenticates user.
I am able to authenticate user on the web server by sending a request to the server but unable to get response from the server to display success alert to the user on the iphone. In clear, I am not getting any response.
The server I'm using is developed in Java.
I am using the following to send the request to server.
NSString *post = #"username=";
post = [post stringByAppendingString:username];
post = [post stringByAppendingString:#"&password="];
post = [post stringByAppendingString:password];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:#"http://example.com/login.action?"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody:postData];
NSURLConnection *conn=[[NSURLConnection alloc] initWithRequest:request delegate:self];
And,
- (void)connectionNSURLConnection *)connection didReceiveDataNSData *)data
{
[receivedData appendData:data];
NSString *ReturnStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"In \"didReceiveData()\" : receivedData = %#",receivedData);
NSLog(#"Return String : %#", ReturnStr);
}
This function is never called.
What should I do to receive response for the request I have sent?
A brief collection of my queries:
Example: Login screen that validates a user in Java server
I am sending request that I am able to see at the console of my server application.
I'm unable to get response for the request.
In which format the response data must be sent?
Would someone provide a clear picture of data exchange between the iPhone application and a Java servlet?
Update
I have implemented these three methods also. But no response from server. Can you guide me the process of developing LOGIN application that communicates with a JAVA server by requests and responses? I am stuck with communicating.
Yeah, my server is receiving request from iphone. I am able to see the request that is sent to the server.
I am not able to understand how the data exchange between iphone application and JAVA server takes place. I'm a newbie to this iphone development.
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[responseData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[responseData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSString *failureMessage = [NSString stringWithFormat:#"Connection failed: %#", [error description]];
NSLog(failureMessage);
}
Try implementing these three methods as well, you might be getting an error or no response. You can tell by using these methods.
you should verify that the server is actually recieving the request from the iphone.
a simple php script should return something.
<?php
echo print_r($_GET, true);
echo print_r($_POST, true);
?>
create a dummy app that makes a request to the script and displays the response
I'm not sure what authentication you refer to with LOGIN, but if the server requires HTTP Auth, you need to implement the delegate method
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
I am using the following to send the request to server.
The code looks OK, but where is this code and what happens before and after the code you've shown us? Is it in a handler for a button press? Is it in a background thread?
For the asynchronous load you are using to work properly, you need to be on a thread with a default run loop, e.g. the main thread. And this code must return control to the run loop, i.e. must not busy wait doing something else.
Another thing to check is your server - is it in fact sending a response? If you send the same request from a browser, what content length do you get back?
Edit: it may help if you also show the java action handler on the server side. For example, does it send a HTTP redirect if the login is successful? And are you implementing the following delegate method:
- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse
...and if so, what does that method look like. I'm wondering if you've maybe implemented this method and not got the right logic in there, thus stopped a redirect from happening.
U can do it this way...
-(NSString* )createWebServiveRequest:(NSString *)urlLink{
NSURL *url = [NSURL URLWithString:urlLink];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60];
NSData *returnData = [NSURLConnection sendSynchronousRequest:urlRequest returningResponse: nil error: nil ];
NSString *responseString = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
return responseString;
}
call the above method with the NSString containing url.....and capture it in the method call.

user credential persistence in iPhone

Since i'm new to iPhone programming, I want to do something like that: In the main screen of my app, i ask for username and password and authorize user using these info calling .net web service. In this main screen, there is a switch controller "Remember me" so that app will remember the user next time he run the app. My questions are:
1. how can i call .net web services in iPhone?
2. how can i persist these username and password information (like cookie in web app), so that user will not be asked for credential info?
i would really appreciate, thanks.
Persisting user credentials
If you are storing a username and password you should use the Keychain. Using the Keychain on iPhone is analogous to Keychain on the Mac.
To use Keychain import "Security/Security.h".
Apple has an example here of adding and retrieving a username and password in their documentation. The example methods
- (void)mySetObject:(id)inObject forKey:(id)key;
- (id)myObjectForKey:(id)key;
- (void)resetKeychainItem;
will enable you to persist and retrieve your user credentials almost without modifying the example code.
Calling webservices that require authentication
Depending on you authentication scheme you can either
provide username and password directly as parameters in the URL using NSURLConnection
provide a didReceiveAuthenticationChallenge method in your NSURLConnection delegate if your webservice use the "Basic Authentication" scheme
you use nsmutableurlrequest to generate a web services request
use nsuserdefaults to persist the username and password
Here's an example of the first:
-(void)sendUpdate {
User *user = [User sharedManager];
NSData *parkedLocation = [[NSString stringWithFormat:#"<location><latitude>%f</latitude><longitude>%f</longitude><userid>%#</userid><udid>%#</udid></location>",
coordinate.latitude, coordinate.longitude, userid, user.udid] dataUsingEncoding:NSASCIIStringEncoding];
//NSLog("parkedlocation = %#", parkedLocation);
NSString *URLstr = LOCATIONS_URL;
if (controller.put_url)
URLstr = controller.put_url;
NSURL *theURL = [NSURL URLWithString:URLstr];
//NSURLRequest *theRequest = [NSURLRequest requestWithURL:theURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
NSLog(#"IN SEND UPDATE put_url = %#", controller.put_url);
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:theURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
if (controller.put_url) {
[theRequest setHTTPMethod:#"PUT"];
} else {
[theRequest setHTTPMethod:#"POST"];
}
[theRequest setValue:#"text/xml" forHTTPHeaderField:#"Content-type"];
[theRequest setHTTPBody:parkedLocation];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:TRUE];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (!theConnection) {
NSLog(#"COuldn't get a connection to the iParkNow! server");
}
}
and an example of the second:
[[NSUserDefaults standardUserDefaults] setObject:self.userName.text forKey:#"Username"];
[[NSUserDefaults standardUserDefaults] setObject:self.password.text forKey:#"Password"];
You have two choices: User Preferences or Core Data. Both are capable - Core Data might be overkill.
NSURLRequest can be used to retrieve URLs such as your authentication service. And NsUserDefaults can be used to save settings in between app invocations.
Note: Check out ennuikiller's awesome example.