This is an Alpha project and not production ready as is. Use as a starting point but move it out of dev with caution.
Passwordless authentication building block for Flask.
Flask-Pass0 provides the authentication flow for passwordless authentication. This initial version works with magic link (passwordless) login and it is designed to extend to passkey and other passwordless auth formats later. It will handle token generation, expiration, and session management. All security implementation is your responsibility.
Flask-Pass0 is authentication plumbing, not a security solution.
This extension provides:
- Token generation (
secrets.token_urlsafe) - Token expiration checking
- One-time, time-limited token use
- Session management
- Pluggable, overrideable storage interface
You are responsible for implementing:
- Token storage security (hashing, encryption, etc.)
- CSRF protection
- Rate limiting
- Session security (SECRET_KEY, secure cookies)
- Database security
- HTTPS/TLS
- Input validation
- Email delivery security
- All other security measures
from flask import Flask, render_template
from flask_pass0 import Pass0
from flask_pass0.utils import login_required
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['PASS0_DEV_MODE'] = True # Dev only - shows links instead of sending email
pass0 = Pass0(app)
@app.route('/')
@login_required
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)| Option | Default | Description |
|---|---|---|
PASS0_TOKEN_EXPIRY |
10 |
Token expiry in minutes |
PASS0_REDIRECT_URL |
/ |
Where to redirect after login |
PASS0_LOGIN_URL |
/auth/login |
Login page URL |
PASS0_DEV_MODE |
True |
If True, prints magic links to console instead of sending email |
PASS0_SESSION_DURATION |
86400 |
Session lifetime in seconds (24 hours) |
PASS0_APP_NAME |
"Your Application" |
Application name for emails |
from flask_pass0 import Pass0
app = Flask(__name__)
pass0 = Pass0(app) # Uses in-memory storage by defaultWarning: In-memory storage is cleared when the app restarts. For development only.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_pass0 import Pass0
from flask_pass0.storage import SQLAlchemyStorageAdapter
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
db = SQLAlchemy(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True, nullable=False)
def to_dict(self):
return {"id": self.id, "email": self.email}
storage = SQLAlchemyStorageAdapter(user_model=User, session=db.session)
pass0 = Pass0(app, storage_adapter=storage)Implement the StorageAdapter interface:
from flask_pass0.storage import StorageAdapter
class MyStorage(StorageAdapter):
def get_user_by_email(self, email):
"""Retrieve user by email address."""
pass
def get_user_by_id(self, user_id):
"""Retrieve user by ID."""
pass
def create_user(self, email, **kwargs):
"""Create new user."""
pass
def get_or_create_user(self, email):
"""Get existing user or create new one."""
pass
def update_user(self, user_id, **kwargs):
"""Update user data."""
pass
def delete_user(self, user_id):
"""Delete user."""
pass
def store_token(self, token, token_data):
"""Store authentication token."""
pass
def get_token(self, token):
"""Retrieve token data."""
pass
def delete_token(self, token):
"""Delete token (after use)."""
passSecurity Note: Override these methods to implement token hashing, encryption, or other security measures. Tokens are passed to your storage adapter as-is.
from flask_pass0.utils import login_required
@app.route('/profile')
@login_required
def profile():
return "Protected page"from flask_pass0.utils import get_current_user
@app.route('/dashboard')
@login_required
def dashboard():
user = get_current_user()
return f"Welcome {user['email']}"from flask_pass0.utils import is_authenticated
@app.route('/status')
def status():
if is_authenticated():
return "Logged in"
return "Not logged in"from flask_pass0.utils import logout
@app.route('/logout')
def logout_route():
return logout() # Clears session and redirects to loginFlask-Pass0 uses Flask-Mail for sending magic links. Configure Flask-Mail in your app:
app.config['MAIL_SERVER'] = 'smtp.example.com'
app.config['MAIL_PORT'] = 587
app.config['MAIL_USE_TLS'] = True
app.config['MAIL_USERNAME'] = 'your-email@example.com'
app.config['MAIL_PASSWORD'] = 'your-password'
app.config['MAIL_DEFAULT_SENDER'] = 'noreply@example.com'Set PASS0_DEV_MODE = False to send actual emails instead of printing to console.
Flask-Pass0 registers these routes under /auth (configurable):
GET /auth/login- Login pagePOST /auth/request-magic-link- Request magic link (expects JSON:{"email": "user@example.com"})GET /auth/verify/<token>- Verify magic link tokenGET /auth/logout- Logout
from flask_pass0.utils import (
login_required, # Decorator for protected routes
get_current_user, # Returns current user dict or None
is_authenticated, # Returns True if user is logged in
logout # Logs out and redirects to login
)-
Set a strong SECRET_KEY
import secrets app.config['SECRET_KEY'] = secrets.token_hex(32)
-
Disable development mode
app.config['PASS0_DEV_MODE'] = False
-
Configure Flask-Mail with your email provider (SendGrid, AWS SES, Mailgun, etc.)
-
Implement security measures (see Security Checklist below)
Before deploying to production:
- Strong
SECRET_KEYconfigured (32+ random bytes) -
PASS0_DEV_MODE = False - HTTPS enabled on your server
- Session cookies secured (
SESSION_COOKIE_SECURE,SESSION_COOKIE_HTTPONLY,SESSION_COOKIE_SAMESITE) - CSRF protection added (Flask-WTF recommended)
- Rate limiting implemented (Flask-Limiter recommended)
- Token security implemented in storage adapter (hashing/encryption)
- Database connections secured (TLS, encryption at rest)
- Email authentication configured (SPF, DKIM, DMARC)
- Logging and monitoring active
- Input validation added to your routes
- Backup procedures in place
These are YOUR responsibility to implement. Flask-Pass0 does not include these features.
For examples of CSRF protection, rate limiting, token hashing, and other security measures, consult:
- No built-in token hashing (implement in your storage adapter)
- No built-in CSRF protection (add Flask-WTF)
- No built-in rate limiting (add Flask-Limiter)
- No password-based authentication (passwordless only)
- No multi-factor authentication (future roadmap)
- Email delivery requires Flask-Mail and external email service
- Session security depends on your Flask configuration
When PASS0_DEV_MODE = True:
- Magic links are printed to console instead of being emailed
- Useful for testing without email configuration
- Must be disabled in production
Example console output:
MAGIC LINK for user@example.com: http://localhost:5000/auth/verify/abc123...
Planned features for future releases:
- Passkey/WebAuthn support
- MFA options
- Admin interface
- Additional authentication methods
- Token cleanup utilities
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Submit a pull request
MIT License - see LICENSE file for details.
- Documentation: GitHub README
- Issues: GitHub Issues
- Security: Review and test your own implementation thoroughly
Note: The maintainers of Flask-Pass0 are not responsible for security vulnerabilities in your application. This extension provides authentication flow only.
If taking the "bare bones" approach, be aware:
-
Token Storage: Tokens are passed to your storage adapter in plaintext. Implement hashing/encryption in your adapter.
-
Open Redirects: The
nextparameter is not validated. Validate redirect URLs in your application. -
Account Enumeration: Requesting a magic link for any email succeeds. Decide if this is acceptable for your use case.
-
Email Bombing: No rate limiting per email address. Implement in your application.
-
Token Expiry: Expired tokens remain in storage until manually cleaned. Implement cleanup in your storage adapter.
-
Session Security: Session hijacking prevention depends on your Flask configuration.
-
Input Sanitization: Basic email format validation only. Add comprehensive validation in your application.
These are design decisions, not bugs. Flask-Pass0 gives you control over security implementation.