Skip to content

Python: [Bug]: DevUI bearer-token authentication returns 500 for non-ASCII tokens #9713

Description

Observed Behavior

DevUI bearer-token authentication can return HTTP 500 when the presented bearer token or the configured DEVUI_AUTH_TOKEN contains non-ASCII characters.

The authentication middleware uses secrets.compare_digest() with string values. When either value contains non-ASCII characters, compare_digest() raises a TypeError instead of returning a comparison result.

As a result:

  1. A request containing a non-ASCII value in the Authorization: Bearer ... header results in HTTP 500 instead of HTTP 401 Unauthorized.
  2. If DEVUI_AUTH_TOKEN itself contains non-ASCII characters, a request containing the correct token also results in HTTP 500, making that configured token unusable.

The failure is therefore a correctness/robustness issue in the authentication middleware. I did not observe an authentication bypass; the failure is effectively fail-closed.

Expected Behavior

Invalid or malformed bearer tokens should be rejected with HTTP 401 Unauthorized rather than causing an internal server error.

A valid DEVUI_AUTH_TOKEN containing non-ASCII characters should also be accepted when the client supplies the corresponding token.

Authentication input should not cause the middleware to raise an uncaught TypeError.

Steps to Reproduce

  1. Start a DevUI server with bearer-token authentication enabled.
  2. Set DEVUI_AUTH_TOKEN to an ASCII token and make a request with an ASCII bearer token. The request behaves normally.
  3. Make another request with an Authorization header containing a non-ASCII byte in the bearer token.
  4. Observe that the request returns HTTP 500 instead of HTTP 401.
  5. Alternatively, configure DEVUI_AUTH_TOKEN with a non-ASCII character, for example sécret.
  6. Send a request using Authorization: Bearer sécret.
  7. Observe that authentication raises an internal error and the request returns HTTP 500 instead of authenticating successfully.

Minimal Reproduction

The issue is reproducible against the DevUI implementation in:

python/packages/devui/agent_framework_devui/_server.py

The bearer-token authentication middleware compares the presented token with the configured token using secrets.compare_digest().

A minimal configuration is:

DEVUI_AUTH_TOKEN=sécret

Then send a request with:

Authorization: Bearer sécret

The authentication middleware raises a TypeError from secrets.compare_digest() rather than successfully comparing the values.

The same behavior can be reproduced with a non-ASCII byte in the presented Authorization header while the configured token remains ASCII.

I also reproduced this against the actual DevUI application rather than only testing the comparison function in isolation.

Error Messages and Stack Traces

TypeError: comparing strings with non-ASCII characters is not supported


This originates from the use of `secrets.compare_digest()` with `str` values containing non-ASCII characters.

The resulting exception is not handled by the authentication middleware, causing the request to return HTTP 500.

No credentials or authorization values are included in the reported traceback.

Package Versions

Agent Framework Core version: 1.23.0

Python Version

Python 3.13.15

Operating System

Windows

Regression

Unknown

Additional Context

I reproduced the behavior with the following cases:

Case | Result -- | -- Wrong ASCII token | 401 Correct ASCII token | 200 Non-ASCII value in presented token | 500 Correct non-ASCII configured token | 500

The likely cause is that secrets.compare_digest() is being called with str values. Python's secrets.compare_digest() does not support non-ASCII characters when comparing strings and raises TypeError.

I have prepared a small local patch that performs the comparison using byte representations and added regression tests covering both affected cases.

The patch is intentionally limited to the DevUI authentication path.

I also noticed the same general hmac.compare_digest() pattern in two hosting samples:

  • samples/04-hosting/af-hosting/local_telegram/app.py

  • samples/04-hosting/foundry-hosted-agents/invocations/telegram/main.py

Those are samples rather than the shipped DevUI package, so I have not included them in this issue's primary scope.

Acknowledgements

  • I searched existing issues and did not find a duplicate.
  • I personally verified this behavior and the reproduction details are authentic.
  • I will wait for explicit maintainer agreement before starting implementation of a non-trivial change.

Activity

  1. added
    pythonUsage: [Issues, PRs], Target: Python
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Oct 26, 2026
  2. added
    needs-maintainer-triageUsage: [Issues], Target: all issues that the automated triage flow failed to process
    on Oct 26, 2026
  3. lisry-1 commented on Oct 26, 2026

    @lisry-1

    Reproduced and opened #9714 with a fix.

    auth_middleware calls secrets.compare_digest(token, expected_token) on two str values. compare_digest only accepts ASCII str and raises TypeError for anything else, so:

    • a wrong-but-non-ASCII bearer token answers 500 instead of 401;
    • a non-ASCII DEVUI_AUTH_TOKEN never matches, because the comparison raises before it can succeed.

    The fix adds DevServer._token_matches(), which compares bytes instead of str. Bytes comparison has no ASCII restriction. The presented value is re-encoded with latin-1 (headers travel as bytes and Starlette hands them back decoded as latin-1, so this recovers the exact wire bytes), and the configured token is encoded as UTF-8 since it is text. ASCII tokens compare bit-for-bit as before.

    Sending the header as raw bytes against /v1/entities:

                           before      after
    ascii   / ascii        200         200
    ascii   / non-ascii    500         401
    non-ascii / non-ascii  500         200
    non-ascii / ascii      500         401
    

    Four parametrized test cases cover this. They fail without the fix (assert 500 == 401, assert 500 == 200).

    One judgement call worth your input: there is no standard encoding for a non-ASCII bearer token (RFC 6750 restricts b64token to ASCII), so I picked wire-bytes/UTF-8, which is what makes the non-ASCII DEVUI_AUTH_TOKEN case work. If you would rather treat any non-ASCII token as simply invalid, the alternative is a try/except TypeError around the existing call — that removes the 500 but leaves such a token unusable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-maintainer-triageUsage: [Issues], Target: all issues that the automated triage flow failed to processpythonUsage: [Issues, PRs], Target: PythontriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions