Search code examples
cookieshttp-headersmobile-safarimobile-websitesetcookie

Cookies not set or sent in request in iOS Safari or Chrome works on all Android and Desktop Browsers


I use a set-cookie header made on the backend to set a secure, http-only cookie on the device. This has worked like a charm on desktop Safari, Chrome, FF, IE etc. This also works on Android Chrome. However, when I tried to run the same exact code from my web app on iOS in mobile Safari or Chrome, no cookie is sent in the request and my web app does not work.

I've tried looking at other stack overflow answers and questions and I've tried modifying my settings and testing whether several different iOS devices. I want my web app to work without modifying the default iOS Safari settings. I genuinely don't understand why iOS isn't working.. as this behavior seems unprecedented.

issues the cookie with tokens included

(authorization.py)

#creates the cookie string for the response
def setCookie(name, value, expires='', domain=None,
    secure=False, httponly=False, path=None):
    morsel = cookies.Morsel()
    # morsel.set(name, value, quote(value))
    morsel.set(name, value, value)
    expires = datetime.utcnow() + timedelta(days=365)
    expireStr = expires.strftime("%a, %d %b %Y %X GMT")
    print(expireStr)
    morsel['expires'] = expireStr
    if path:
        morsel['path'] = path
    if domain:
        morsel['domain'] = domain
    if secure:
        morsel['secure'] = secure
    value = morsel.OutputString()
    if httponly:
        value += '; httponly'
    print(value)
    return value

def issueCookieTokens(userData):
    accessToken = issueAccessToken(userData).decode('utf-8')
    refreshToken = issueRefreshToken(userData).decode('utf-8')
    tokens = {'accessToken': accessToken, 'refreshToken': refreshToken}
    return setCookie('tokens', json.dumps(tokens), path= '/', secure=True, httponly=True)

(customfuncs.py)

headers = {
      'Access-Control-Allow-Origin': 'REDACtED',
      'Access-Control-Allow-Credentials': True
}

def makeHeader(cookieVal):
    newHeader = headers
    print("cookieVal: " + cookieVal)
    newHeader['Set-Cookie'] = cookieVal
    print(newHeader['Set-Cookie'])
    return newHeader

(where I return the response)

create a response to return to the device

response = {"statusCode": 200,
"headers": customfuncs.makeHeader(authorization.issueCookieTokens(userItem)),
"body": json.dumps({'Item': userItem}, cls=decimalencoder.DecimalEncoder)}
return response

I expect my response to return successfully, encompassing my json in the return body, and successfully returning a header that sets a cookie using the set-cookie header to store a secure, http-only cookie that stores my access/refresh tokens for the user. The cookie should also be sent up in subsequent requests. If you would like to test it for yourselves, the site is https://swipeme.in . I store cookies during login and signup. The best way to test the cookies set and being sent back up would be in the signup and in the next request which would be photo-upload.


Solution

  • There are two things that are important to do that will make this cookie(secure, httpOnly) setting process work.

    1. Make sure that requests on the client always include credentials to match "Allow-Credentials" https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials

    2. If you are using secure, http only cookies, the API that you request needs to be from the same domain. (For example if my site was hosted on https://swipeme.in, the api it request must be something like https://api.swipeme.in)

    I ran into this issue when I was using AWS Lambda functions with a different domain. I then configured API Gateway to use my custom domain to get it fixed.