[GH-ISSUE #721] Bug: Web interface logout does not invalidate token #260

Closed
opened 2026-02-27 08:16:13 +03:00 by kerem · 2 comments
Owner

Originally created by @Zepmann on GitHub (Oct 30, 2023).
Original GitHub issue: https://github.com/lldap/lldap/issues/721

Situation:
An admin is logged in into lldap's web interface. The admin logs out.

Expected behavior:
All credentials generated to support the session are invalidated. The admin is completely logged out.

Actual behavior:
The refresh token is invalidated, but the token remains valid and can still be used to perform admin actions.

Additional notes:
The token should be invalidated even if only the refresh token is offered in a cookie. Both the refresh token and token are part of the same session. If the session is logged out using only a refresh token (i.e. no token in a cookie), the token associated with the session should still be invalidated.

Proof of concept code:

#!/usr/bin/env bash

httpurl="http://localhost:17170"
username="admin"
password="somepassword"

echo "Logging in using account ${username} and password"
response="$(curl -s -X POST -H 'Content-Type: application/json' --data-binary "{\"username\":\"${username}\", \"password\":\"${password}\"}" "${httpurl}/auth/simple/login")"
refreshToken="$(echo "$response" | jq .refreshToken | tr -d '"')"
token="$(echo "$response" | jq .token | tr -d '"')"
echo ""

echo "Refresh token: $refreshToken"
echo "Token: $token"
echo ""

echo "Testing if refresh token is valid by requesting a new token"
response=$(curl -s -b "refresh_token=${refreshToken}" "${httpurl}/auth/refresh")
token="$(echo "$response" | jq .token | tr -d '"')"
echo "Token: $token"
echo ""

echo "Create and delete a user using token"
curl \
  -s \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $token" \
  --data-binary '{"query":"mutation createUser($user: CreateUserInput!){\n  createUser(user: $user){ \n id\n email\n }\n}","variables":{"user":{"id":"test","email":"test@example.com"}}}' \
  "${httpurl}/api/graphql"
echo ""
curl \
  -s \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $token" \
  --data-binary '{"query":"mutation deleteUser($userId: String!){\n  deleteUser(userId: $userId){\n    ok\n  }\n}","variables":{"userId":"test"}}' "${httpurl}/api/graphql"
echo -e "\n"

echo "Logging out using both refresh token and token"
curl -s -b "refresh_token=${refreshToken}; is_admin=true; token=${token}; user_id=${username}" "${httpurl}/auth/logout"
echo ""

echo "Testing if refresh token is valid by requesting a new token (which will fail, since the refresh token was invalidated previously)"
response=$(curl -s -b "refresh_token=${refreshToken}" ${httpurl}/auth/refresh)
echo "$response"
echo ""

echo "Refresh token: (invalidated)"
echo "Token: $token"
echo ""

echo "Create and delete a user using token (this should not work, but it does)"
curl \
  -s \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $token" \
  --data-binary '{"query":"mutation createUser($user: CreateUserInput!){\n  createUser(user: $user){ \n id\n email\n }\n}","variables":{"user":{"id":"test","email":"test@example.com"}}}' \
  "${httpurl}/api/graphql"
echo ""
curl \
  -s \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $token" \
  --data-binary '{"query":"mutation deleteUser($userId: String!){\n  deleteUser(userId: $userId){\n    ok\n  }\n}","variables":{"userId":"test"}}' "${httpurl}/api/graphql"
echo -e "\n"
Originally created by @Zepmann on GitHub (Oct 30, 2023). Original GitHub issue: https://github.com/lldap/lldap/issues/721 Situation: An admin is logged in into lldap's web interface. The admin logs out. Expected behavior: All credentials generated to support the session are invalidated. The admin is completely logged out. Actual behavior: The refresh token is invalidated, but the token remains valid and can still be used to perform admin actions. Additional notes: The token should be invalidated even if only the refresh token is offered in a cookie. Both the refresh token and token are part of the same session. If the session is logged out using only a refresh token (i.e. no token in a cookie), the token associated with the session should still be invalidated. Proof of concept code: ``` bash #!/usr/bin/env bash httpurl="http://localhost:17170" username="admin" password="somepassword" echo "Logging in using account ${username} and password" response="$(curl -s -X POST -H 'Content-Type: application/json' --data-binary "{\"username\":\"${username}\", \"password\":\"${password}\"}" "${httpurl}/auth/simple/login")" refreshToken="$(echo "$response" | jq .refreshToken | tr -d '"')" token="$(echo "$response" | jq .token | tr -d '"')" echo "" echo "Refresh token: $refreshToken" echo "Token: $token" echo "" echo "Testing if refresh token is valid by requesting a new token" response=$(curl -s -b "refresh_token=${refreshToken}" "${httpurl}/auth/refresh") token="$(echo "$response" | jq .token | tr -d '"')" echo "Token: $token" echo "" echo "Create and delete a user using token" curl \ -s \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $token" \ --data-binary '{"query":"mutation createUser($user: CreateUserInput!){\n createUser(user: $user){ \n id\n email\n }\n}","variables":{"user":{"id":"test","email":"test@example.com"}}}' \ "${httpurl}/api/graphql" echo "" curl \ -s \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $token" \ --data-binary '{"query":"mutation deleteUser($userId: String!){\n deleteUser(userId: $userId){\n ok\n }\n}","variables":{"userId":"test"}}' "${httpurl}/api/graphql" echo -e "\n" echo "Logging out using both refresh token and token" curl -s -b "refresh_token=${refreshToken}; is_admin=true; token=${token}; user_id=${username}" "${httpurl}/auth/logout" echo "" echo "Testing if refresh token is valid by requesting a new token (which will fail, since the refresh token was invalidated previously)" response=$(curl -s -b "refresh_token=${refreshToken}" ${httpurl}/auth/refresh) echo "$response" echo "" echo "Refresh token: (invalidated)" echo "Token: $token" echo "" echo "Create and delete a user using token (this should not work, but it does)" curl \ -s \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $token" \ --data-binary '{"query":"mutation createUser($user: CreateUserInput!){\n createUser(user: $user){ \n id\n email\n }\n}","variables":{"user":{"id":"test","email":"test@example.com"}}}' \ "${httpurl}/api/graphql" echo "" curl \ -s \ -H 'Content-Type: application/json' \ -H "Authorization: Bearer $token" \ --data-binary '{"query":"mutation deleteUser($userId: String!){\n deleteUser(userId: $userId){\n ok\n }\n}","variables":{"userId":"test"}}' "${httpurl}/api/graphql" echo -e "\n" ```
kerem 2026-02-27 08:16:13 +03:00
Author
Owner

@nitnelave commented on GitHub (Oct 30, 2023):

Yep, I can reproduce the bug. However, looking at the code it seems to do the right thing, the bug is not obvious. I'll have a deeper look.

Note that in case of multi-instance deployment, it's possible to be logged out from one instance but not (immediately) logged out from others; this is a flaw in the current design that should be fixed by an additional round-trip to the DB for valid tokens. But this is a separate issue.

<!-- gh-comment-id:1785417261 --> @nitnelave commented on GitHub (Oct 30, 2023): Yep, I can reproduce the bug. However, looking at the code it _seems_ to do the right thing, the bug is not obvious. I'll have a deeper look. Note that in case of multi-instance deployment, it's possible to be logged out from one instance but not (immediately) logged out from others; this is a flaw in the current design that should be fixed by an additional round-trip to the DB for valid tokens. But this is a separate issue.
Author
Owner

@nitnelave commented on GitHub (Oct 30, 2023):

Thanks for the report and the reproduction script! I had a fancy blacklist scheme, but forgot to actually populate the DB when issuing a JWT...

<!-- gh-comment-id:1785967105 --> @nitnelave commented on GitHub (Oct 30, 2023): Thanks for the report and the reproduction script! I had a fancy blacklist scheme, but forgot to actually populate the DB when issuing a JWT...
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/lldap-lldap#260
No description provided.