Haze is a high-performance, easy-to-use Magic Link Authentication service for Python applications. Generate secure authentication links that work across devices with minimal setup.
- β‘ Fast & Efficient - Optimized core with minimal overhead
- π Ultra Secure - Modern cryptography with JWT
- π§ Highly Configurable - Like Neovim, but for authentication
- 𧩠Zero Daemon - No background processes required
- π¦ Minimal Dependencies - Lightweight core with optional extras
- π± Cross-Device Auth - Click on phone, authenticate on desktop
- πΎ Pluggable Storage - Use any database system
- π¦ Modern Defaults - NanoID, JWT, MsgPack by default
Since Haze is now available on PYPI, you can use any package manager you want.
pip install haze-auth[full]
# Basic installation (install jwt later, with different package version)
pip install git+https://github.com/itsmeadarsh2008/haze.git@main
# With JWT support (Recommended)
pip install "haze[jwt] @ git+https://github.com/itsmeadarsh2008/haze.git@main"
# With all optional dependencies
pip install "haze [full] @ git+https://github.com/itsmeadarsh2008/haze.git@main"
You can also specify individual extra dependencies:
# Pick and choose what you need
pip install "haze[<optional deps>] git+https://github.com/itsmeadarsh2008/haze.git"
import haze
import secrets
# Configure Haze
haze.use(
base_url="https://myapp.com",
magic_link_path="/auth/verify",
secret_key=secrets.token_urlsafe(32)
)
# Simple in-memory storage for demo purposes
token_store = {}
# Define storage handler
@haze.storage
def store_token(token_id, data=None):
if data is None:
return token_store.get(token_id)
token_store[token_id] = data
return data
# Generate a magic link for a user
link = haze.generate(
user_id="user123",
metadata={"name": "John Doe", "email": "john@example.com"}
)
print(f"Magic Link: {link}")
# Verify the magic link
# This is typically done in your web endpoint
@app.route("/auth/verify")
def verify_link():
token_id = request.args.get("token_id")
signature = request.args.get("signature")
try:
user_data = haze.verify(token_id, signature)
# Authentication successful
# Set session, JWT, etc.
return {"success": True, "user": user_data}
except Exception as e:
return {"success": False, "error": str(e)}
haze.use(
# Base settings
base_url="https://myapp.com",
magic_link_path="/auth/magic",
link_expiry=3600, # 1 hour
allow_reuse=False, # One-time use by default
# Token settings
token_provider="jwt",
jwt_algorithm="HS256", # or RS256, ES256
# ID generation
id_generator="nanoid", # or "uuid"
nanoid_size=21,
# Format settings
serialization_format="msgpack" # or "json"
)
import secrets
# Generate a secure key
secret_key = secrets.token_urlsafe(32)
# Configure Haze to use JWT with HMAC
haze.use(
token_provider="jwt",
jwt_algorithm="HS256",
secret_key=secret_key
)
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
# Generate key pair
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048
)
public_key = private_key.public_key()
# Configure Haze
haze.use(
token_provider="jwt",
jwt_algorithm="RS256",
private_key=private_key,
public_key=public_key
)
from sqlalchemy import create_engine, Column, String, Integer, Boolean, JSON, Text
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
# Setup database
Base = declarative_base()
engine = create_engine("sqlite:///haze_tokens.db")
Session = sessionmaker(bind=engine)
class Token(Base):
__tablename__ = "tokens"
token_id = Column(String, primary_key=True)
user_id = Column(String, nullable=False)
exp = Column(Integer, nullable=False)
created_at = Column(Integer, nullable=False)
metadata = Column(JSON, nullable=True)
consumed = Column(Boolean, default=False)
Base.metadata.create_all(engine)
# Setup Haze storage handler
@haze.storage
def store_token(token_id, data=None):
session = Session()
try:
if data is None:
# Retrieve token
token = session.query(Token).filter_by(token_id=token_id).first()
if not token:
return None
return {
"user_id": token.user_id,
"exp": token.exp,
"created_at": token.created_at,
"metadata": token.metadata,
"consumed": token.consumed
}
else:
# Create or update token
token = session.query(Token).filter_by(token_id=token_id).first()
if token:
# Update existing token
token.user_id = data["user_id"]
token.exp = data["exp"]
token.created_at = data.get("created_at")
token.metadata = data.get("metadata")
token.consumed = data.get("consumed", False)
else:
# Create new token
token = Token(
token_id=token_id,
user_id=data["user_id"],
exp=data["exp"],
created_at=data.get("created_at"),
metadata=data.get("metadata"),
consumed=data.get("consumed", False)
)
session.add(token)
session.commit()
return data
finally:
session.close()
import redis
import json
import time
# Setup Redis connection
r = redis.Redis(host='localhost', port=6379, db=0)
@haze.storage
def store_token(token_id, data=None):
key = f"haze:token:{token_id}"
if data is None:
# Retrieve token
token_data = r.get(key)
if not token_data:
return None
return json.loads(token_data)
else:
# Store token with expiration
ttl = data["exp"] - int(time.time())
r.setex(key, ttl, json.dumps(data))
return data
Haze provides hooks for various authentication events:
# Called when a link is verified
@haze.verification
def on_verification(user_id, token_data):
print(f"User {user_id} verified with token: {token_data['jti']}")
# Update last login time, etc.
# Called when a magic link is clicked
@haze.onclick
def on_link_clicked(user_id, user_data):
print(f"User {user_id} clicked magic link")
# Track analytics, etc.
from flask import Flask, request, redirect, session
import haze
import secrets
app = Flask(__name__)
app.secret_key = secrets.token_urlsafe(32)
# Configure Haze
haze.use(
base_url="http://localhost:5000", # For local development
magic_link_path="/auth/verify",
secret_key=app.secret_key
)
# Simple in-memory storage for demo purposes
token_store = {}
@haze.storage
def store_token(token_id, data=None):
if data is None:
return token_store.get(token_id)
token_store[token_id] = data
return data
@app.route("/login", methods=["POST"])
def login():
email = request.form.get("email")
if not email:
return {"error": "Email required"}, 400
# Generate magic link
link = haze.generate(
user_id=email,
metadata={"email": email}
)
# In a real app, send this link via email
# For demo, we'll just return it
return {"link": link}
@app.route("/auth/verify")
def verify():
token_id = request.args.get("token_id")
signature = request.args.get("signature")
try:
user_data = haze.verify(token_id, signature)
# Set session
session["user_id"] = user_data["user_id"]
session["authenticated"] = True
# Redirect to dashboard
return redirect("/dashboard")
except Exception as e:
return {"error": str(e)}, 400
@app.route("/dashboard")
def dashboard():
if not session.get("authenticated"):
return redirect("/login")
return f"Welcome, {session.get('user_id')}!"
from fastapi import FastAPI, Depends, HTTPException, Request, Response
from fastapi.responses import RedirectResponse
from pydantic import BaseModel, EmailStr
import secrets
import haze
app = FastAPI()
# Configure Haze
haze.use(
base_url="http://localhost:8000", # For local development
magic_link_path="/auth/verify",
secret_key=secrets.token_urlsafe(32)
)
# Simple in-memory storage
token_store = {}
@haze.storage
def store_token(token_id, data=None):
if data is None:
return token_store.get(token_id)
token_store[token_id] = data
return data
class LoginRequest(BaseModel):
email: EmailStr
@app.post("/login")
async def login(request: LoginRequest):
# Generate magic link
link = haze.generate(
user_id=request.email,
metadata={"email": request.email}
)
# In a real app, send this link via email
return {"link": link}
@app.get("/auth/verify")
async def verify(token_id: str, signature: str, response: Response):
try:
user_data = haze.verify(token_id, signature)
# Set cookie for authentication
response.set_cookie(
key="session_token",
value=user_data["user_id"],
httponly=True,
secure=False, # Set to True in production with HTTPS
samesite="lax"
)
return RedirectResponse(url="/dashboard")
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@app.get("/dashboard")
async def dashboard(request: Request):
session_token = request.cookies.get("session_token")
if not session_token:
return RedirectResponse(url="/login")
return {"message": f"Welcome, {session_token}!"}
- Always use HTTPS for production environments
- Set appropriate token expiry times - shorter is better
- Rotate your secret keys periodically
- Use asymmetric cryptography (JWT with RSA/ECDSA) for increased security
- Implement rate limiting to prevent brute force attacks
- Store tokens securely in a database with proper encryption
- Enable one-time use for magic links by setting
allow_reuse=False
pip install "haze[full] @ git+https://github.com/itsmeadarsh2008/haze.git"
Ensure you've set a secure secret key with haze.use(secret_key=...)
.
The magic link has expired. Generate a new one or increase the link_expiry
setting.
The token doesn't exist in storage. Check your storage handler implementation.
The signature verification failed. This could indicate a tampered link or configuration issues.