(ESP32) Cookie lost after page refresh only on safari

118 Views Asked by At

So I have created an authentication system for ESP32. This is a really basic system where a user can enter the credentials on the webpage, the server authenticates them and sets a cookie with a session id. The webpage is refreshed after that, immidiately.

I list the relevant function for this:

ESP32 Backend cookie set function

void setCookie(AsyncWebServerResponse* response, const char* name, const char* value, int secondsFromNow) {
    time_t now = time(nullptr);
    time_t expirationTimestamp = now + secondsFromNow;

    // Convert the expiration timestamp to a formatted string for the "expires" attribute
    struct tm* tmInfo = gmtime(&expirationTimestamp);
    char expires[128];
    strftime(expires, sizeof(expires), "%a, %d %b %Y %H:%M:%S GMT", tmInfo);

    // Set the cookie with the "expires" attribute
    char cookie[512];  // Adjust the buffer size as needed
    snprintf(cookie, sizeof(cookie), "%s=%s; expires=%s; path=/", name, value, expires);
    response->addHeader("Set-Cookie", cookie);

    printf("[Server] - Set cookie: %s\n", cookie);
}

This function prints out the following:

Set cookie: sessionID=2B945041-3DF6-454A-BC37-2C72A0C46150; expires=Mon, 27 Nov 2023 10:36:14 GMT; path=/

On the client side i just reload the page if the request was ok

form.addEventListener('submit', async function(e){
    e.preventDefault();
    if( isLoading ){return;}
    const username = usernameElem.value;
    const password = passwordElem.value;
    if( username === "" || password === "" ){
        return setError(lang.getWord("emptyInput"));
    }
    setLoading(true);
    const resp = await fetch("/auth/login", {
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify({
            username: username,
            password: password
        })
    });
    setLoading(false);
    if( resp.ok ){
        setTimeout(() => {
            location.reload();
            //window.location.href = "/";
        },500);
    }else{
        return setError(lang.getWord("wrongUsernameOrPassword"));
    }
}, false);

Tested on

  • Android chrome, firefox, opera ( All works )
  • Windows chrome, opera, edge, firefox, brave ( All works )
  • Iphone safari does not work. ( Yet to test other browsers )

When the page reloads the backend checks for the session id and logs in the user. If no session id found (or expired or changed), returns the login page. On Iphone safari i always get the login page back.

I have tried the followings

  • Set/unset path on cookie
  • Set/unset domain on cookie
  • Delay the reload on client side with various timeouts
  • href to "/" instead of location.reload()
  • Add/Remove last semicolon from cookie string

Yet to try

  • Even longer delay on reload
  • Other browsers on Iphone.
  • SameSite attribute to none
  • httponly attribute

There are a lot of issues with Safari on Iphone but this is the most frustrating. I have multiple bugs on Safari which works across all different platforms and browsers but Safari on Iphone. I don't know what apple developers are doing but it would be good if they would stop and just implement things the way others do. ( z-index on a page for example, haptic feedback on webpages etc... etc... )

EDIT1*

  • Longer delay not helping. 5 or 10 sec does the same
  • Tested on Chrome,Safari,Opera ( with ip and with mdns ) Not working on any of the browsers on IOS.
  • SameSite attribute made it worse
  • httpOnly attribute did nothing

EDIT2*

The problem was my getCookie method. On Android and windows and every other possible OS and system the cookies comes like this:

language=HU; dark-theme=true; zone-grid=true; sessionID=FE0ED968-C246-4BCC-814E-6E49E

On IOS the cookies comes like this

sessionID=F04E13A7-0D26-4DD6-A444-814A5F3E7758; dark-theme=false; language=HU

My getCookie method was this:

boolean HsH_Server::getCookie(const char* name, const char* cookie, const char*& result) {
    if (name == nullptr || cookie == nullptr ||
        strlen(name) == 0 || strlen(cookie) == 0) {
        return false;
    }
    const char* position = strstr(cookie, name);
    if (position == nullptr) {
        return false;
    }

    // Calculate the start position after the name and '=' character.
    position += strlen(name) + 1;

    // Find the end position (either the next ';' or the end of the cookie string).
    const char* endPos = strchr(position, ';');
    if (endPos == nullptr) {
        // If there's no ';', it means this is the last cookie.
        endPos = cookie + strlen(cookie);
    }

    // Calculate the length of the cookie value.
    size_t length = endPos - position;

    // Set the result pointer to point to the start of the cookie value.
    result = position;

    return true;
}

I rewrote to use Strings so it become this

boolean HsH_Server::getCookie(String name, String cookie, String& result) {
    int position = cookie.indexOf(name);
    if (position == -1) {
        return false;
    }
    name += "=";
    int length = name.length();
    int startPos = position + length;
    int stopPos = cookie.indexOf(";", startPos);
    result = cookie.substring(startPos, stopPos);
    return true;
}

It is not really a solution yet because i try to avoid Strings. But the problem was in my getCookie() method.

0

There are 0 best solutions below