Absinthe API: Sharing Cookies between parent domain and its subdomains

I’m working on an API, right now it’s residing on my local machine. I’ve added/modified /etc/hosts file to work locally. I’m also using Nginx as a reverse proxy for my frontend React.js app and backend Absinthe api.

I’m able to set cookies(with put_resp_cookie/4 with domain attribute of a cookie set to .mydomain.social for accessing cookies also via subdomains) using Normal REST API Routes for authorization … The absinthe API that I’m working is being served through http://mydomain.social/graphql and it’s working completely fine.

The real problem is when I tried to host the Absinthe and REST API on http://api.mydomain.social/graphql, I’m able to authenticate myself via cookies in browser when accessed through http://mydomain.social/… but the Absinthe is unable to set the context for authenticated user, ie., api.mydomain.social(also added to /etc/hosts files is unable to access the cookie data, even after I’ve also added withCredentials: true in frontend React app XHR’s

defmodule MyAppWeb.WebContextPlug do
  @behaviour Plug

  alias Plug.Conn

  alias MyApp.{
    Repo,
    Users.User,
    Utils
  }

  @doc false
  @spec init(Plug.opts()) :: Plug.opts()
  def init(opts), do: opts

  @doc false
  @spec call(Plug.Conn.t(), Plug.opts()) :: Plug.Conn.t()
  def call(conn, _) do
    context = build_context(conn)
    Absinthe.Plug.put_options(conn, context: context)
  end

  @doc """
  Return the current user context based on the cookie
  """
  @spec build_context(Plug.Conn.t()) :: map()
  def build_context(conn) do
    load_auth_cookie = conn.req_cookies["auth"]
    load_username_cookie = conn.req_cookies["username"]

    id = Utils.verify_token(load_auth_cookie)
    username = Utils.verify_token(load_username_cookie)

    needed =
      if id != nil do
        current_user = Repo.get(User, id)

        if username == current_user.username do
          Repo.put_current_user(current_user)

          %{current_user: current_user}
        else
          %{}
        end
      else
        conn
        |> Conn.halt()
      end

    needed
  end
end

I guess following two lines of code is real issue here


    load_auth_cookie = conn.req_cookies["auth"]
    load_username_cookie = conn.req_cookies["username"]

Once again, I’m able to set the context when access through http://mydomain.social/graphql but not via http://api.mydomain.social/graphql. Both the endpoints are pointing to http://localhost:4000 but with different url’s when needed for scaling using nginx proxy_pass. I’m stuck with this for a really long period of time, so I thought I could reach out the community for some advice/help.

Hey @HappyBee, Absinthe.Plug — absinthe_plug v1.5.8 provides a way for you to set cookies on an HTTP response based on the result of a GraphQL query. Do note however that if you’re doing cross domain cookie manipulation then that’s going to be more complicated. Fortunately, those complications have nothing specific to do with GraphQL or Absinthe, so any resource on that topic should be useful.

Hi Ben, you’re right about Absinthe … the issue is with subdomain cookies.
I want to separate both the frontend(mydomain.social) and backend(api.mydomain.social) for scaling, you know to reduce and balance the load webserver. For example https://lenster.xyz/ is able to access https://api.lens.dev/ GraphQL API. I’ve enabled CORS, but I’m still stuck ;(

For future readers:
It’s a CORS related issue, when I disable my security flag in chrome browser, I’m able to see the desired output.

@HappyBee if you’d like to support subdomains you can do this through your Phoenix Endpoint configuration. It isn’t an Absinthe concern.

If you’re using Plug.Session, you can modify the domain option to be the domain apex. This will inform your browser that the issued cookie is valid across subdomains.

I’ve found that this in combination with CORSPlug works well for building apps across subdomains.

1 Like

I already figured out the solution… thank you very much for the advice