user credential persistence in iPhone - 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.

Related

cachedResponseForRequest method not being accessed

I am trying to set up a cache, however the method I am using 'as below' is not being accessed by the thread.
- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse
I am initializing the connection like this, and connectionDidFinishLoading is accessed so I am not sure what I am missing.
- (IBAction)searchRequest:(NSData *)postBodyData
{
//Set database address
NSMutableString *databaseURL = [[NSMutableString alloc] initWithString:#"https://127.0.0.1:88"];
NSURL *url = [NSURL URLWithString:databaseURL];
NSString *postLength = [NSString stringWithFormat:#"%d", [postBodyData length]];
//SynchronousRequest to grab the data, also setting up the cachePolicy
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0]; //if request dose not finish happen within 60 second timeout.
// NSInputStream *fileStream = [NSInputStream inputStreamWithData:postBodyData];
[request setHTTPMethod: #"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/octet-stream" forHTTPHeaderField:#"content-type"];
[request setHTTPBody:postBodyData];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:request delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [NSMutableData data];
} else {
// Inform the user that the connection failed from the connection:didFailWithError method
}
}
any help would be appreciated.
connection:willCacheResponse: is only called in cases when the response will be cached. POST requests are not cacheable in most cases. (More details: Is it possible to cache POST methods in HTTP?)
You should probably look at something like MKNetworkKit which handles a lot of this kind of caching, particularly for REST protocols.
You can also look at Drop-in offline caching for UIWebView. You'd have to modify it significantly, but NSURLProtocol can be used to solve this kind of problem. AFCache is currently working to integrate this approach, and is another toolkit to consider. (Read through the comments in the blog post for more background on the issues.)

Facebook page in UIWebView

I have a UIWebView in my application where I 'm loading the facebook profile of my application. I am using facebook's single sign on SDK(Graph API). And I logged in into facebook in the Safari in background using my application. But When I try loadmy game profile in my application web view still I could see "Log In" and "Sign Up" buttons.
I tried setting cookies, main document URL and relative URL too but no use.
I am adding here my code too here,
NSURL *url = [NSURL URLWithString:#"http://www.facebook.com/myapp" relativeToURL:[NSURL URLWithString:#"http://login.facebook.com"]];
NSMutableURLRequest *request = nil;
if(url)
request = [NSMutableURLRequest requestWithURL:url];
NSArray * availableCookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookiesForURL:[NSURL URLWithString:#"http://login.facebook.com"]];
NSDictionary * headers = [NSHTTPCookie requestHeaderFieldsWithCookies:availableCookies];
if(request)
{
[request setHTTPShouldHandleCookies:YES];
[request setMainDocumentURL:[NSURL URLWithString:#"http://login.facebook.com"]];
[request setAllHTTPHeaderFields:headers];//login.facebook.com
[mWebView loadRequest:request];
}
You may be running into the situation I describe in the answer on this page:
Facebook iOS SDK not storing cookies for access

creating JSON request URL in Objective C via DropBox SDK for iPhone

I'm rather new to trying to figure out how JSON and the DropBox API function with the iphone SDK that DB has release for the iPhone. I understand how both technologies work in general and that you have to build a dictionary into a request and get another dictionary back, but I can't find a solid example of how to do anything specifically enough online in Objective C.
I just need one example of how to retrieve for instance the user's profile information by creating a JSON request to fetch info from the drop-box server.
I've been able to log the user in and linking the device using the consumer key and consumer secret but what's next I'm a little at a loss.
MANY thanks for any guidance or examples.
To send your data
Example for POST methods:
url = #"https://api.dropbox.com/<version>/account/"
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:value forKey:#"callback"];
Example for GET methods (URL queries):
NSString *urlString = [[[NSString alloc] initWithString:#"https://api.dropbox.com/<version>/account/info
"] autorelease];
urlString = [urlString stringByAppendingFormat:#"?callback=whatever&status_in_response=something"];
NSURL *url = [[NSURL alloc] initWithString:urlString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setDidFinishSelector:#selector(requestDidFinishForThreadID:)];
[request startAsynchronous];
To retrieve JSON values and convert them into Dictionary
SBJsonParser *json = [[SBJsonParser alloc] init];
NSDictionary *dict = (NSDictionary*)[json objectWithString:responseString];
You will need JSON Framework: http://code.google.com/p/json-framework/
And also ASIHTTPRequest: http://allseeing-i.com/ASIHTTPRequest/
Haven't tested it with dropbox, but should be this way.

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.

HTTP Authentication Headers for IIS windows authentication

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?