# JWT login always returns 404 “user does not exist / not active / deleted” (Passbolt 5.4.1)

**URL:** <https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664>\
**Category:** API & Customization issues\
**Created:** [September 16, 2025, 1:56pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664 "2025-09-16T13:56:26Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![fabricio0915](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/fabricio0915/32/5322_2.png) [@fabricio0915](https://community.passbolt.com/u/fabricio0915)\
**Post date:** [September 16, 2025, 1:56pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/1 "2025-09-16T13:56:26Z")

</div>

Hi everyone,  
I’m trying to use **JWT Authentication** but I consistently receive **404** :

```json
{
  "header": {
    "id": "e9139c26-4c6f-4e52-bf7e-05d11f7401c7",
    "status": "error",
    "servertime": 1758029601,
    "action": "28c0972b-e6a2-5d44-a5cb-bc2d11799cc1",
    "message": "The user does not exist or is not active or has been deleted.",
    "url": "/auth/jwt/login.json",
    "code": 404
  },
  "body": ""
}

```

**Server log:**

```bash
2025-09-16 13:33:21 error: [Cake\Http\Exception\NotFoundException]
The user does not exist or is not active or has been deleted.
in /usr/share/php/passbolt/plugins/PassboltCe/JwtAuthentication/src/Controller/JwtLoginController.php on line 77

```

### Environment

- Passbolt CE **5.4.1**

### What I have verified

1. **User exists and is active in the database**

```sql
SELECT id, username, active, deleted FROM users
WHERE id = '<USER_UUID>';
-- returns: active=1, deleted=0

SELECT user_id, fingerprint, deleted FROM gpgkeys
WHERE user_id = '<USER_UUID>';
-- returns: fingerprint = 58E98A725953255F37AF622B4B903A6E7B2AA0E6, deleted=0

```

1. **PGP challenge is correct (sign-then-encrypt)**

- **Encrypted** to the **server public key** from `GET /auth/verify.json` (recipient keyid matches).

- **Signed** with the user’s private key (GnuPG shows **Good signature** ) with fingerprint:  
`58E9 8A72 5953 255F 37AF 622B 4B90 3A6E 7B2A A0E6`.

- Decrypting locally (for debugging) shows this **inner JSON** :

```json
{
  "version": "1.0.0",
  "domain": "https://<my-passbolt-domain>",
  "verify_token": "9640cd11-b6ed-4246-9774-61625b1660d7",
  "verify_token_expiry": 1757705719
}

```

(Expiry is an integer epoch in the future; token is UUID with hyphens.)

1. **HTTP flow / CSRF**

- I request `csrfToken` via `GET /auth/verify.json` and send it back on the POST (cookie + `X-CSRF-Token`), with `Content-Type: application/json`.

- Minimal repro:

```bash
# 1) get csrf + cookies
curl -sS -c cookies.txt https://<my-passbolt-domain>/auth/verify.json >/dev/null
CSRF=$(awk '/csrfToken/ {print $7}' cookies.txt)

# 2) send the armored PGP challenge
CH=$(jq -Rs . < challenge.asc)

# 3) POST login
curl -i -b cookies.txt \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: $CSRF" \
  -d "{\"user_id\":\"<USER_UUID>\",\"challenge\":$CH}" \
  https://<my-passbolt-domain>/auth/jwt/login.json
# → consistently returns 404 (same message)

```

### Ask

Given the validations above (user active, GPG key present and matching, PGP challenge valid, CSRF handled), what else should I check to understand why `/auth/jwt/login.json` returns **404** (`FAILURE_IDENTITY_NOT_FOUND`) in this scenario?

Thanks!

---

<div class="post-metadata">

**Author:** ![remy](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/remy/32/17_2.png) [@remy](https://community.passbolt.com/u/remy)\
**Post date:** [September 16, 2025, 2:14pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/2 "2025-09-16T14:14:12Z")

</div>

Hello,

The flow you are using is not correct (some vibe coding led you there maybe? 😃 ). It’s mixing both authentication flows (GpgAuth and GpgJwtAuth).

For GpgJwtAuth authentication there is only one endpoint to call, e.g.

```auto
POST /auth/jwt/login.json
{
  "user_id": "f848277c-5398-58f8-a82a-72397af2d450"
  "challenge": "-----BEGIN PGP MESSAGE-----"
}

```

Where challenge is encrypted with server public key and signed with user private key:

```auto
{
  "version": "1.0.0",
  "domain": "https://passbolt.dev/workspace", // your domain
  "verify_token":"799c69c7-1789-4d87-9fbf-02529b0d21dc", // your random uuid
  "verify_token_expiry": 1622630495, // current unix time + 2min or so
}

```

Once the initial request has been verified, the server can return the success response. It contains the following:

```auto
{
  "header": {
    "id": "799c69c7-1789-4d87-9fbf-02529b0d21dc",
    "status": "success",
    "servertime": 1554909967,
    "action": "ad71952e-7842-599e-a19e-3a82e6974b23",
    "message": "The operation was successful.",
    "url": "\/auth\/jwt\/login.json",
    "code": 200
  },
  "body": {
    "challenge": "-----BEGIN PGP MESSAGE-----"
  }
}

```

The tokens are contained within the challenge returned by the login endpoint encrypted using the user OpenPGP keys. Once decrypted the challenge includes access tokens, verify token, refresh token. The access tokens are signed with the server public RSA key (see JWKS endpoint).

```auto
{
  "version": "v1.0.0",
  "domain": "https://passbolt.dev/workspace",
  "verify_token": "799c69c7-1789-4d87-9fbf-02529b0d21dc", // from previous step
  "access_token": "<JWT token>",
  "refresh_token": "899c69c7-1789-4d87-9fbf-02529b0d21dd",
}

```

Ref. [https://docs.google.com/document/d/1KNpnKNj0c8V3McFn160GErtxmWk3Z9KFfPLV7VBCQyQ/edit?tab=t.0](https://docs.google.com/document/d/1KNpnKNj0c8V3McFn160GErtxmWk3Z9KFfPLV7VBCQyQ/edit?tab=t.0)

---

<div class="post-metadata">

**Author:** ![fabricio0915](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/fabricio0915/32/5322_2.png) [@fabricio0915](https://community.passbolt.com/u/fabricio0915)\
**Post date:** [September 16, 2025, 2:20pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/4 "2025-09-16T14:20:52Z")

</div>

When i go directly to:

```auto
POST /auth/jwt/login.json
{
  "user_id": "f848277c-5398-58f8-a82a-72397af2d450"
  "challenge": "-----BEGIN PGP MESSAGE-----"
}

```

i recive:

```auto
{
    "header": {
        "id": "26fae4e9-7121-4ddf-bed3-1eec394b188f",
        "status": "error",
        "servertime": 1758032328,
        "action": "28c0972b-e6a2-5d44-a5cb-bc2d11799cc1",
        "message": "Missing or incorrect CSRF cookie type.",
        "url": "/auth/jwt/login.json",
        "code": 403
    },
    "body": ""
}

```

I should disable CSRF cookie then?

---

<div class="post-metadata">

**Author:** ![remy](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/remy/32/17_2.png) [@remy](https://community.passbolt.com/u/remy)\
**Post date:** [September 16, 2025, 2:23pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/5 "2025-09-16T14:23:25Z")

</div>

> [@fabricio0915](#):
>
> I should disable CSRF cookie then?

CSRF Cookie is not needed for this endpoint

---

<div class="post-metadata">

**Author:** ![fabricio0915](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/fabricio0915/32/5322_2.png) [@fabricio0915](https://community.passbolt.com/u/fabricio0915)\
**Post date:** [September 16, 2025, 2:33pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/6 "2025-09-16T14:33:38Z")

</div>

strange, because I when not set this cookie, keep return this response:

```json
{
    "header": {
        "id": "26fae4e9-7121-4ddf-bed3-1eec394b188f",
        "status": "error",
        "servertime": 1758032328,
        "action": "28c0972b-e6a2-5d44-a5cb-bc2d11799cc1",
        "message": "Missing or incorrect CSRF cookie type.",
        "url": "/auth/jwt/login.json",
        "code": 403
    },
    "body": ""
}

```

I need to set any environment variable? to disable this validation?

---

<div class="post-metadata">

**Author:** ![remy](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/remy/32/17_2.png) [@remy](https://community.passbolt.com/u/remy)\
**Post date:** [September 16, 2025, 3:01pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/7 "2025-09-16T15:01:10Z")

</div>

> [@fabricio0915](#):
>
> `Missing or incorrect CSRF cookie type.`

I’m not managing to reproduce your problem do you have the full curl query you are sending with header and cookies if any?

---

<div class="post-metadata">

**Author:** ![fabricio0915](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/fabricio0915/32/5322_2.png) [@fabricio0915](https://community.passbolt.com/u/fabricio0915)\
**Post date:** [September 16, 2025, 3:13pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/8 "2025-09-16T15:13:16Z")

</div>

```bash
curl --location 'https://mydomain/auth/jwt/login.json' \
--header 'Content-Type: application/json' \
--header 'Cookie: AWSALBAPP-0=_remove_; AWSALBAPP-1=_remove_; AWSALBAPP-2=_remove_; AWSALBAPP-3=_remove_; passbolt_session=1ub2vdq587721vo4hm22kpt5bo' \
--data '{"user_id":"2f57f325-dac3-444e-b759-73f762c731e0", "challenge": "\"-----BEGIN PGP MESSAGE-----\\n\\nhQGMA9\\u002B0R5V2cYciAQv/VvVvXmknUiqD6nhQy4dHPMgrzJEcT5IVoAf7y/xhFHDV\\nnTY/yeNw7yynjJV2zjse33vHNp4NvQFlqbzQp9CSKtpWPgM2c3KHr4VsJ4B5Mgwf\\naQSc1qiEkqQ4TyZX3ChJfJhjDBfVAT09BfRyAsbGdkOA4IjfWERhD1rZN20TU/Ic\\n9vRava6R40upPLe5l8vXK5Qu4C8qAX\\u002BY\\u002BL8LIdm/DzCRzkGOE9IN3NA/Nx/dbA5B\\ni8PZLXXu66iVNjwXx1dpwLwOip3TJIx0Vm7D4DZapv32LkQpkeQGgYvgphkYrUpa\\nBzCFz0897w1aa6/Z2ONGa8RtzKbou4/ZYZrxC62ca8Pbq3I8C4TkWzmXgZyi3mB6\\notPRthXcXX5e3RpnQAvEQDeH7Ff/lQ0Ql2QtzqCcP/e1ntQNlgYEzc\\u002BOmMO9gl\\u002B0\\nswfxUc2yNXqORAXg3oufv5nb1\\u002B02IEod35AVlTkCKJZUG7VY82Q0q2jODQWimhZn\\n2nIDC3qmP7YOM3ubVYc/0sCBAUfNTLb/sIlDwXyS7PAid2Y4Q5DsA6WKTOLicFK8\\nTu6TOkWOKENYxtIdkOKWITbRAkfSaATRqQovtVdHSwn6dRE3vOscuNEWnTBIq99O\\n3GXnn5opeNWDxBePDt2PkDau6uKfj0S2vKk3gdZPOEXuIhklq7pCVi49RHxaopWx\\nvANxFuCCqJjlByHTHXby8bj17X8pK1I5gOEEkXx\\u002Bs8RhbJz/xSWQ1TFTCIWbqKMW\\n\\u002BfI6h2B3QcmtWJRvm9rTz/IultJ0DEJSOAustZx0C74xVDtr\\u002B\\u002BRG7rRv0biZ3u\\u002Bl\\nyWygCuFBBLNgJrCuipK7JRlghBbF/l749b36Fq2EUEYrKgXQeEhvc78K\\u002BlEWQN9K\\nXt2K13htkTOoF8DGWwZPcD/2WzMwv//0CFphrokqXPjj0Jc/RmVJ6gcLtqgouy3V\\ns2Dy\\n=Jocl\\n-----END PGP MESSAGE-----\\n\""}'

```

---

<div class="post-metadata">

**Author:** ![remy](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/remy/32/17_2.png) [@remy](https://community.passbolt.com/u/remy)\
**Post date:** [September 16, 2025, 3:25pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/9 "2025-09-16T15:25:18Z")

</div>

Still not able to reproduce, it should look like this nonetheless:

```auto
curl --request POST \
  --url https://passbolt-api/auth/jwt/login.json \
  --header 'content-type: application/json' \
  --data '{"user_id": "uuid", "challenge": "-----BEGIN PGP MESSAGE-----"}'

```

No need to send passbolt\_session cookie or any other cookie for that matter. Request should be POST.

---

<div class="post-metadata">

**Author:** ![fabricio0915](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/fabricio0915/32/5322_2.png) [@fabricio0915](https://community.passbolt.com/u/fabricio0915)\
**Post date:** [September 17, 2025, 2:12am UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/10 "2025-09-17T02:12:18Z")

</div>

@remy I try to use your example from another topic, and I was able to reproduce the same output.

[https://community.passbolt.com/t/jwt-authentication-issues-with-api/10999/6?u=fabricio0915](https://community.passbolt.com/t/jwt-authentication-issues-with-api/10999/6)

```bash
(node:13185) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
{
    "header": {
        "id": "7f6b1eff-c0b5-4094-bc7c-25c5ae623293",
        "status": "error",
        "servertime": 1758074821,
        "action": "28c0972b-e6a2-5d44-a5cb-bc2d11799cc1",
        "message": "Missing or incorrect CSRF cookie type.",
        "url": "\/auth\/jwt\/login.json",
        "code": 403
    },
    "body": ""
}

```

and when I add the X-CSRF-Token header, the output is:

```json
{
    "header": {
        "id": "c2c69b2b-1682-4be3-bdb8-281d85026cb9",
        "status": "error",
        "servertime": 1758074879,
        "action": "28c0972b-e6a2-5d44-a5cb-bc2d11799cc1",
        "message": "The user does not exist or is not active or has been deleted.",
        "url": "/auth/jwt/login.json",
        "code": 404
    },
    "body": ""
}

```

Would you recommend to implement de old authentication method (the deprecated one)?

Using the go example on the Passbolt Github, I was able to connect to the Passbolt api, but these example use the old way..

---

<div class="post-metadata">

**Author:** ![fabricio0915](https://yyz1.discourse-cdn.com/flex031/user_avatar/community.passbolt.com/fabricio0915/32/5322_2.png) [@fabricio0915](https://community.passbolt.com/u/fabricio0915)\
**Post date:** [September 17, 2025, 5:56pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/11 "2025-09-17T17:56:29Z")

</div>

@remy can you help with that point? if necessary i can provide more information or do more tests to resolve this issue.

---

<div class="post-metadata">

**Author:** ![dion](https://avatars.discourse-cdn.com/v4/letter/d/cab0a1/32.png) [@dion](https://community.passbolt.com/u/dion)\
**Post date:** [September 23, 2025, 8:14am UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/12 "2025-09-23T08:14:46Z")

</div>

The jwt-auth check should be before the csrf-validation (as I look at the github source).

Apparently, your login-request does not seem to be recognized as a jwt-login request.  
The post you link is an example I used as well to get the jwt-login working with js/node. What does your fetch look like?

---

<div class="post-metadata">

**Author:** ![stanislav.zaharov](https://avatars.discourse-cdn.com/v4/letter/s/ee59a6/32.png) [@stanislav.zaharov](https://community.passbolt.com/u/stanislav.zaharov)\
**Post date:** [October 13, 2025, 7:32pm UTC](https://community.passbolt.com/t/jwt-login-always-returns-404-user-does-not-exist-not-active-deleted-passbolt-5-4-1/13664/13 "2025-10-13T19:32:32Z")

</div>

Had the same issue today. The JWT authentication plugin was disabled in my case, that was the problem.

**Fix for Docker:**

```yaml
environment:
  PASSBOLT_PLUGINS_JWT_AUTHENTICATION_ENABLED: "true"

```

Restart container and you’re good.  
No cookies, no csrf-token needed. Just enable the plugin and JWT login works.

For non-Docker (not 100% sure, but should work):

```php
'passbolt' => [
    'plugins' => [
        'jwtAuthentication' => [
            'enabled' => true,
        ],
    ],
],

```

That’s it.
