E_UNAUTHORIZED on (almost) all API endpoints #840

Closed
opened 2026-02-04 21:27:50 +03:00 by OVERLORD · 2 comments
Owner

Originally created by @krylua on GitHub (Oct 15, 2025).

Where is the problem occurring?

I'm having issues using all API endpoints except /api/access-tokens, trying to use any other endpoint results in an E_UNAUTHORIZED "Access token is missing, invalid or expired" error message.

What browsers are you seeing the problem on?

N/A, I've tried with PowerShell and Python using the code snippets provided by Postman, adapting them to my environment. Code below.

Current behavior

I'm able to get a token from /api/access-tokens successfully, but then when I plug the token into the <token> spot shown in Postman any other API calls fail with E_UNAUTHORIZED.

Environment

Note: Data obfuscated for privacy purposes.

Docker

  planka:
    image: ghcr.io/plankanban/planka:2.0.0-rc.4
    container_name: planka
    restart: unless-stopped
    volumes:
      - /docker/planka/app/favicons:/app/public/favicons
      - /docker/planka/app/user-avatars:/app/public/user-avatars
      - /docker/planka/app/background-images:/app/public/background-images
      - /docker/planka/app/attachments:/app/private/attachments
      - /docker/planka/app/logs:/app/logs
    ports:
      - 3030:1337
    environment:
      - LOG_LEVEL=silly
      - TRUST_PROXY=true
      - TOKEN_EXPIRES_IN=365 # In days
      - SHOW_DETAILED_AUTH_ERRORS=true
      - BASE_URL=https://planka.mysite.casa
      - DATABASE_URL=postgresql://postgres@postgres/planka
      - SECRET_KEY=e26d1f36ce071833e29a558829ad50061ba2b49d9b703e67314a7cb064c4ab38cfbbf801aa58bbd4bc56fedceddab520b9c094cf793b5bacc0cc4790e61c53e7
    depends_on:
      postgres:
        condition: service_healthy

  postgres:
    image: postgres:16-alpine
    container_name: postgres-planka
    restart: unless-stopped
    volumes:
      - /docker/planka/db:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=planka
      - POSTGRES_HOST_AUTH_METHOD=trust
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres -d planka"]
      interval: 10s
      timeout: 5s
      retries: 5

NGINX proxy.conf

server {
    listen 443 ssl;
    listen [::]:443 ssl;

    server_name planka.*;

    include /config/nginx/ssl.conf;

    client_max_body_size 0;

    location / {
        include /config/nginx/proxy.conf;
        include /config/nginx/resolver.conf;
        set $upstream_app planka;
        set $upstream_port 1337;
        set $upstream_proto http;
        proxy_pass $upstream_proto://$upstream_app:$upstream_port;
    }
}

PowerShell

[str]$token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY4MjYwZDRiLWE5OGYtNDZhMy04NTgyLTBhMmI1NzI1YWI1OSJ9.eyJpYXQiOjE3NjA1NTA2MjUsImV4cCI6MTc5MjA4NjYyNSwic3ViIjoiMTYyMjAxMDA0MTUxMzAxODM"
2OSJ9.uJnukgF6upVOFghv-qKjP5W2AihjBvzl-Vk7yhb-ezw"
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Accept", "application/json")
$headers.Add("Authorization", "Bearer $token")

$response = Invoke-RestMethod 'https://planka.mysite.casa/api/lists/1622010308799235077/cards' -Method 'GET' -Headers $headers
$response | ConvertTo-Json

Python

import requests

url = "https://planka.mysite.casa/api/lists/1622010308799235077/cards"

payload={}
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY4MjYwZDRiLWE5OGYtNDZhMy04NTgyLTBhMmI1NzI1YWI1OSJ9.eyJpYXQiOjE3NjA1NTA2MjUsImV4cCI6MTc5MjA4NjYyNSwic3ViIjoiMTYyMjAxMDA0MTUxMzAxODM
2OSJ9.uJnukgF6upVOFghv-qKjP5W2AihjBvzl-Vk7yhb-ezw'
}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)

Troubleshooting

I added the following options to the Docker container in hopes of troubleshooting but unfortunately there has been no change. I'll admit to not fully understanding each setting, maybe I've misconfigured something:

      - LOG_LEVEL=silly
      - TRUST_PROXY=true
      - TOKEN_EXPIRES_IN=365 # In days
      - SHOW_DETAILED_AUTH_ERRORS=true

I'm actually a bit annoyed with the lack of logs, even with the 'silly' setting, here's my current planka.log:

2025-10-15 17:50:03 [I] Initializing custom hook (`current-user`)
2025-10-15 17:50:03 [I] Initializing custom hook (`file-manager`)
2025-10-15 17:50:03 [I] Initializing custom hook (`query-methods`)
2025-10-15 17:50:03 [I] Initializing custom hook (`watcher`)
2025-10-15 19:36:26 [I] Initializing custom hook (`current-user`)
2025-10-15 19:36:26 [I] Initializing custom hook (`file-manager`)
2025-10-15 19:36:26 [I] Initializing custom hook (`query-methods`)
2025-10-15 19:36:26 [I] Initializing custom hook (`watcher`)
2025-10-15 19:36:58 [I] Initializing custom hook (`current-user`)
2025-10-15 19:36:58 [I] Initializing custom hook (`file-manager`)
2025-10-15 19:36:58 [I] Initializing custom hook (`query-methods`)
2025-10-15 19:36:58 [I] Initializing custom hook (`watcher`)
2025-10-15 19:38:59 [I] Initializing custom hook (`current-user`)
2025-10-15 19:38:59 [I] Initializing custom hook (`file-manager`)
2025-10-15 19:38:59 [I] Initializing custom hook (`query-methods`)
2025-10-15 19:38:59 [I] Initializing custom hook (`watcher`)

I made sure to stop/remove/recreate the container each time. I'll admit I'm pretty new to using APIs, but I've tried to format the token in many ways such as adding/removing <>, "", '', etc. I've even passed it as a string variable, as can be seen in the PowerShell code, to no avail.

Originally I was doing this all with the initial admin account, however I've created several users with different access rights and I get stuck at the same point each time. I've ensure each user has access to the project in question, etc. I've even double checked the datetime on my local machine, the host machine and the docker container and they all match.

I'm truly lost, could you please assist me? Any guidance would be greatly appreciated.

Originally created by @krylua on GitHub (Oct 15, 2025). ### Where is the problem occurring? I'm having issues using all API endpoints except /api/access-tokens, trying to use any other endpoint results in an E_UNAUTHORIZED "Access token is missing, invalid or expired" error message. ### What browsers are you seeing the problem on? N/A, I've tried with PowerShell and Python using the code snippets provided by [Postman](https://documenter.getpostman.com/view/48263477/2sB3HqJebc), adapting them to my environment. Code below. ### Current behavior I'm able to get a token from /api/access-tokens successfully, but then when I plug the token into the `<token>` spot shown in Postman any other API calls fail with E_UNAUTHORIZED. ### Environment Note: Data obfuscated for privacy purposes. Docker ```Dockerfile planka: image: ghcr.io/plankanban/planka:2.0.0-rc.4 container_name: planka restart: unless-stopped volumes: - /docker/planka/app/favicons:/app/public/favicons - /docker/planka/app/user-avatars:/app/public/user-avatars - /docker/planka/app/background-images:/app/public/background-images - /docker/planka/app/attachments:/app/private/attachments - /docker/planka/app/logs:/app/logs ports: - 3030:1337 environment: - LOG_LEVEL=silly - TRUST_PROXY=true - TOKEN_EXPIRES_IN=365 # In days - SHOW_DETAILED_AUTH_ERRORS=true - BASE_URL=https://planka.mysite.casa - DATABASE_URL=postgresql://postgres@postgres/planka - SECRET_KEY=e26d1f36ce071833e29a558829ad50061ba2b49d9b703e67314a7cb064c4ab38cfbbf801aa58bbd4bc56fedceddab520b9c094cf793b5bacc0cc4790e61c53e7 depends_on: postgres: condition: service_healthy postgres: image: postgres:16-alpine container_name: postgres-planka restart: unless-stopped volumes: - /docker/planka/db:/var/lib/postgresql/data environment: - POSTGRES_DB=planka - POSTGRES_HOST_AUTH_METHOD=trust healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres -d planka"] interval: 10s timeout: 5s retries: 5 ``` NGINX proxy.conf ```Nginx server { listen 443 ssl; listen [::]:443 ssl; server_name planka.*; include /config/nginx/ssl.conf; client_max_body_size 0; location / { include /config/nginx/proxy.conf; include /config/nginx/resolver.conf; set $upstream_app planka; set $upstream_port 1337; set $upstream_proto http; proxy_pass $upstream_proto://$upstream_app:$upstream_port; } } ``` PowerShell ```PowerShell [str]$token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY4MjYwZDRiLWE5OGYtNDZhMy04NTgyLTBhMmI1NzI1YWI1OSJ9.eyJpYXQiOjE3NjA1NTA2MjUsImV4cCI6MTc5MjA4NjYyNSwic3ViIjoiMTYyMjAxMDA0MTUxMzAxODM" 2OSJ9.uJnukgF6upVOFghv-qKjP5W2AihjBvzl-Vk7yhb-ezw" $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" $headers.Add("Accept", "application/json") $headers.Add("Authorization", "Bearer $token") $response = Invoke-RestMethod 'https://planka.mysite.casa/api/lists/1622010308799235077/cards' -Method 'GET' -Headers $headers $response | ConvertTo-Json ``` Python ```Python import requests url = "https://planka.mysite.casa/api/lists/1622010308799235077/cards" payload={} headers = { 'Accept': 'application/json', 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImY4MjYwZDRiLWE5OGYtNDZhMy04NTgyLTBhMmI1NzI1YWI1OSJ9.eyJpYXQiOjE3NjA1NTA2MjUsImV4cCI6MTc5MjA4NjYyNSwic3ViIjoiMTYyMjAxMDA0MTUxMzAxODM 2OSJ9.uJnukgF6upVOFghv-qKjP5W2AihjBvzl-Vk7yhb-ezw' } response = requests.request("GET", url, headers=headers, data=payload) print(response.text) ``` ### Troubleshooting I added the following options to the Docker container in hopes of troubleshooting but unfortunately there has been no change. I'll admit to not fully understanding each setting, maybe I've misconfigured something: ```Dockerfile - LOG_LEVEL=silly - TRUST_PROXY=true - TOKEN_EXPIRES_IN=365 # In days - SHOW_DETAILED_AUTH_ERRORS=true ``` I'm actually a bit annoyed with the lack of logs, even with the 'silly' setting, here's my current planka.log: ``` 2025-10-15 17:50:03 [I] Initializing custom hook (`current-user`) 2025-10-15 17:50:03 [I] Initializing custom hook (`file-manager`) 2025-10-15 17:50:03 [I] Initializing custom hook (`query-methods`) 2025-10-15 17:50:03 [I] Initializing custom hook (`watcher`) 2025-10-15 19:36:26 [I] Initializing custom hook (`current-user`) 2025-10-15 19:36:26 [I] Initializing custom hook (`file-manager`) 2025-10-15 19:36:26 [I] Initializing custom hook (`query-methods`) 2025-10-15 19:36:26 [I] Initializing custom hook (`watcher`) 2025-10-15 19:36:58 [I] Initializing custom hook (`current-user`) 2025-10-15 19:36:58 [I] Initializing custom hook (`file-manager`) 2025-10-15 19:36:58 [I] Initializing custom hook (`query-methods`) 2025-10-15 19:36:58 [I] Initializing custom hook (`watcher`) 2025-10-15 19:38:59 [I] Initializing custom hook (`current-user`) 2025-10-15 19:38:59 [I] Initializing custom hook (`file-manager`) 2025-10-15 19:38:59 [I] Initializing custom hook (`query-methods`) 2025-10-15 19:38:59 [I] Initializing custom hook (`watcher`) ``` I made sure to stop/remove/recreate the container each time. I'll admit I'm pretty new to using APIs, but I've tried to format the token in many ways such as adding/removing `<>`, `""`, `''`, etc. I've even passed it as a string variable, as can be seen in the PowerShell code, to no avail. Originally I was doing this all with the initial admin account, however I've created several users with different access rights and I get stuck at the same point each time. I've ensure each user has access to the project in question, etc. I've even double checked the datetime on my local machine, the host machine and the docker container and they all match. I'm truly lost, could you please assist me? Any guidance would be greatly appreciated.
Author
Owner

@meltyshev commented on GitHub (Oct 16, 2025):

Hi!

I just tested the Python version of the example script, and it works fine on my end. The only thing I noticed is that in the example, the token got split across two lines - probably just a copy-paste issue (otherwise the script wouldn't run at all).

I have a couple of quick questions:

Does authentication through the UI work correctly?

If it does, then the issue likely isn't related to the proxy configuration, since the frontend uses the same API.

How are you obtaining the accessToken?

If you're logging in through the UI, then copying the accessToken from the network response, it won't work - the frontend sends withHttpOnlyToken=true, which means the generated token is only valid when paired with the corresponding httpOnly cookie. This is done for additional security in browsers.

To debug this, I'd recommend trying to get the token directly in your script. For example:

import requests

BASE_URL = 'https://planka.mysite.casa/api' # Adjust to your base URL

AUTH = {
    'emailOrUsername': 'demo', # Replace with your email or username
    'password': 'demo'         # Replace with your password
}

response = requests.post(f'{BASE_URL}/access-tokens', data=AUTH)
access_token = response.json()['item']

# Your part...

url = f'{BASE_URL}/lists/1622010308799235077/cards'

headers = {
    'Accept': 'application/json',
    'Authorization': f'Bearer {access_token}'
}

response = requests.get(url, headers=headers)
print(response.text)
@meltyshev commented on GitHub (Oct 16, 2025): Hi! I just tested the Python version of the example script, and it works fine on my end. The only thing I noticed is that in the example, the token got split across two lines - probably just a copy-paste issue (otherwise the script wouldn't run at all). I have a couple of quick questions: **Does authentication through the UI work correctly?** If it does, then the issue likely isn't related to the proxy configuration, since the frontend uses the same API. **How are you obtaining the `accessToken`?** If you're logging in through the UI, then copying the `accessToken` from the network response, it won't work - the frontend sends `withHttpOnlyToken=true`, which means the generated token is only valid when paired with the corresponding `httpOnly` cookie. This is done for additional security in browsers. To debug this, I'd recommend trying to get the token directly in your script. For example: ```python import requests BASE_URL = 'https://planka.mysite.casa/api' # Adjust to your base URL AUTH = { 'emailOrUsername': 'demo', # Replace with your email or username 'password': 'demo' # Replace with your password } response = requests.post(f'{BASE_URL}/access-tokens', data=AUTH) access_token = response.json()['item'] # Your part... url = f'{BASE_URL}/lists/1622010308799235077/cards' headers = { 'Accept': 'application/json', 'Authorization': f'Bearer {access_token}' } response = requests.get(url, headers=headers) print(response.text) ```
Author
Owner

@krylua commented on GitHub (Oct 16, 2025):

How are you obtaining the accessToken?

If you're logging in through the UI, then copying the accessToken from the network response, it won't work - the frontend sends withHttpOnlyToken=true, which means the generated token is only valid when paired with the corresponding httpOnly cookie. This is done for additional security in browsers.

Thanks for that information, this turned out to be the root cause. I was going by the v2 API documentation and both Swagger and Postman have "withHttpOnlyToken": true documented for the /api/access-tokens endpoint.

I think I did try commenting this out at some point, but it must've been when I was playing around with wrapping the token in different characters as it never worked for me.

For the sake of any future troubleshooters and to answer your other question, yes authentication worked via the frontend for all users.

Thank you for the assistance. Now to figure out how to submit a request to update the documentation.

@krylua commented on GitHub (Oct 16, 2025): > **How are you obtaining the `accessToken`?** > > If you're logging in through the UI, then copying the `accessToken` from the network response, it won't work - the frontend sends `withHttpOnlyToken=true`, which means the generated token is only valid when paired with the corresponding `httpOnly` cookie. This is done for additional security in browsers. Thanks for that information, this turned out to be the root cause. I was going by the v2 API documentation and both [Swagger](https://plankanban.github.io/planka/swagger-ui/#/Access%20Tokens/createAccessToken) and [Postman](https://documenter.getpostman.com/view/48263477/2sB3HqJebc#7ba660b5-ed6d-4e61-9e8a-9da58b4959f6) have `"withHttpOnlyToken": true` documented for the `/api/access-tokens` endpoint. I think I did try commenting this out at some point, but it must've been when I was playing around with wrapping the token in different characters as it never worked for me. For the sake of any future troubleshooters and to answer your other question, yes authentication worked via the frontend for all users. Thank you for the assistance. Now to figure out how to submit a request to update the documentation.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/planka#840