Skip to content

Authentication

jsem-nerad edited this page Sep 1, 2026 · 3 revisions

Authentication

Logging in

The quickest way is to pass all three credentials to the constructor:

from strava_cz import StravaCZ

strava = StravaCZ("your.username", "YourPassword123", "3753")

All three are required. Passing only some of them is a mistake the library refuses rather than silently ignores:

StravaCZ("your.username", "YourPassword123")   # TypeError: ... must be given together

To create the client first and log in later, pass nothing:

strava = StravaCZ()          # no network traffic at all
strava.login("your.username", "YourPassword123", "3753")

Constructing a client never touches the network. The session is warmed up lazily, on the first API call.

Keeping credentials out of your code

The tidiest way is a stored profile. It keeps the username and canteen number in a config file and the password in the operating system's keyring, so nothing sensitive is in your source or your shell history:

from strava_cz import StravaCZ

with StravaCZ.from_profile("skola") as strava:   # omit the name for the default
    strava.menu.fetch()

Create it once with strava-cz profile add skola --username ... --canteen .... Full details, including what happens on a machine with no keyring, are in Profiles.

Otherwise, read them from the environment, or from a file you have not committed:

import os
from strava_cz import StravaCZ

strava = StravaCZ(
    os.environ["STRAVA_USERNAME"],
    os.environ["STRAVA_PASSWORD"],
    os.environ["STRAVA_CANTEEN"],
)

The password is used for the login request and then discarded — it is never stored on the client or the user object.

The user object

strava.user is populated once login succeeds:

user = strava.user

user.username        # "your.username"
user.full_name       # "Vojtěch Nerad"
user.email           # "you@example.com"
user.balance         # 512.5   (a float, always)
user.currency        # "Kč"
user.canteen_name    # "Školní jídelna, Praha 5 - Smíchov"
user.canteen_number  # "3753"
user.id              # the canteen's internal account id
user.is_logged_in    # True

print(strava.user) gives a readable summary.

The balance is refreshed automatically after every ordering operation, from the value the API returns.

user.sid and user.s5url hold the session. They are excluded from the object's repr() so they do not end up in logs by accident.

Logging out

strava.logout()

This ends the session and clears both user and menu in place, so any reference you were already holding stays valid and correctly reports being logged out:

user = strava.user
strava.logout()
assert user is strava.user
assert user.is_logged_in is False

Calling logout() when not logged in does nothing and returns True.

Context manager

Preferred, because it always cleans up:

with StravaCZ("your.username", "YourPassword123", "3753") as strava:
    strava.menu.fetch()
    strava.menu.order_meals(5)
# logged out, connection pool closed

Failed logins

Strava.cz answers a bad login with a non-standard HTTP 555 and an error envelope, which the library turns into an exception:

from strava_cz import AuthenticationError

try:
    strava = StravaCZ("your.username", "wrong-password", "3753")
except AuthenticationError as exc:
    print(exc.message)  # the canteen's own wording
    print(exc.code)     # 20

A login that returns success but no session id is also treated as a failure, so is_logged_in is never True without a usable session.

Client options

import httpx
from strava_cz import StravaCZ

strava = StravaCZ(
    "your.username", "YourPassword123", "3753",
    timeout=30.0,       # default: 15s, 10s to connect
    retries=2,          # retries for connections that never got established
    language="CS",      # `lang` sent with every request
    base_url="https://app.strava.cz",
)

# Or bring your own client, for proxies or custom transports:
strava = StravaCZ(client=httpx.Client(proxy="http://localhost:8080"))

Requests always carry a timeout. If you supply your own httpx.Client, closing it is your responsibility — strava.close() leaves it alone.

Sessions and rate limits

There is no documented rate limit, but the other end is a school canteen's server. Log in once and reuse the client rather than logging in per operation, and do not poll the menu in a tight loop.

Clone this wiki locally