Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EXPERIMENTAL] save/restore iOS session cookies #574

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions plugins/WebViewObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,10 @@ private static extern void _CWebViewPlugin_GoForward(
private static extern string _CWebViewPlugin_GetCookies(string url);
[DllImport("__Internal")]
private static extern void _CWebViewPlugin_SetBasicAuthInfo(IntPtr instance, string userName, string password);
[DllImport("__Internal")]
private static extern string _CWebViewPlugin_GetSessionCookieFromServer(IntPtr instance);
[DllImport("__Internal")]
private static extern void _CWebViewPlugin_SetSessionCookieFromClient(IntPtr instance, string cookie);
#elif UNITY_WEBGL
[DllImport("__Internal")]
private static extern void _gree_unity_webview_init(string name);
Expand Down Expand Up @@ -999,6 +1003,43 @@ public void SetBasicAuthInfo(string userName, string password)
#endif
}

public string GetSessionCookieFromServer()
{
#if UNITY_WEBPLAYER || UNITY_WEBGL
//TODO: UNSUPPORTED
return "";
#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_EDITOR_LINUX
//TODO: UNSUPPORTED
return "";
#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
//TODO: UNSUPPORTED
return "";
#elif UNITY_IPHONE
if (webView == IntPtr.Zero)
return "";
return _CWebViewPlugin_GetSessionCookieFromServer(webView);
#elif UNITY_ANDROID
//TODO: UNSUPPORTED
return "";
#endif
}

public void SetSessionCookieFromClient(string cookie)
{
#if UNITY_WEBPLAYER || UNITY_WEBGL
//TODO: UNSUPPORTED
#elif UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN || UNITY_EDITOR_LINUX
//TODO: UNSUPPORTED
#elif UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
//TODO: UNSUPPORTED
#elif UNITY_IPHONE
if (webView == IntPtr.Zero)
return;
_CWebViewPlugin_SetSessionCookieFromClient(webView, cookie);
#elif UNITY_ANDROID
//TODO: UNSUPPORTED
#endif
}

#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
void OnApplicationFocus(bool focus)
Expand Down
115 changes: 110 additions & 5 deletions plugins/iOS/WebView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ - (void)setScrollBounce:(BOOL)enable

@end

// cf. https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303

@interface CWebViewPlugin : NSObject<WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler>
{
UIView <WebViewProtocol> *webView;
Expand All @@ -99,6 +101,8 @@ @interface CWebViewPlugin : NSObject<WKUIDelegate, WKNavigationDelegate, WKScrip
NSRegularExpression *hookRegex;
NSString *basicAuthUserName;
NSString *basicAuthPassword;
NSHTTPCookie *sessionCookieFromServer;
NSHTTPCookie *sessionCookieFromClient;
}
@end

Expand All @@ -119,6 +123,8 @@ - (id)initWithGameObjectName:(const char *)gameObjectName_ transparent:(BOOL)tra
hookRegex = nil;
basicAuthUserName = nil;
basicAuthPassword = nil;
sessionCookieFromServer = nil;
sessionCookieFromClient = nil;
if (ua != NULL && strcmp(ua, "") != 0) {
[[NSUserDefaults standardUserDefaults]
registerDefaults:@{ @"UserAgent": [[NSString alloc] initWithUTF8String:ua] }];
Expand Down Expand Up @@ -299,11 +305,18 @@ - (void)observeValueForKeyPath:(NSString *)keyPath

if ([keyPath isEqualToString:@"loading"] && [[change objectForKey:NSKeyValueChangeNewKey] intValue] == 0
&& [webView URL] != nil) {
UnitySendMessage(
[gameObjectName UTF8String],
"CallOnLoaded",
[[[webView URL] absoluteString] UTF8String]);

WKWebView *wkWebView = (WKWebView *)webView;
[wkWebView.configuration.websiteDataStore.httpCookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * cookies) {
for (NSHTTPCookie *cookie in cookies) {
if (cookie.sessionOnly) {
sessionCookieFromServer = cookie;
}
}
UnitySendMessage(
[gameObjectName UTF8String],
"CallOnLoaded",
[[[webView URL] absoluteString] UTF8String]);
}];
}
}

Expand Down Expand Up @@ -695,6 +708,80 @@ - (void)setBasicAuthInfo:(const char *)userName password:(const char *)password
basicAuthUserName = [NSString stringWithUTF8String:userName];
basicAuthPassword = [NSString stringWithUTF8String:password];
}

- (const char *)getSessionCookieFromServer
{
if (sessionCookieFromServer == nil) {
return NULL;
}
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
[formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"];
NSHTTPCookie *cookie = sessionCookieFromServer;
NSMutableString *result = [NSMutableString string];
[result appendString:[NSString stringWithFormat:@"%@=%@", cookie.name, cookie.value]];
if ([cookie.domain length] > 0) {
[result appendString:[NSString stringWithFormat:@"; "]];
[result appendString:[NSString stringWithFormat:@"Domain=%@", cookie.domain]];
}
if ([cookie.path length] > 0) {
[result appendString:[NSString stringWithFormat:@"; "]];
[result appendString:[NSString stringWithFormat:@"Path=%@", cookie.path]];
}
if (cookie.expiresDate != nil) {
[result appendString:[NSString stringWithFormat:@"; "]];
[result appendString:[NSString stringWithFormat:@"Expires=%@", [formatter stringFromDate:cookie.expiresDate]]];
}
[result appendString:[NSString stringWithFormat:@"; "]];
[result appendString:[NSString stringWithFormat:@"Version=%zd", cookie.version]];
const char *s = [result UTF8String];
char *r = (char *)malloc(strlen(s) + 1);
strcpy(r, s);
return r;
}

- (void)setSessionCookieFromClient:(const char *)cookie
{
sessionCookieFromClient = nil;
if (cookie == nil || strlen(cookie) == 0) {
return;
}
NSError *err = nil;
NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:@"([^=; ]+)=([^=;]+)" options:0 error:&err];
if (err != nil) {
return;
}
NSMutableDictionary *props = [[NSMutableDictionary alloc] init];
NSString *str = [NSString stringWithUTF8String:cookie];
NSArray *matches = [re matchesInString:str options:0 range:NSMakeRange(0, strlen(cookie))];
[matches enumerateObjectsUsingBlock:^(NSTextCheckingResult *result, NSUInteger idx, BOOL *stop) {
NSString *k = [str substringWithRange:[result rangeAtIndex:1]];
NSString *v = [str substringWithRange:[result rangeAtIndex:2]];
if (idx == 0) {
[props setValue:k forKey:NSHTTPCookieName];
[props setValue:v forKey:NSHTTPCookieValue];
} else if ([k isEqualToString:@"Domain"]) {
[props setValue:v forKey:NSHTTPCookieDomain];
} else if ([k isEqualToString:@"Path"]) {
[props setValue:v forKey:NSHTTPCookiePath];
} else if ([k isEqualToString:@"Version"]) {
[props setValue:v forKey:NSHTTPCookieVersion];
} else if ([k isEqualToString:@"Expires"]) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
[formatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss zzz"];
NSDate *d = [formatter dateFromString:v];
[props setValue:d forKey:k];
}
}];
if (props.count == 0) {
return;
}
sessionCookieFromClient = [[NSHTTPCookie alloc] initWithProperties: props];
WKWebView *wkWebView = (WKWebView *)webView;
WKHTTPCookieStore *cookieStore = wkWebView.configuration.websiteDataStore.httpCookieStore;
[cookieStore setCookie:sessionCookieFromClient completionHandler:^{}];
}
@end

extern "C" {
Expand Down Expand Up @@ -722,6 +809,8 @@ void _CWebViewPlugin_SetMargins(
const char *_CWebViewPlugin_GetCookies(const char *url);
const char *_CWebViewPlugin_GetCustomHeaderValue(void *instance, const char *headerKey);
void _CWebViewPlugin_SetBasicAuthInfo(void *instance, const char *userName, const char *password);
const char *_CWebViewPlugin_GetSessionCookieFromServer(void *instance);
void _CWebViewPlugin_SetSessionCookieFromClient(void *instance, const char *cookie);
}

void *_CWebViewPlugin_Init(const char *gameObjectName, BOOL transparent, const char *ua, BOOL enableWKWebView)
Expand Down Expand Up @@ -903,4 +992,20 @@ void _CWebViewPlugin_SetBasicAuthInfo(void *instance, const char *userName, cons
[webViewPlugin setBasicAuthInfo:userName password:password];
}

const char *_CWebViewPlugin_GetSessionCookieFromServer(void *instance)
{
if (instance == NULL)
return NULL;
CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance;
return [webViewPlugin getSessionCookieFromServer];
}

void _CWebViewPlugin_SetSessionCookieFromClient(void *instance, const char *cookie)
{
if (instance == NULL)
return;
CWebViewPlugin *webViewPlugin = (__bridge CWebViewPlugin *)instance;
[webViewPlugin setSessionCookieFromClient:cookie];
}

#endif // !(__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_9_0)
9 changes: 9 additions & 0 deletions sample/Assets/Scripts/SampleWebView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ IEnumerator Start()
},
ld: (msg) =>
{
#if UNITY_IPHONE
PlayerPrefs.SetString("sessionCookie", webViewObject.GetSessionCookieFromServer());
#endif
Debug.Log(string.Format("CallOnLoaded[{0}]", msg));
#if UNITY_EDITOR_OSX || !UNITY_ANDROID
// NOTE: depending on the situation, you might prefer
Expand Down Expand Up @@ -104,6 +107,12 @@ IEnumerator Start()
},
//ua: "custom user agent string",
enableWKWebView: true);
#if UNITY_IPHONE
if (!string.IsNullOrEmpty(PlayerPrefs.GetString("sessionCookie", ""))) {
webViewObject.SetSessionCookieFromClient(PlayerPrefs.GetString("sessionCookie", ""));
}
#endif

#if UNITY_EDITOR_OSX || UNITY_STANDALONE_OSX
webViewObject.bitmapRefreshCycle = 1;
#endif
Expand Down