Support for Cookie Authentication

Created on 1 November 2023, 8 months ago
Updated 14 April 2024, 3 months ago

Problem/Motivation

We should support this due to the inclusion in core.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

✨ Feature request
Status

Fixed

Component

Code

Created by

🇺🇸United States brianperry

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @brianperry
  • 🇮🇳India pratik_kamble Pune, India

    @brianperry @CobyPear For cookie based authentication what should be parameter for object?

    {type: "Cookie", credentials: {xCSRFToken: "token", value: "cookiesessionstring"}
    
  • I'm not as familiar with cookie based on in this context. I am probably misunderstanding these docs but is the username and password required credentials? https://www.drupal.org/docs/8/core/modules/rest/using-other-authenticati... →

    In other words, what do we need to provide to get the cookie back? `xCSRFToken` makes sense. I don't know if we need `value` here unless we need it to pass to Drupal. I would expect `username` and `password if I understand correctly.

  • 🇮🇳India pratik_kamble Pune, India

    While testing on the Postman, I had to fetch the x-csrf-token from path `session/token`. Get the cookie value from the browser.
    Do we expect to perform the operation to fetch x-csrf-token. and session value by calling `user/login` path with appropriate params in the code?

  • 🇺🇸United States brianperry

    This gets tricky because the behavior varies quite a bit between server side and client side. Somewhat surprisingly, I was only able to create a functioning server side example. Apparently Cookie headers are considered forbidden response headers on the client side. Here's the server-side example I came up with:

    const cookie = await fetch(
      "http://drupal-contributions.lndo.site/user/login?_format=json",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          name: "myusername",
          pass: "mypassword",
        }),
      }
    ).then((response) => response.headers.get("Set-Cookie"));
    
    const headers = new Headers();
    if (typeof cookie === "string") {
      headers.append("Cookie", cookie);
    }
    
    // Returns 1 if user is logged in
    const loginStatus = await fetch(
      "http://drupal-contributions.lndo.site/user/login_status?_format=json",
      {
        headers,
      }
    ).then((response) => response.json());
    console.log("Login Status", loginStatus);
    
    // If not authenticated, data will be an empty array.
    const authenticatedEndpoint = await fetch(
      "http://drupal-contributions.lndo.site/jsonapi/action/action",
      {
        headers,
      }
    ).then((response) => response.json());
    
    console.log("Actions", authenticatedEndpoint);

    So looping back to the original question, I think the object would probably be:

    {type: "Cookie", credentials: {username: "username", password: "password"}

    We would store the actual cookie value (and the csrf_token if necessary) and include it on subsequent requests.

    Along the way I saw examples that suggested both including `credentials: "include",` in the fetch options object, and also `"X-CSRF-Token` in the headers (as mentioned, this is available in the login response data as csrf_token.) I didn't find either to be necessary in this example.

    This was one of those things that I added based on hearing it as user feedback. I continue to be a little skeptical. What does it mean for us to add a feature like this that will likely only work reliably on the server? And since you have to pass username and password, what benefit do you really get from this compared to basic auth? I guess the credentials are only passed once rather than all the time?

    I don't think this would be hard to implement, but I'm wondering if the right choice here is to postpone it from 1.0 and revive it if we get more feedback requesting it. I also wonder if people who think they want this would be better served with a JWT based solution if we can come up with one in the future.

  • Status changed to Postponed 5 months ago
  • 🇺🇸United States brianperry
  • 🇺🇸United States ctrlADel North Carolina, USA

    Would this also postpone nice to have features for using cookie based auth to make requests? I agree in a fully decoupled scenario JWTs make a lot more sense but when looking to progressively decouple part of the admin interface cookie support is nice. Since a user logs in via Drupal to access the admin portion of the site anyways they typically will already have a cookie that can be used.

  • Status changed to Active 5 months ago
  • 🇺🇸United States brianperry

    > Would this also postpone nice to have features for using cookie based auth to make requests? I agree in a fully decoupled scenario JWTs make a lot more sense but when looking to progressively decouple part of the admin interface cookie support is nice.

    That's a good point. We should test this progressively decoupled use case. My guess is that if it can work at all, it would 'just work' because the cookie is already set for the same site. If that doesn't work, I'm not sure what I outlined above would help either.

    Un-postponing this for now as to not lose track. Would like to either test this prior to 1.0 or mark this as some kind of fast follow.

  • 🇺🇸United States brianperry
  • Status changed to Fixed 3 months ago
  • 🇺🇸United States brianperry

    > Would this also postpone nice to have features for using cookie based auth to make requests? I agree in a fully decoupled scenario JWTs make a lot more sense but when looking to progressively decouple part of the admin interface cookie support is nice.

    Looped back to this. Confirmed that in a progressively decoupled use case the client will automatically inherit the active Drupal authentication.

    I created the following JS file:

    import { JsonApiClient } from "https://esm.run/@drupal-api-client/json-api-client";
    
    const client = new JsonApiClient(window.location.origin);
    
    const actions = await client.getCollection("action--action");
    
    console.log("This request requires authentication", actions);

    And loaded that script as a module in my library:

    api_client_examples:
      js:
        js/api-client-example.js: { attributes: { type: module } }
      dependencies:
        - core/drupal

    In a browser window where I'm logged in, I see results for the 'action' collection. Unauthenticated in an incognito window I see no results.

    As a result, I think we can close this. I'll also add this use case to the docs.

  • Automatically closed - issue fixed for 2 weeks with no activity.

Production build 0.69.0 2024