Android Webview: Login cookie keeps getting deleted

99 Views Asked by At

I'm using an Android Webview in Java to load my webpages on my node server.

My node server requires me to login. After that, it creates an httpOnly cookie with the access token and sends it back which I then store manually in Java on Android.

Something is causing my cookie to be either destroyed or lost, causing me to constantly have to relogin, but it's random. Sometimes it stays logged in, other times I'll resume the app and it'll switch to the login page.

The cookie is not expiring either. I've set it to expire 30 days. It's also working just fine on browsers. It's just..being deleted from my phone automatically at random times.

I'm manually saving and loading the cookie too so how could this be?

Should I only save it after the login page finished? Am I flushing it at incorrect times?

    public class MainActivity extends AppCompatActivity {

    private WebView webView;

    private String websiteDomain = "https://10.0.4.12";

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize WebView

        webView = findViewById(R.id.webView);
        webView.setWebContentsDebuggingEnabled(true);
        WebSettings webSettings = webView.getSettings();
        webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);

        if (Build.VERSION.SDK_INT >= 21) {
            CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true);
        } else {
            CookieManager.getInstance().setAcceptCookie(true);
        }

        webSettings.setJavaScriptEnabled(true);
        webSettings.setDomStorageEnabled(true);

        loadCookiesFromSharedPreferences();

        // Set WebViewClient to handle redirects and other events
        webView.setWebViewClient(new WebViewClient() {
            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, android.net.http.SslError error) {
                handler.proceed(); // Ignore SSL certificate errors
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                saveCookiesToSharedPreferences(url);
                printCookies(url);
            }

        });

        // When we first start the webview, load the route that takes us to the last page we were on previously
        webView.loadUrl(websiteDomain + "/auth/last");
    }

    // Save cookie retrieved from webview manually to shared preferences
    private void saveCookiesToSharedPreferences(String url) {
        CookieManager cookieManager = CookieManager.getInstance();
        String cookies = cookieManager.getCookie(url);

        // Save cookies to shared preferences
        SharedPreferences preferences = getPreferences(Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = preferences.edit();
        editor.putString("saved_cookies", cookies);
        editor.apply();
    }

    // Load cookie retrieved from webview manually from shared preferences into the cookie manager
    private void loadCookiesFromSharedPreferences() {
        // Load cookies from shared preferences
        SharedPreferences preferences = getPreferences(Context.MODE_PRIVATE);
        String cookies = preferences.getString("saved_cookies", "");

        // Set cookies in CookieManager
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.setCookie(websiteDomain, cookies);
    }

    // Print the cookie for debugging
    private void printCookies(String url) {
        Log.d("COOKIE", "Has cookies: " + String.valueOf(CookieManager.getInstance().hasCookies()) );
        String currentCookie = CookieManager.getInstance().getCookie(url);
        Log.d("COOKIE", "Current cookie in webView:" + currentCookie);
    }

    @Override
    protected void onPause() {
        super.onPause();
        CookieManager.getInstance().flush();
        webView.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        loadCookiesFromSharedPreferences();
        webView.onResume();
    }
}
1

There are 1 best solutions below

0
mtrakal On

EDIT 2024-03-05

We must persist cookies manually somerhere. Cookie manager is not properly work when open webview again...

Call function like this before load login page:

    private fun setCookiesToLoginPage() {
        val cookies = viewModel.preferencesManager.cookies // load from local storage
        if (!cookies.isNullOrEmpty()) {
            cookies.split(";").map { it.trim() }.forEach {
                CookieManager.getInstance().setCookie(MY_ROOT_URL_FOR_PERSIST, it)
            }
            CookieManager.getInstance().flush()
        }
    }

This code will load cookies from local storage and set to page and flush it to CookieManager for later use (until app is killed).

And persist cookies (before exit app - I still keep code in onPageLoaded to be sure)

binding.mainWebView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(
                view: WebView,
                url: String,
            ) {
                super.onPageFinished(view, url)
                // Store cookies after every page load, to be sure, that we store login cookies...
                persistCookies()
            }

    override fun onPause() {
        persistCookies()
        super.onPause()
    }

    private fun persistCookies() {
        CookieManager.getInstance().getCookie(MY_ROOT_URL_FOR_PERSIST)?.let {
            viewModel.preferencesManager.cookies = it // store it locally
        }
        CookieManager.getInstance().flush()
    }


Old answer

Have same issue for long time (4 years) without properly fix :(. I call CookieManager.getInstance().flush() after every single page load finish

binding.mainWebView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(
                view: WebView,
                url: String,
            ) {
                super.onPageFinished(view, url)
                // Store cookies after every page load, to be sure, that we store login cookies...
                CookieManager.getInstance().flush()
            }

and in onPuase on fragment as well, but nothing help.

    override fun onPause() {
        CookieManager.getInstance().flush()
        super.onPause()
    }

We have reproducible use case:

  • user open app and login to webview app
  • user use "HW" button for task list and kill app
  • user open app
  • user is on login page instead of logged in

When user don't kill app and use back button to close it, everything work as expected.

It looks, that flush in onPause is killed before it can store it. But why it didn't work as well, when we call it after every page load? No idea... it have bunch of time to persist cookies.