Skip to content

Commit

Permalink
fix(cookies): hide httpOnly cookies from client
Browse files Browse the repository at this point in the history
  • Loading branch information
ItsChaceD committed Aug 4, 2023
1 parent 4583c57 commit 0cc927e
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 20 deletions.
4 changes: 3 additions & 1 deletion android/capacitor/src/main/assets/native-bridge.js
Expand Up @@ -349,6 +349,7 @@ var nativeBridge = (function (exports) {
if (doPatchCookies) {
Object.defineProperty(document, 'cookie', {
get: function () {
var _a, _b, _c;
if (platform === 'ios') {
// Use prompt to synchronously get cookies.
// https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
Expand All @@ -359,7 +360,8 @@ var nativeBridge = (function (exports) {
return res;
}
else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
return win.CapacitorCookiesAndroidInterface.getCookies();
// return original document.cookie since Android does not support filtering of `httpOnly` cookies
return (_c = (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(document)) !== null && _c !== void 0 ? _c : '';
}
},
set: function (val) {
Expand Down
@@ -1,16 +1,17 @@
package com.getcapacitor.plugin;

import android.webkit.JavascriptInterface;
import androidx.annotation.Nullable;
import com.getcapacitor.JSObject;
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginConfig;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.annotation.CapacitorPlugin;
import java.io.UnsupportedEncodingException;
import java.net.CookieHandler;
import java.net.HttpCookie;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

@CapacitorPlugin
public class CapacitorCookies extends Plugin {
Expand All @@ -31,26 +32,41 @@ public boolean isEnabled() {
return pluginConfig.getBoolean("enabled", false);
}

@JavascriptInterface
public String getCookies() {
String cookieString = cookieManager.getCookieString(null);
return (null == cookieString) ? "" : cookieString;
}

@JavascriptInterface
public void setCookie(String domain, String action) {
cookieManager.setCookie(domain, action);
}

@PluginMethod
public void getCookies(PluginCall call) {
String url = call.getString("url");
JSObject cookiesMap = new JSObject();
HttpCookie[] cookies = cookieManager.getCookies(url);
for (HttpCookie cookie : cookies) {
cookiesMap.put(cookie.getName(), cookie.getValue());
}
call.resolve(cookiesMap);
this.bridge.eval(
"document.cookie",
value -> {
String cookies = value.substring(1, value.length() - 1);
String[] cookieArray = cookies.split(";");

JSObject cookieMap = new JSObject();

for (String cookie : cookieArray) {
if (cookie.length() > 0) {
String[] keyValue = cookie.split("=", 2);

if (keyValue.length == 2) {
String key = keyValue[0].trim();
String val = keyValue[1].trim();
try {
key = URLDecoder.decode(keyValue[0].trim(), StandardCharsets.UTF_8.name());
val = URLDecoder.decode(keyValue[1].trim(), StandardCharsets.UTF_8.name());
} catch (UnsupportedEncodingException ignored) {}

cookieMap.put(key, val);
}
}
}

call.resolve(cookieMap);
}
);
}

@PluginMethod
Expand Down
3 changes: 2 additions & 1 deletion core/native-bridge.ts
Expand Up @@ -394,7 +394,8 @@ const initBridge = (w: any): void => {
} else if (
typeof win.CapacitorCookiesAndroidInterface !== 'undefined'
) {
return win.CapacitorCookiesAndroidInterface.getCookies();
// return original document.cookie since Android does not support filtering of `httpOnly` cookies
return win.CapacitorCookiesDescriptor?.get?.call(document) ?? '';
}
},
set: function (val) {
Expand Down
7 changes: 5 additions & 2 deletions ios/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift
Expand Up @@ -77,7 +77,9 @@ public class CapacitorCookieManager {
let jar = HTTPCookieStorage.shared
if let cookies = jar.cookies(for: url) {
for cookie in cookies {
cookiesMap[cookie.name] = cookie.value
if !cookie.isHTTPOnly {
cookiesMap[cookie.name] = cookie.value
}
}
}
return cookiesMap
Expand All @@ -88,7 +90,8 @@ public class CapacitorCookieManager {
let jar = HTTPCookieStorage.shared
guard let url = self.getServerUrl() else { return "" }
guard let cookies = jar.cookies(for: url) else { return "" }
return cookies.map({"\($0.name)=\($0.value)"}).joined(separator: "; ")
let filteredCookies = cookies.filter { !$0.isHTTPOnly }
return filteredCookies.map({"\($0.name)=\($0.value)"}).joined(separator: "; ")
}

public func deleteCookie(_ url: URL, _ key: String) {
Expand Down
4 changes: 3 additions & 1 deletion ios/Capacitor/Capacitor/assets/native-bridge.js
Expand Up @@ -349,6 +349,7 @@ var nativeBridge = (function (exports) {
if (doPatchCookies) {
Object.defineProperty(document, 'cookie', {
get: function () {
var _a, _b, _c;
if (platform === 'ios') {
// Use prompt to synchronously get cookies.
// https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
Expand All @@ -359,7 +360,8 @@ var nativeBridge = (function (exports) {
return res;
}
else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
return win.CapacitorCookiesAndroidInterface.getCookies();
// return original document.cookie since Android does not support filtering of `httpOnly` cookies
return (_c = (_b = (_a = win.CapacitorCookiesDescriptor) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(document)) !== null && _c !== void 0 ? _c : '';
}
},
set: function (val) {
Expand Down

0 comments on commit 0cc927e

Please sign in to comment.