Having separate cookie storage for two UIWebView? - ios

Background
I am developing a simple iPad application that allow the user to browse the same website with different logins at the same time. Therefore I have two UIWebView and they should have different cookie storage so the user can login one account on the first UIWebView and another account on the second UIWebView.
What have I tried?
I think the solution is to implement different cookie storages in the two UIWebView I have.
Sasmito Adibowo wrote an article Implementing Your Own Cookie Storage which provide details on how to use a custom cookie storage for WebView on Mac.
It is done by modify the NSURLRequest that WebView is going to send, adding cookie headers to it, and also intercept the response from WebView and extract the cookies from the response header and save it to our own cookie storage.
Technically, it is done by implementing these two delegate methods:
- (void)webView:(WebView *)sender resource:(id)identifier didReceiveResponse:(NSURLResponse *)response fromDataSource:(WebDataSource *)dataSource
- (NSURLRequest *)webView:(WebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(WebDataSource *)dataSource
Although it is undocumented, UIWebView did support one of the method above with a slightly different method name:
- (NSURLRequest *)uiWebView:(UIWebView *)sender resource:(id)identifier willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)redirectResponse fromDataSource:(id)dataSource
However, UIWebView don't have a equivalent delegate method for webView:resource:didReceiveResponse:fromDataSource: and hence I cannot extract the cookies from the response headers.
The Question
Is there a way to have UIWebView to use a custom cookie storage, so the two UIWebView can have their own cookie storage?
Thanks!

Have you tried getting the cookies associated with a particular webview (and holding onto them) in webViewDidStartLoad:
NSHTTPCookie *cookie;
NSHTTPCookieStorage *cookieJar = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [cookieJar cookies]) {
[self.cookies addObject:cookie];
}
And storing these cookies right after (retrieve values and keys from self.cookies):
NSMutableDictionary *cookieDict = [NSMutableDictionary dictionary];
[cookieDict setObject:#"value1" forKey:NSHTTPCookieName];
[cookieDict setObject:#"value2" forKey:NSHTTPCookieValue];
[cookieDict setObject:#"value3" forKey:NSHTTPCookieDomain];
...etc..
NSHTTPCookie *cookie = [NSHTTPCookie cookieWithProperties:cookieDict];
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
You'll also need to see this in your viewDidLoad:
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways];

If you're willing to dig a little deeper, the used-to-be famous ASIHttpRequest knew how to arrange this back in the day.
Read switching cookie storage for requests.
They also have an interesting wrapper for UIWebView requests, AKA ASIWebPageRequest.
Sadly, the project has since been discontinued, but it should provide you with an operation ground to achieve your goal.
Otherwise, as bdev has presented, I would queue the requests and replace the cookie storage each time, before firing a request. You could use the dispatcher nicely here.

Related

How to handle CloudFront Signed Cookie with .m3u8 files on iOS?

I have an App which is playing audio files stored on AWS.
The audio content is secured by AWS CloudFront signed cookies functionality. I had no problem in creating the signed cookies and set them in the original HTTP request using AVURLAsset, and everything works just fine for .mp3 content. Nevertheless, when accessing .m3u8 files, I get a 403 HTTP error. I noticed that the initial request is ok and the .m3u8 file is downloaded correctly, but the subsequent requests (for the audio fragments) do not work and receive a 403 as the cookie is not sent.
I already tried using the NSHTTPCookieStorage, but it did not work ;-(
NSURL *url = [NSURL URLWithString: #"http://..../stream.m3u8"]
// Get the Cookie Storage
NSHTTPCookieStorage *cookiesStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
// Create the array to store the cookies
NSMutableArray *cookies = [NSMutableArray array];
// Create the Cloud-Front-Policy Cookie
NSHTTPCookie *cloudFrontPolicyCookie = ...
[cookies addObject:cloudFrontPolicyCookie];
// Create the Cloud-Front-Signature Cookie
NSHTTPCookie *cloudFrontSignatureCookie = ...
[cookies addObject:cloudFrontSignatureCookie];
// Create the Cloud-Front-Key-Paid-Id Cookie
NSHTTPCookie *cloudFrontKeyPairIdCookie = ...
[cookies addObject:cloudFrontKeyPairIdCookie];
// Create the HTTP Header Dictionary
NSMutableDictionary * headers = [NSMutableDictionary dictionary];
// I omitted the cookie creation, but it is ok! I tested using curl on the command line
NSString *cookieAsHeader = ...
// Set the header
[headers setObject:cookieAsHeader forKey:#"Cookie"];
// Create the AVURLAsset so that I can use the headers and the cookies
// Notice that I tried using only the headers (which works)!
AVURLAsset * asset = [AVURLAsset URLAssetWithURL:url options:#{#"AVURLAssetHTTPHeaderFieldsKey": headers, AVURLAssetHTTPCookiesKey : [cookiesStorage cookies] }];
// For secured .mp3 files, it works just fine
// but for .m3u8, the content does not play as the first file part receives 403. Notice that the first request (for the .m3u8) works just fine.
AVPlayerItem *playerItem = [AVPlayerItem playerItemWithAsset:asset];
You need to generate the cookies, unfortunately aws ios sdk doesn't have this function. My temporary solution for now is one the the following:
1) Ask web server to generate the cookies (using aws php sdk). In this way you can set timeout in a few minutes or anytime you desire, so you can keep the data safe.
2) Hardcoded the cookies inside the code. You can generate it locally using aws php sdk, and then set duration as long as possible. Maybe a year or more depend on your condition. However if someone can see it, your data will be exposed.
3) Create our own cookies generator function in swift/obj-c. We can use aws php sdk for reference.
It looks like would be a good way to handle it: https://github.com/ykying/Swift_Player_AWS_Cookie
You will likely need to generate the cookies on whatever server you have written for serving content and whenever the user opens the app or logs in, give them the cookies with a 24 hour expiration (or however long your current session TTL is).
I hope this helps someone else, we're going to try this out in our app as well.

SFSafariViewController cookies

I understand that as of iOS9 you should be able to read cookies with SFSafariViewController.
If I set a cookie on my page in JS using the following:
var dd = new Date(Date.now() + 1000 * 60 * 60 * 24).toGMTString();
var expires = "expires="+ dd;
document.cookie = "mycookie=cookievalue; " + expires + " domain=.mydomain.co.uk ; path=/ ";
If I do :
- (void)safariViewController:(SFSafariViewController *)controller didCompleteInitialLoad:(BOOL)didLoadSuccessfully
{
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookiesArray = [storage cookies];
}
cookiesArray is always empty.
If I use a traditional UIWebView
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
NSArray *cookiesArray = [storage cookies];
}
I get the cookie I was expecting.
Any ideas what I might be doing wrong?
SFSafariViewController is basically a Safari process, running outside of your app. Your app will not have any access to the cookies used by the SFSafariViewController, just as your app has no access to the cookies in the Safari app itself.
If you need this functionality, you'll need to stuck with UIWebView or WKWebView.
SFSafariViewController runs in a separate process, so reading cookies is NOT possible.
However, in case SFSafariViewController is the only option available due to some limitations with the existing available options like WKWebView, and UIWebView.Then the custom URL scheme approach will be helpful in sending the data from SFSafariViewController to the parent App which initiates the SFSafariViewController.
In the above case, a possible URL will be as follows, where "myapp" is the custom URL scheme
"myapp://SendData?mycookie=cookievalue&domain=.mydomain.co.uk&path=/"
So, the custom URL scheme will be registered against the parent app to launch it and custom URL scheme parameters will have the intended data to be received by the parent app. If the data is sensitive then it can be encrypted by javascript prior to sending and can be decrypted by the parent app after receiving it.
Hope this would help :)
For more details on the custom URL scheme, please visit
https://developer.apple.com/documentation/xcode/defining-a-custom-url-scheme-for-your-app

Instagram: How to get a brand new token for another user

I am working on an iOS app that uses Instagram Integration. I am able to have everything working. The on thing I just noticed though, once I've called:
https://instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=token
and received my token, let's say another user wants to login using the same device, I will need another token for that user. The problem I am having is that when I call https://instagram.com/oauth/authorize/?client_id=CLIENT-ID&redirect_uri=REDIRECT-URI&response_type=token subsequent times, it is not bringing up the IG login screen. It automatically authenticates the existing token and redirects to the user page.
Is there soem kind of parameter that can be included in the URL call to force instagram to bring up the login screen, so another user can authenticate?
Deleting cookies will log the user out. It looks like you are asking for iOS:
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies]) {
[storage deleteCookie:cookie];
}
The previous answer should work for you. You just need to add the following statement at the end.
[[NSUserDefaults standardUserDefaults] synchronize];
Here is the full Swift code for deleting all cookies:
let storage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
if let cookies = storage.cookies {
for cookie in cookies as [NSHTTPCookie] {
storage.deleteCookie(cookie)
}
}
NSUserDefaults.standardUserDefaults().synchronize()

How to clear browser cache from webobjects application?

In log out action, we are terminating the session by using terminate() method of session. But after log-out, if we click on back button of the browser, i can able to see the content of last page, but i am unable to do any action(This is fine). If we clear the browser cache after log-out, there is no problem. So we found, it is because of browser cache.
So please let us know how to clear the browser cache from webobjects application programmatically.
I actually think it may retain cached information when you close out the UIWebView. I've tried removing a UIWebView from my UIViewController, releasing it, then creating a new one. The new one remembered exactly where I was at when I went back to an address without having to reload everything (it remembered my previous UIWebView was logged in).
So a couple of suggestions:
[[NSURLCache sharedURLCache] removeCachedResponseForRequest:NSURLRequest];
This would remove a cached response for a specific request. There is also a call that will remove all cached responses for all requests ran on the UIWebView:
[[NSURLCache sharedURLCache] removeAllCachedResponses];
After that, you can try deleting any associated cookies with the UIWebView:
for(NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
if([[cookie domain] isEqualToString:someNSStringUrlDomain]) {
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
}
Happy Coding :)
Deepak

Clear cookies for response in AFNetworking 2

I am implementing one simple login-logout controller for my application. I am using a GET request for the login screen and segue to get the secondViewController. At the second view controller there is logout button that when pressed returns back to loginView - But whatever I type in the fields I always get in until I close the app (so the cache is playing part here)
Is there any good way to clear the cache. I have tried to do that by alloc init NSURLCache at the beginning of the request and then set removeAllCachedResponses but its not doing any difference.
Any other suggestions?
Try to remove all your cookies from the cookie store upon logout:
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (NSHTTPCookie *cookie in [storage cookies]) {
[storage deleteCookie:cookie];
}
AFNetwork uses the shared NSURLCache you can clear this one by call the
removeAllCachedResponses on the shared cache
[[NSURLCache sharedURLCache] removeAllCachedResponses];

Resources