Nobody has started this yet — be first.
Business impact
Every module downstream of get_current_user_id -- the stream generator, the publisher, the Redis key helpers -- treats whatever string this function returns as the identity for that connection. If a client authenticates with Authorization: Bearer alice and this function hands back a mangled id instead of "alice", that connection is durably, silently subscribed to a different user's notification stream than the one everyone else means when they say "alice." Two different ways of supplying the same identity -- the Authorization header vs. the ?user_id= query param the browser test page uses -- would authenticate the same human as two different users who never see each other's notifications, with no error and no log line pointing at why.
Problem
get_current_user_id slices the Authorization header one character too far past the "Bearer " prefix, so the returned user id is missing its first character.
Current behavior
get_current_user_id(authorization='Bearer alice', ...) returns 'lice', not 'alice' -- the slice index used is len("bearer ") + 1, one past where the actual prefix ends.
Expected behavior
Authorization: Bearer alice must resolve to the exact user id "alice" -- not "lice", not " alice", not anything else.
Steps to reproduce
cd fastapi/sse_notificationspython3 -c "import asyncio; from app.auth import get_current_user_id; print(asyncio.run(get_current_user_id(authorization='Bearer alice', x_user_id=None, user_id_query=None)))"lice, not alice.Why this matters
len("bearer ") + 1 reads like a plausible "be careful not to include the space" adjustment to someone skimming the line -- it's an off-by-one that looks more careful than the correct version, which is exactly what makes it easy to wave through in review. It's also the kind of bug that a unit test calling the dependency function directly catches instantly, but that an integration test using a fixed, memorable test username can easily miss if nothing ever compares the returned user id against the sent one.
Suggested approach
Work out exactly how many characters are in the literal string "bearer " (the same one .lower().startswith(...) just checked against), and compare that to what's being passed to the slice.
Acceptance criteria
Verification
pytest practice_tickets/tests/test_ticket_05_bearer_token_off_by_one.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
Sign in to create it automatically, or do it yourself:
$git checkout -b fix/bearer-token-off-by-oneFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin fix/bearer-token-off-by-oneSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign inhttp://localhost:8000http://localhost:8001Authorization: Bearer <user_id>X-User-Id: <user_id>curl localhost:8000/healthz
curl -N -H "Authorization: Bearer alice" localhost:8000/notifications/stream
# watch `: heartbeat` lines arrive roughly every 15s with no other traffic
Work the tickets in practice_tickets/tickets/ (ordered easy → hard); each names one dedicated pytest test in practice_tickets/tests/:
pytest practice_tickets/tests/test_ticket_01_heartbeat_default_margin.py -v # a single ticket
./practice_tickets/run_tickets.sh # all 15, clean pass/fail summary
Tickets 14 and 15 need genuinely concurrent, long-lived streams, which the ASGI test transport used everywhere else in this project can't represent -- their tests spin up a real (short-lived) uvicorn server on a loopback socket instead (LiveServer in practice_tickets/tests/conftest.py).
Level 1
Fix a bug
Read existing behaviour, correct it.