bolt Valebyte VPS from $4/mo — NVMe, 60s deploy.

Get a VPS arrow_forward

Self-Hosted Authentication for Python Web Apps: Bottle-Cork and Server Hardening

calendar_month July 17, 2026 schedule 6 min read visibility 15 views
person
Valebyte Team
summarize

TL;DR

  • Bottle-Cork gives small self-hosted Python apps user accounts, integer-level roles, signed session cookies and registration/reset flows. Put authorisation at the route, keep the signing key secret, and harden the host (default-deny firewall, fail2ban, TLS, reverse proxy) — on a server you actually control.

Bottle-Cork is a lightweight authentication and authorisation library for Python web apps built on the Bottle micro-framework. It gives a small self-hosted application the things you would otherwise re-implement badly: user accounts, role-based permissions, signed session cookies, registration and password-reset flows. This guide explains how Cork works, how it compares to other Python auth options, and how to run an authenticated app safely on your own server.

What Bottle-Cork does at a glance

CapabilityHow Cork handles it
User accountsUsername, hashed password, e-mail, role, plus arbitrary profile data
AuthorisationNamed roles with an integer level; decorators enforce a minimum level
SessionsSigned, expiring cookies via Beaker; no server-side session store required for small apps
RegistrationPending-registration flow with e-mail confirmation token
Password resetTime-limited, signed reset token delivered by e-mail
Storage backendJSON files, MongoDB or SQLAlchemy

How Cork works

Cork sits between Bottle's routing and your handlers. Each request that needs protection calls a Cork method that checks the session cookie, resolves the current user, and either allows the handler to run or aborts with a redirect to the login page. Users, roles and pending registrations live in a backend that you choose at start-up. For a single small app the JSON backend is enough; for anything with concurrent writes, move to MongoDB or a SQLAlchemy store.

Roles are ordered by an integer level. An admin at level 100 can do everything an editor at level 50 can, and more. Handlers declare the minimum level they require, so authorisation is expressed once, at the route, instead of being scattered through your business logic.

A minimal authenticated app

from bottle import Bottle, request, redirect
from cork import Cork
from beaker.middleware import SessionMiddleware

aaa = Cork('conf_dir') # backend: users.json, roles.json, ...
app = Bottle()

@app.post('/login')
def login():
 aaa.login(request.forms.get('user'),
 request.forms.get('pwd'),
 success_redirect='/dashboard',
 fail_redirect='/login')

@app.route('/dashboard')
def dashboard():
 aaa.require(fail_redirect='/login')
 return 'Hello %s' % aaa.current_user.username

@app.route('/admin')
def admin():
 aaa.require(role='admin', fail_redirect='/login')
 return 'Admin area'

wrapped = SessionMiddleware(app, {'session.type': 'cookie',
 'session.encrypt_key': 'CHANGE-ME',
 'session.validate_key': True})

The two ideas worth taking away even if you never use Cork: authorisation belongs at the route via a single require() call, and session integrity depends entirely on a secret signing key that must be long, random and kept off disk in your source tree.

rocket_launch Quick pick

Need a dedicated server?

Compare prices from top providers. Configure and order in minutes.

Browse dedicated servers arrow_forward

Roles and authorisation

Keep the number of roles small and the level gaps wide (for example 0 = private, 30 = user, 60 = editor, 100 = admin). Wide gaps let you insert a new tier later without renumbering everyone. Never check the username to grant power; check the role level. Usernames change, get impersonated in support tickets, and leak into logs.

Password and session security

Cork stores password hashes, not passwords, and signs its session cookies. That is the baseline, not the finish line. Enforce a real minimum password length, rate-limit the login route to blunt credential stuffing, expire sessions on a sensible timeout, and rotate the signing key if it is ever exposed. Serve everything over HTTPS so the session cookie cannot be read on the wire, and set the cookie Secure and HttpOnly.

Bottle-Cork vs other Python authentication options

OptionFrameworkBest forTrade-off
Bottle-CorkBottle / WSGISmall self-hosted apps that need roles without a databaseMinimal ecosystem; you wire e-mail and rate-limiting yourself
Flask-LoginFlaskSession management in Flask appsOnly handles sessions; roles and registration are your job
AuthlibAnyOAuth2 / OpenID Connect providers and clientsHeavier; overkill for a single internal app
Django authDjangoFull apps that already use Django's ORMYou buy into the whole framework
Authelia / KeycloakStandalone SSOProtecting several services behind one loginA separate service to run and secure itself

If you already run more than a couple of self-hosted services, a standalone identity provider such as Keycloak is often the better long-term answer — see our guide on installing Keycloak on a server for SSO and IAM. For a single Bottle app, Cork stays out of your way.

rocket_launch Quick pick

Need a dedicated server?

Compare prices from top providers. Configure and order in minutes.

Browse dedicated servers arrow_forward

Hardening the server behind your login

Authentication is only as strong as the machine it runs on. A perfect login page does nothing if the database port is open to the internet or an old admin panel answers on port 8080. Treat the host itself as part of the auth boundary:

  • Firewall by default-deny. Expose only 443 (and SSH on a key-only, ideally non-default configuration). Tools in the tradition of Firelet, and today nftables or ufw, let you describe the allowed traffic explicitly and audit it as a ruleset rather than a pile of ad-hoc commands.
  • Fail2ban on the login and SSH logs to ban brute-force sources.
  • TLS everywhere with automatic certificate renewal; redirect all HTTP to HTTPS.
  • A reverse proxy (nginx or Caddy) in front of the Python process, so the app never faces the internet directly and you get one place to add headers and rate limits.
  • Least privilege: run the app as a non-root user, and keep the auth backend file readable only by that user.

The first thirty minutes on a new box set the tone for everything after — our checklist for securing a dedicated server right after delivery covers the firewall, SSH and update steps in order.

Choosing a server to self-host an authenticated app

Once real users log in, you want predictable CPU and memory, a fixed IP, and full control of the firewall — none of which you fully get on shared hosting. A private virtual server works for a light app; a dedicated server is the right call once you store real user data, run a database, or need guaranteed resources with no noisy neighbours. If cost is the constraint, a budget dedicated server still gives you the isolation and firewall control that authentication depends on.

Bottle-Cork itself is small, and that is the point: the security work that matters is not the library, it is the discipline around it — hashed credentials, role-based authorisation, signed sessions, and a hardened host underneath. Get those right and even a hundred-line Bottle app is safe to put on the open internet.

support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.