microdot.login — user login flow¶
A higher-level wrapper around microdot.session that implements
the conventional username / password login flow: a user loader
callback maps the session’s stored user-id back to the application’s
user object, route decorators redirect unauthenticated requests to a
configurable login URL, and optional “remember me” cookies let returning
visitors stay signed in across browser sessions.
Requires microdot.session to be initialized on the application
before the login object is constructed – the session is where the
user-id is stored.
class Login¶
- class microdot.login.Login(login_url: str = '/login')¶
- login_url
URL the decorator redirects unauthenticated requests to. The application provides the actual login form/handler at this route. Default
'/login'.
- user_loader(f)¶
Decorator that registers the user-resolver callback. f takes the user-id stored in the session and returns the user object (or
Noneif the id is no longer valid – a deleted account, a revoked session, etc.).login = Login() @login.user_loader async def load_user(user_id): return users.get(user_id)
- __call__(f)¶
Decorating a route with the
Logininstance gates it behind authentication:@app.get('/dashboard') @login async def dashboard(request): user = request.g.current_user # ...
Unauthenticated requests are redirected to login_url with a
?next=<original-url>query parameter so the login handler can send the user back where they came from.
- fresh(f)¶
Like
__call__(), but rejects sessions restored from a “remember me” cookie – the user must have logged in explicitly since the last full login. Used to gate sensitive routes (password change, account deletion) so a stolen remember-me cookie cannot reach them.
- async login_user(request, user, remember: bool | int = False, redirect_url: str = '/')¶
Mark user as logged in for request. Stores the user id in the session and returns a redirect response.
- user
The user object returned by the user loader. Must have an
idattribute.- remember
If truthy, also set a long-lived
_remembercookie. PassTruefor the default 30 days, or an integer for the number of days the cookie should last.- redirect_url
Where to redirect after login. Overridden by
?next=<url>from the original request if it points to a same-site path.
Returns the redirect response – return its value from the login route handler.
- async logout_user(request)¶
Clear the user id from the session and remove any
_remembercookie. Use from a/logoutroute:@app.post('/logout') async def logout(request): await login.logout_user(request) return redirect('/')
- async get_current_user(request)¶
Return the currently logged-in user object (or
None). Memoized onrequest.g.current_userso repeated calls within one request hit the loader only once.
Example¶
A minimal login flow:
from microdot import Microdot, redirect
from microdot.session import Session
from microdot.login import Login
app = Microdot()
Session(app, secret_key=load_secret())
login = Login()
@login.user_loader
async def load_user(user_id):
return users.get(user_id)
@app.get('/login')
async def login_page(request):
return Response.send_file('static/login.html')
@app.post('/login')
async def do_login(request):
user = authenticate(request.form['user'], request.form['pass'])
if not user:
return redirect('/login?error=1')
return await login.login_user(request, user, remember=True)
@app.get('/dashboard')
@login
async def dashboard(request):
return 'hi ' + request.g.current_user.name
@app.post('/logout')
async def logout(request):
await login.logout_user(request)
return redirect('/')
The _remember cookie is itself a signed JWT (using the session’s
secret), so a stolen cookie cannot be reused on a different
application or after the secret has been rotated.