Auth
mountaineer-auth is an opinionated authentication plugin. It ships login, signup, logout, password reset, and email verification flows. The React views are included and pre-built, so a working auth system is a few registrations away.
Other ecosystems sell auth as a hosted add-on. This one is open source, free, and runs inside your app.
Key features:
- Full auth flows: login, signup, logout, password reset, and email verification with pre-built React views
- JWT sessions: cookie-based bearer tokens with configurable expiration
- Authorization dependencies:
require_valid_user,require_admin_user, andpeek_userfor gating your own actions - Admin users: a first-class primitive for marking and requiring administrators
- Self-hosted: passwords hashed with bcrypt, sessions verified locally. No third-party auth service in your request path.
Installation
uv add mountaineer-auth
Models
Subclass the model mixins into your own concrete tables. The mixins carry the columns the plugin needs, and you stay free to add your own:
from mountaineer_auth.models import UserAuthMixin, VerificationState as VerificationStateBase
class User(UserAuthMixin):
pass
class VerificationState(VerificationStateBase):
pass
Controllers
Register the flows you want. Each controller brings its own route and pre-built view:
from mountaineer_auth import controllers as auth_controllers
controller = AppController(...)
controller.register(auth_controllers.LoginController(post_login_redirect="/app"))
controller.register(auth_controllers.SignupController(post_signup_redirect="/app"))
controller.register(auth_controllers.LogoutController(post_logout_redirect="/"))
controller.register(auth_controllers.ForgotPasswordController())
controller.register(auth_controllers.VerifyController())
If you want transactional auth emails (verification, password reset), register the email controllers too:
from mountaineer_auth import emails as auth_emails
controller.register(auth_emails.VerifyEmailController())
controller.register(auth_emails.ForgotPasswordEmailController())
Configuration
Inherit AuthConfig in your app config and point it at your concrete models:
from mountaineer_auth import AuthConfig, AuthEmailConfig
class AppConfig(AuthConfig, DatabaseConfig, ConfigBase):
API_SECRET_KEY: str = "development-secret-key"
AUTH_USER: type[models.User] = models.User
AUTH_VERIFICATION_STATE: type[models.VerificationState] = models.VerificationState
AUTH_EMAIL_ENABLED: bool = False
AUTH_EMAIL: AuthEmailConfig | None = AuthEmailConfig(
unsubscribe_url="http://localhost:3000/unsubscribe",
from_email="onboarding@resend.dev",
from_name="Example App",
server_host="http://localhost:3000",
project_name="Example App",
project_address="123 Example Street, San Francisco, CA 94107",
)
Protecting your routes
Gate any of your own controllers or actions with the bundled dependencies:
from mountaineer_auth.dependencies import require_valid_user
@sideeffect
async def update_profile(
self,
payload: ProfileUpdate,
user: User = Depends(require_valid_user),
):
...
There are three levels of access:
peek_user: returns the user if logged in,Noneotherwise. For pages that adapt to auth state.require_valid_user: raisesUnauthorizedErrorif not logged in.require_admin_user: raises unless the user is an administrator.
Handling unauthorized requests
Decide what happens when an anonymous user hits a protected route. The common pattern is a redirect to login that returns them where they started:
from fastapi import Request, status
from fastapi.responses import RedirectResponse
from mountaineer_auth.exceptions import UnauthorizedError
async def handle_unauthorized(request: Request, exc: UnauthorizedError):
return RedirectResponse(
status_code=status.HTTP_307_TEMPORARY_REDIRECT,
url=f"/auth/login?after_login={request.url}",
)
controller.app.exception_handler(UnauthorizedError)(handle_unauthorized)