Authentication Middleware¶
The Authentication middleware is the piece of Uvicore that figures out who is making the request. It runs early in the middleware stack on every request, identifies the user, and makes that user available to the rest of your application as request.user.
It is the authentication half of HTTP security (who are you?). The authorization half (what are you allowed to do?) is handled separately by route guards and scopes, covered in the API Authorization and Web Authorization pages.
Note
This page explains how the middleware works in general. For route-type specifics, see API Authentication and Web Authentication.
Enabling¶
Authentication is enabled by adding the middleware to your config/http.py stack, once for the web router and/or once for the api router. The only required option is the route_type, which tells the middleware which auth config block to load.
# config/http.py
'Authentication': {
'module': 'uvicore.http.middleware.Authentication',
'options': {
'route_type': 'api', # 'web' or 'api'
}
},
If you don't need user-aware behavior, simply leave this middleware commented out.
request.user Always Exists¶
This is an important and deliberate design choice in Uvicore:
request.useris neverNone.
- If authentication succeeds,
request.useris the authenticatedUserInfo. - If authentication does not succeed,
request.userbecomes the configured anonymous user, still a realUserInfoobject.
@route.get('/welcome')
async def welcome(request: Request):
user = request.user # always a UserInfo, even for anonymous visitors
return {'welcome': user.email}
Because anonymous visitors are still real users (with their own roles and permissions), your public routes behave consistently and your authorization checks work the same whether someone is logged in or not.
Authenticators & User Providers¶
The middleware delegates the actual work to two pluggable pieces, both configured in your config/auth.py:
Authenticators validate the credentials on the incoming request. Uvicore ships with:
- Basic - HTTP Basic auth (username / password)
- JWT - Bearer token validation, ideal for any standards-based IdP (FusionAuth, Keycloak, Auth0, Okta...)
User Providers turn that validated identity into a UserInfo object. Uvicore ships with:
- ORM - loads a real user (with roles, groups and permissions) from your database
- JWT - builds the user directly from the token's claims, no database required
You can configure multiple authenticators, they are tried in order until one succeeds.
# config/auth.py (api block)
'api': {
'default_provider': 'user_model',
'authenticators': {
'jwt': {'default_options': 'jwt'},
'basic': {'default_options': 'basic'},
},
},
The Flow¶
On every request, the Authentication middleware:
- Loads the
authconfig for itsroute_type(weborapi). - Tries each configured authenticator in order.
- Stops at the first authenticator that returns a valid
UserInfo. - Falls back to the configured anonymous user if none succeed.
- Injects
user,route_typeandauthenticatorinto the request scope.
When a user is authenticated, Uvicore also ensures the authenticated permission is present in their permissions list, which makes guarding "any logged-in user" as simple as scopes=['authenticated'].
Accessing the User¶
# The convenient way
user = request.user
# The explicit scope key (identical object)
user = request.scope['user']
The UserInfo object carries everything you need for authorization:
user.id
user.email
user.roles # ['Administrator', ...]
user.permissions # ['posts.read', 'posts.create', ...]
user.superadmin # bool, superadmins bypass all permission checks
user.authenticated # bool
# Permission helpers
user.can('posts.read') # True if user has ALL given permissions
user.can_any(['posts.read', 'admin']) # True if user has ANY
Tip
Build a custom authenticator or user provider by mirroring the framework's own implementations. See the Authentication middleware source and the uvicore/auth/authenticators and uvicore/auth/user_providers packages for reference.
Debugging¶
As with all middleware, dump() and dd() do not work inside the request pipeline. Use the logger instead and watch the output in the terminal running ./uvicore http serve:
uvicore.log.dump(request.user)