camunda / camunda-8-js-sdk

The Camunda 8 JavaScript SDK for Node.js
https://camunda.github.io/camunda-8-js-sdk/
Apache License 2.0
18 stars 6 forks source link

Operate Client authentication via Basic Auth #246

Open pafmaf opened 1 week ago

pafmaf commented 1 week ago

Started the Operate Docker-image locally and noticed that auth using demo/demo does not work with the Operate JS client. Can the Operate client only use OAuth?

Afaics, Operate only allows auth via cookie (JSESSION) and OAuth/JWT: https://github.com/camunda/camunda/blob/main/operate/webapp/src/main/java/io/camunda/operate/webapp/api/OpenApiConfig.java#L40-L51 (if this is the correct spot).

There are settings like CAMUNDA_AUTH_STRATEGY allowing BASIC, but, currently, the Operate client does not respect any of those settings. I believe Bearer is either set or empty, i.e. no Basic. (URL http://demo:demo@localhost:8080 didn't work either)

So there is no way to quickly inspect the processes via REST locally, only with a more complex Docker or a rather hacky REST-request setup.

However, it might to be useful for testing things.

SDK Component

Operate

Expected Behavior

I can start the Operate docker container locally and start using the following code (or similar):

const c8 = new Camunda8({
    CAMUNDA_OPERATE_BASE_URL: 'http://localhost:8080',
    CAMUNDA_AUTH_STRATEGY: "BASIC",
    CAMUNDA_BASIC_AUTH_USERNAME: "demo",
    CAMUNDA_BASIC_AUTH_PASSWORD: "demo",
    CAMUNDA_OAUTH_DISABLED: true,
    CAMUNDA_SECURE_CONNECTION: false,
});

const operate = c8.getOperateApiClient();

Just need a way to put demo / demo somewhere. ;)

Current Behavior

No way to set credentials, Operate returns 403.

Possible Solution

CAMUNDA_AUTH_STRATEGY: "BASIC",
CAMUNDA_BASIC_AUTH_USERNAME: "demo",
CAMUNDA_BASIC_AUTH_PASSWORD: "demo",

Steps to Reproduce

const c8 = new Camunda8({
    CAMUNDA_OPERATE_BASE_URL: 'http://localhost:8080',
    CAMUNDA_AUTH_STRATEGY: "BASIC",
    CAMUNDA_BASIC_AUTH_USERNAME: "demo",
    CAMUNDA_BASIC_AUTH_PASSWORD: "demo",
    CAMUNDA_OAUTH_DISABLED: true,
    CAMUNDA_SECURE_CONNECTION: false,
});

const operate = c8.getOperateApiClient();
operate.searchProcessInstances({
        filter: {
            processName: "my_process"
        },
    })

Context (Environment)

jwulf commented 1 day ago

This is not really what we think of as Basic Auth in the SDK.

This would be better called "Cookie auth". It's another flow like OAuth, where we have to exchange the creds for a token from an endpoint, then use the token in subsequent calls to the Operate API.

One issue with this is that it is specific to Operate, so we'd be complicating the SDK code to support that specific auth strategy for one component.

A second factor is whether this is going to remain viable. ie: is this a supported access method with a stable API?

A third factor is: will this be solved by supporting the alpha query APIs over REST in 8.6?

I can look into this further, but it will need to be after 8.6 in October.

pafmaf commented 1 day ago

This is not really what we think of as Basic Auth in the SDK.

Nono, we think of the same. Sorry I was not clear.

One issue with this is that it is specific to Operate, so we'd be complicating the SDK code to support that specific auth strategy for one component.

True, Authorization: Basic <credentials> or Cookie: bla is already not fitting the way the IOAuthProvider.getToken() is implemented, since it's just put there as the Bearer token.

For us, it's 'complicating SDK code' VS 'complicating work with the product', since we want to easily spin up a full env, run e2e tests and check what processes have completed / if the correct things have happened.

My current workaround looks something like this:

   private async _getHeadersPatch() {
    const loginData = await _operateLoginCookies();
    return {
      'Content-Type': 'application/json',
      'OPERATE-X-CSRF-TOKEN': loginData.csrfToken,
      'Cookie': loginData.cookies
        .map((cookie) => `${cookie.name}=${cookie.value}`)
        .join('; ')
    };
  }

  /**
   * When `CAMUNDA_BASIC_AUTH_USERNAME` and `CAMUNDA_BASIC_AUTH_PASSWORD` are set for local testing,
   * we use the Cookie-authentication for Operate.
   */
  async getOperateClient(): Promise<OperateApiClient> {
    if (CAMUNDA_CONFIG.CAMUNDA_BASIC_AUTH_USERNAME && CAMUNDA_CONFIG.CAMUNDA_BASIC_AUTH_PASSWORD) {
      const loginData = await _operateLoginCookies(); // POSTs form-data to `/login`
      (this._operate as any).getHeaders = this._getHeadersPatch; // HACKY STUFF
    }
    return this._operate;
  }

and it works, it's just a bit weird to have in our codebase, when the SDK is available.

(In the end, configuration params need to be passed separately to the OperateApiClient.)

A third factor is: will this be solved by supporting the alpha query APIs over REST in 8.6?

🤷 dunno

jwulf commented 1 day ago

How are you spinning up your full env?

Because I run the SDK tests with the full stack using docker-compose - both locally and in GitHub Actions - and we put a default M2M token in the setup for this exact use-case.

The docker-compose setup in the SDK repo starts the complete stack with a known credentials set, and you have full access to all the APIs, including Operate.

pafmaf commented 1 day ago

It's similar, using compose etc., but I can't simply copy the config. It is doable, just takes a little more time to setup everything and getting things to work.