SFSafariViewController cookies - ios

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

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.

WKWebView AJAX calls losing cookies

Our customer requested us to do a switch from WebView to WKWebView recently. Their app uses native login, which is done by a 2 POST calls to their backend, returning various authorization cookies that were later on used in every single HTTP/AJAX call throughout the whole app.
With the WebView, it all worked like a charm without a need to implement a single line of custom code. User logged in, cookies were stored to cookie storage by default, and WebView always took them from there and used them, since the HTTPCookieStorage was shared between NSURLSession and WebView.
It it a whole new story with WKWebView. Once we switched WebView to WKWebView, we saw that the authorization was not working. It was due to losing some cookies in the WKWebView. We store the cookies from the NSURLSession response now and append them to the WKWebView manually, by adding "Cookie" header to the HTTP requests.
We were able to get the authorization for HTTP calls work this way, but now we are seeing a new problem. Somehow, all the AJAX calls done in the WKWebView lose the authorization cookies.
Do you please know if there is any way to somehow have the authorization cookies appear in the AJAX calls too? Injecting javascript with
javascriptCookieString = #"document.cookie = 'testCookie=testValue';";
[self.webView evaluateJavaScript:javascriptCookieString completionHandler:nil];
did not work and it seems like there is no control whatsoever over Javascript calls, so I cannot alter the requests before they are being executed. Thank you.
I found that the following snippet did the trick for us. We had the same problem.
// add session cookie to ajax calls
WKUserContentController* userContentController =
WKUserContentController.new;
WKUserScript * cookieScript =
[[WKUserScript alloc]
initWithSource: [[User sharedInstance] getJavscriptCookieString]
injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[userContentController addUserScript:cookieScript];
WKWebViewConfiguration *webViewConfiguration = [[WKWebViewConfiguration alloc] init];
webViewConfiguration.userContentController = userContentController;
webViewConfiguration.preferences.javaScriptCanOpenWindowsAutomatically = true;
_wk = [[WKWebView alloc] initWithFrame:self.view.frame configuration:webViewConfiguration];
The cookie string needs to be formated correct in order to be accepted.
-(NSString*) getJavscriptCookieString {
return [NSString stringWithFormat: #"document.cookie = '%#=%#'", [self getSessionName], [self getSessionValue]];
}
Hope this can be of some help.
See also:
Can I set the cookies to be used by a WKWebView?

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()

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];

Having separate cookie storage for two UIWebView?

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.

Resources