How can a client receive the cookie from a server-side?
Tags: react, backend, python, aws
Published: 2023-09-24 16:37:54

## Conclusion

* A client-side can receive the cookie from a server-side configuring "SameSite=none" when performing set-cookie.

* But you need to configure Secure attribute as well, otherwise the current modern browsers don't store the cookie for security.


## Environment

* Frontend: React
* Serverside: API Gateway + Lambda + FastAPI(Python)

## Server-side

```python
# functions/hub.py
from fastapi import FastAPI
from mangum import Mangum

from .routers import authentication_router

app = FastAPI()

app.include_router(authentication_router, tags=["authentication"])

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Mangum converts a request and response of FastAPI into event and context of AWS Lambda.
lambda_handler = Mangum(app)
```

FastAPI restricts OPTIONS requests (preflight requests) by default, so even if you allow them on the API Gateway side, if you do not allow them on the FastAPI side, a 405 error will be returned.

So, you need to explicitly include CORS headers in the response in FastAPI middleware so that the browser can make preflight requests.

<br/>
<br/>

```python
# functions/routers/authentication.py
from fastapi import APIRouter, Request, Response

authentication_router = APIRouter()

class RequestBody(BaseModel):
    email: str
    password: str

@authentication_router.post("/signin")
def sign_in(requset_body: RequestBody, request: Request, respose: Response):
  # Get tokens from Cognito
  tokens = signin(requset_body.email, requset_body.password)
  response.set_cookie(
    key="access_token",
    value=tokens.access_token,
    httponly=True,
    secure=True,
    samesite="none"
  )
```

* Enabling HttpOnly, prevent the cookie from being accessed by JavaScript like `document.cookie`.

* Enabling Secure, the cookie is send only via HTTPS. HTTP is prohibited to send it.

* "Samesite=none" is to allow the cookie to be sent over a cross-site request. (e.g. from `example.apigateway.com` to `example.vercel.app` as below)

| API Gateway | Vercel |
| --- | --- |
| example.apigateway.com | example.vercel.app |

##### Remarks

If configuring "SameSite=Lax" or "SameSite=Strict", client-side can't receive the cookie from the server-side.

※Exclaimation mark is displayed at the header of set-cookie in the Network tab of the dev tool of your browser with the mesasge (e.g. "This attempt to set a cookie via a Set-Cookie header was blocked because it had the "SameSite=Lax" attribute but came from a cross-site response which was not the response to a top-level navigation.")

##### Configuration for Serverless Framework

```yml
# serverless.yml
provider:
  httpApi:
    cors:
      allowedOrigins:
        - https://example.vercel.app
        - http://localhost:3000
      allowedHeaders:
        - Content-Type
        - Set-Cookie
      allowedMethods:
        - OPTIONS
        - GET
        - POST
        - PUT
        - DELETE
      allowCredentials: true

functions:
  hub:
    name: hub
    handler: hub.lambda_handler
    events:
     - httpApi:
       # ANY method is used to catch all HTTP methods
       method: any
       # /{proxy+} route works as a catch-all route
       path: /{proxy+}


```

* Only the origins in allowedOrigins can access API Gateway.

* Setting allowCredentials to true, enables cookies to be sent across domains.

* /{proxy+} catches all requests that don't match other routes already configured in API Gateway.