How to get cookies from WKWebView - ios

I have this WKWebView which loads a login page and what I need is the "iPlanetDirectoryPro" cookie (see picture bellow) that is set after a successful login (form submit). So, I'm trying to store it in order to use it in another WKWebView.. The funny thing is "sharedHTTPCookieStorage" contains the other cookies but not the "iPlanetDirectoryPro" one.
What I tried so far:
Created a shared proccess pool and used the same configuration for this first WKWebView and the one I'm trying to use "iPlanetDirectoryPro" on.
I used this delegate method decidePolicyForNavigationResponse to fetch cookies:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];
for (NSHTTPCookie *cookie in cookies) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
Evaluating JavaScript command document.cookie on webView.
Any ideas?

For whatever reason, WKWebView and HTTPCookieStorage aren't working perfectly together. You would need to manage the cookies yourself, for example you could look here

Related

How to get cookie from WKwebview (IOS)

I'd like to receive a cookie containing login status through WKwebview.
what I finally want is receiving that cookie data, parsing them,and then changing the view for the user logged in.
What I've tired :
webview.evaluateJavascript("document.cookie.search('LoginSession=Y')") { (data,error) -> .....
}
result : if the data is 'data >= 1', login status(a variable in IOS app) = true, but under 0(data < 0),login status will be false.
and it works like a charm seemingly for my app.
However, This way looks very a physical and simple way, so I think, it could be not secure for some users, and It might have no guarantee for working perfectly for all environments with IOS.
Q1 : Is it not dangerous way?
Q2 : I've heard that IOS stores the cookies in Memory unlike other platforms, and we could manage to load the cookie data from Memory through some codes. Are there any recommended libraries for developers to handle cookies from WKWEB?
I tried this. Javascript returns a single string with all cookies in form "key=value;"
I don't know how stable it is. Hope it helps.
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
[webView evaluateJavaScript:#"document.cookie;" completionHandler:^(NSString *result, NSError *error)
{
NSLog(#"Error getting cookies: %#",error);
[self updateCookies:result];
}];
}
Courtesy : Siyu Song's Answer
You can have access to an NSHTTPURLResponse object in - webView:decidePolicyForNavigationResponse:decisionHandler: method defined on WKNavigationDelegate. You can later extract the cookies manually from the HTTP header:
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:[NSURL URLWithString:#""]];
for (NSHTTPCookie *cookie in newCookies) {
// Do something with the cookie
}
decisionHandler(WKNavigationResponsePolicyAllow);
}
Please post your solution if you have a better one.

UIWebView not save cookies

I use UIWebView that users login in accounts. Users may login with facebook account. He is click button Facebook and opens UIWebView. After login UIWebView close and users may use your personal account. But when I close my app and open it again users not login. UIWebView not save cookies.
I found this answer https://stackoverflow.com/a/26006163
And added this code in my app. This only works temporarily. I close my app and open through hours it again users not login.
I tried to change this line
[cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:2629743] forKey:NSHTTPCookieExpires];
to this
[cookieProperties setObject:[[NSDate date] dateByAddingTimeInterval:100*12*30*60*60] forKey:NSHTTPCookieExpires];
But it did not help me.
Cookies are temporary and it doest miraculously come back when you relaunch the app.
you need to save the cookies or the credential in keychain and get it back once you relaunch.
I had such a problem. I tried many ways. I decided use dirty hack :D
That's my way:
When I was getting NSHTTPURLResponse for facebook (or else) i save request url to NSUserDefaults:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse*)response;
if ([[httpResponse URL].absoluteString isEqualToString:#"http://www.simple.com/"])
{
[[NSUserDefaults standardUserDefaults] setURL:self.url forKey:#"urlLogin"];
[self dismissViewController];
}
}
And when I open my App i use NSURLRequest with my stored url:
NSURLRequest *request = [NSURLRequest requestWithURL:[[NSUserDefaults standardUserDefaults] URLForKey:#"urlLogin"] cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:3.0];
[NSURLConnection connectionWithRequest:request delegate:self];

Steps to use SharePoint Rest interfaces in iOS

I am developing an app for SharePoint online and wanted to use the SharePoint Rest interfaces in my ios app. Can Some one please tell me the steps to use SharePoint Rest interfaces in iOS
I got it, below are the steps to be followed:
Include RestKit in your ios app.
Create a UIView in your home screen and load the login page.
load http: //server name/Pages/default.aspx in the UIWebView
In webViewDidFinished method find out the Fed Auth token and append it with the request URL
- (void)webViewDidFinishLoad:(UIWebView *)webView {
//Retrive HTTPOnly Cookie
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookiesArray = [storage cookies];
//Search for Fed Auth cookie
for (NSHTTPCookie *cookie in cookiesArray) {
if ([[cookie name] isEqualToString:#"FedAuth"]) {
/*** DO WHATEVER YOU WANT WITH THE COOKIE ***/
NSLog(#"Found FedAuth");
NSURL *url=[NSURL URLWithString:#"http://my server/_vti_bin/listdata.svc"];
RKClient *client = [RKClient clientWithBaseURL:url];
client.requestQueue.requestTimeout = 10;
client.cachePolicy = RKRequestCachePolicyNone;
client.authenticationType = RKRequestAuthenticationTypeHTTPBasic;
client.username = #"username";
client.password = #"Password";
NSString *cookieVale=[cookie value];
NSString *getResourcePath=[#"?" stringByAppendingFormat:#"%#",cookieVale];
[client get:getResourcePath delegate:self];
break;
}
}
}
And here you can find the response.
- (void)request:(RKRequest *)request didLoadResponse:(RKResponse *)response {
id xmlParser = [[RKParserRegistry sharedRegistry] parserForMIMEType:RKMIMETypeXML];
NSError *error = nil;
id parsedResponse = [xmlParser objectFromString:[response bodyAsString] error:&error];
RKLogInfo(#"Parsed response : %#, error:%#",parsedResponse,error);
if ([response isSuccessful]) {
NSLog(#"%d",[response isCreated]);
// Response status was 200..299
if ([response isCreated] && [response isJSON]) {
// Looks like we have a 201 response of type application/json
RKLogInfo(#"The JSON is %#", [response bodyAsJSON]);
}
} else if ([response isError]) {
// Response status was either 400..499 or 500..599
RKLogInfo(#"Ouch! We have an HTTP error. Status Code description: %#", [response localizedStatusCodeString]);
}
}
The self accepted answer lost me lots of hours of trials and errors. It omits some key aspects like the fact that you also need to grab the rtFa cookie. And what's up with client.username = #"username" and client.password = #"Password" provided in the users code. What is that? Note that the client does not know the username or password at any moment...
AAAnyway, below is a great article which will guide you in the right direction:
http://www.codeproject.com/Articles/571996/Development-of-iPhone-client-application-for-Share
And this describes how to get the cookies without using a UIWebView
http://allthatjs.com/2012/03/28/remote-authentication-in-sharepoint-online/
Send the FedAuth cookie with all your subsequent Requests.
Once authenticated, you can call the REST API, documentation here:
http://msdn.microsoft.com/en-us/library/fp142385(v=office.15).aspx#bk_determining
When the user finish the sign in process towards a Office365 Sharepoint instance, the web view will be redirected in several steps. As one of the final steps before loading the actual Sharepoint web site, the web view will be asked to load "about:blank".
Detect when you web view starts loading "about:blank" and you know when the user finished the sign in process and can close the web view. Example code below.
// Load page in web view
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSLog(#"WebView should start loading %#", request.URL.absoluteString);
// Detect that the user finished the sign in process
if ([request.URL.absoluteString isEqualToString:#"about:blank"]) {
// Do your stuff here
return NO;
}
return YES;
}
The Sharepoint instance will also set the FedAuth cookie if the authentication was successful. The cookie must be included in future requests to the server.
You do not have to append the cookie manually, this will be taken care of by the URL loading system as long as the cookies has been accepted and stored in the NSHTTPCookieStorage and you are sending the request to the same server.
From Apple documentation
The URL loading system automatically sends any stored cookies
appropriate for an NSURLRequest. unless the request specifies not to
send cookies. Likewise, cookies returned in an NSURLResponse are
accepted in accordance with the current cookie acceptance policy.

iOS - problems with NSURLAuthenticationChallenge - connection successful but can't access server

I am using NSURLAuthenticationChallenge to log in to a webserver through my app. All the server requires is a username and a password. Here is what's happening:
(1) Ping server with POST message containing a User-Agent string in the HTML header
(2) Server responds with an authentication challenge which is detected by the didReceiveAuthenticationChallenge delegate method
(3) Respond by sending a challenge response using username and password:
NSURLCredential *cred = [[NSURLCredential alloc] initWithUser:unameTextField.text
password:pswdTextField.text
persistence:NSURLCredentialPersistenceForSession];
[[challenge sender] useCredential:cred forAuthenticationChallenge:challenge];
(4) If username/password are correct, delegate method connectionDidFinishLoading gets called, detecting that the challenge response was accepted by the server. User is now logged in and can send/receive messages from the server. (If username/password are incorrect, delegate method didFailWithError gets called and user is shown an alert.)
Here's where it's going wrong: the very first time I open my Xcode project and run the app and attempt to login with the correct username/password, there is a time lag of 10-15 seconds between steps 3 and 4. And then even after connectionDidFinishLoading is called, when I send messages to the server requesting files it responds by sending me the HTML login page which is the default behavior for unauthenticated requests...so it seems as though I'm not logged in after all.
If I stop and then run the app again there is no lag and everything works fine.
EDIT: I solved the above problem by clearing the URLCache, all cookies and all credentials before each login attempt. Code for these 3 methods is below:
- (void)clearCookiesForURL
{
NSURL *loginUrl = [NSURL URLWithString:#"https://www.MYURL.com"];
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookies = [cookieStorage cookiesForURL:loginUrl];
for (NSHTTPCookie *cookie in cookies)
{
[cookieStorage deleteCookie:cookie];
}
}
- (void)eraseCredentials
{
NSString *urlString = #"www.MYURL.com";
NSURLCredentialStorage *credentialsStorage = [NSURLCredentialStorage sharedCredentialStorage];
NSDictionary *allCredentials = [credentialsStorage allCredentials];
if ([allCredentials count] > 0)
{
for (NSURLProtectionSpace *protectionSpace in allCredentials)
{
if ([[protectionSpace host] isEqualToString:urlString])
{
NSDictionary *credentials = [credentialsStorage credentialsForProtectionSpace:protectionSpace];
for (NSString *credentialKey in credentials)
{
[credentialsStorage removeCredential:[credentials objectForKey:credentialKey] forProtectionSpace:protectionSpace];
}
}
}
}
}
- (void)eraseURLCache
{
NSURL *loginUrl = [NSURL URLWithString:#"https://www.MYURL.com"];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:loginUrl];
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:urlRequest];
[[NSURLCache sharedURLCache] setMemoryCapacity:0];
[[NSURLCache sharedURLCache] setDiskCapacity:0];
}
Another problem: if I wait for a long time between sending message requests to the server while the app is running, the server thinks I've logged out and exhibits the same behavior described above.
EDIT: this 2nd problem remains unsolved. Additional information - it appears that the magic time lag number is 10 seconds. In other words, if I wait more than 10 seconds after the server has authenticated me to request a file from the server, it doesn't recognize my request and sends me the web login page instead, just as it would do for an unauthenticated request.
Any idea what's going on? And no, I can't simply load the webserver login page inside my app, because that doesn't meet the requirements for this project.

NSURLConnection's delegate method, asking for authenticationChallenge is not getting fired?

I use NSURLConnection to call a webservice and there is a client certificate present in my keychain which I set as the credential in - (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
After I removed this certificate and adding a new one to keychain, the NSURLConnection still maintains the credential that I give already and gives me back with 417 status error code, which was 200, before I remove the old certificate.
Is there a way to make the NSURLConnection ask for credential, forcefully.? or how can close the existing SSL connection or the authentication challenge's credentials.
NSURLConnection is fickle beast, and I've been having a similar problem for the past couple of days. But I've found a solution to my problem and a couple of suggestions to what could possibly be the reason for an issue like the one your having.
TLS
First of there is a possibility that what is happening to you is the TLS layer caching the credentials. That is due to it being computationally expensive to establish the TLS connection [1].
Possible solutions to this are to change the DNS name you are using, by for example adding a dot (.) to the end of the string since the DNS protocol accepts a string ending in dots as fully qualified DNS name. I've also seen people adding a hashtag (#) to all URL requests and thus tricking the system to never look for a stored credential but just initiate the didRecieveAuthenticationChallenge call instead [2].
Cookies
Another possibility is that the server is setting a cookie that you would need to clear. You can do that by doing the following:
-(void)clearCookies:(NSString *)urlString{
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedCookieStorage];
NSURL *url = [[NSURL alloc] initWithString urlString];
NSArray *cookies = [cookieStorage cookiesForURL:tempURL];
for(NSHTTPCookie *cookie in cookies){
//iterate over all cookies and delete them
[cookieStorage deleteCookie:cookie];
}
}
NSURLCredentialStorage
Now it could be that the credentials are still being stored in the sharedCredentialStorage and thus should be erased by doing the following:
NSURLCredentialStorage *store = [NSURLCredentialStorage sharedCredentialStorage];
if(store !=nil){
for(NSURLProtectionSpace *protectionSpace in [store allCredentials]){
NSDictionary *map = [[NSURLCredentialStorage sharedCredentialStorage]
credentialsForProtectionSpace:protectionSpace];
if(map!=nil){
for(NSString *user in map){
NSURLCredential *cred = [map objectForKey:user];
[store removeCredential:cred forProtectionSpace:protectionSpace];
}
}
}
}
I hope that these will help.

Resources