TL;DR: Open-source auth that costs $0/year (vs $2,880+ for Auth0), sets up in 2 minutes, gives you 20+ production routes in one line, and includes an industry-first "smart cookie fallback". Both packages work independently or together. I'm using it in production for my own projects.
π Links: HeadlessKit Hub β’ React (NPM) β’ Flask (PyPI) β’ React GitHub β’ Flask GitHub
Table of Contents
- The Problem - Why existing solutions fall short
- Meet HeadlessKit - Three architectures, one system
- Architecture - How it works under the hood
- 2-Minute Setup - Get started instantly
- What You Get - 20+ routes, complete state management
- Smart Cookie Fallback - Industry-first feature
- JWT-Aware Refresh - Intelligent token management
- Comparison Table - vs NextAuth, Clerk, Auth0, Supabase
- Production Use Cases - Real-world examples
- Extensibility - Hooks, custom models, OAuth
- Security - OWASP-compliant, production-ready
- Get Started - Installation instructions
- Roadmap - What's next
The $2,880+/Year Problem
You're building a new app. You need authentication. Your options?
| Solution | Cost/Year | Setup Time | The Catch |
|---|---|---|---|
| Auth0 | $2,880+ | 20 min | Vendor lock-in, expensive (Professional: $240/mo) |
| Clerk | $300+ | 15 min | Vendor lock-in, expensive with add-ons ($100/mo each) |
| NextAuth | Free | 30 min | Manual backend, DIY security |
| Supabase | Free tier | 15 min | Limited, vendor lock-in |
| Build it yourself | Free | 2-3 weeks | JWT rotation, OAuth, MFA, password reset... |
I faced this exact problem for my side projects (PDFCourt.com and ShuffleTurn.com). As an indie developer, I couldn't justify $240-300/month for basic auth. But building from scratch meant weeks of work handling JWT rotation, OAuth, token refresh, password reset flows, email verification...
So I built this library. And now I'm open-sourcing it.
What This Is
- β MIT licensed - Use it however you want
- β Self-hosted - Your data, your server
- β No vendor lock-in - Standard JWT, REST APIs
- β Battle-tested - Running my own production apps
- β Actively maintained - I use this daily, so I keep it updated
What This Isn't
- β Not trying to replace Auth0/Clerk for enterprises
- β Not a VC-backed startup (just an indie dev)
- β Not claiming thousands of users (yet!)
- β Not promising 24/7 support
This is a tool I built for myself and am sharing with the community. If it helps you, awesome!
Meet HeadlessKit Authentication
Two packages. Three ways to use them. Zero vendor lock-in.
π¦ The Packages
Frontend: @headlesskits/react-headless-auth
npm install @headlesskits/react-headless-auth
Backend: flask-headless-auth
pip install flask-headless-auth
ποΈ Three Architectures, One System
1οΈβ£ Full Stack (React + Flask)
Use case: New projects, rapid prototyping, maximum convenience
# Backend: One line
auth = AuthSvc(app)
// Frontend: One component
<AuthProvider config={{ apiBaseUrl: 'http://localhost:5000' }}>
<App />
</AuthProvider>
Result: Complete auth system in 2 minutes. 20+ routes. Zero config.
2οΈβ£ Backend Only (Flask β Any Frontend)
Use case: You have Vue/Angular/Svelte/Mobile app, need a backend
auth = AuthSvc(app) # 20+ REST endpoints ready
Works with:
- Vue, Angular, Svelte, Solid.js
- React Native, Flutter, iOS, Android
- Desktop apps (Electron, Tauri)
- ANY HTTP client
What you get: Production-ready REST API with JWT auth, OAuth, MFA, password reset, email verification. Just point your frontend to the endpoints.
3οΈβ£ Frontend Only (React β Any Backend)
Use case: You have Express/Django/FastAPI backend, need a frontend
<AuthProvider config={{ apiBaseUrl: 'https://your-api.com' }}>
<App />
</AuthProvider>
Works with: Express, Django, FastAPI, .NET, Rails, Go, Rust...
What you need: Implement 5 simple endpoints:
-
POST /api/auth/loginβ{ user, access_token, refresh_token } -
POST /api/auth/signupβ{ user, access_token, refresh_token } -
GET /api/auth/user/@meβ{ user } -
POST /api/auth/logoutβ{ message } -
POST /api/auth/token/refreshβ{ access_token, refresh_token }
Bonus: The React package uses a framework-agnostic core. Import AuthClient directly for Vue, Svelte, or vanilla JS.
Architecture Overview
Understanding how HeadlessKit works under the hood helps you make informed decisions and extend it effectively.
Core Design Principles
1. Framework-Agnostic Core
βββββββββββββββββββββββββββββββββββββββββββ
β React Layer β
β AuthProvider, useAuth, hooks β
ββββββββββββββββ¬βββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β Framework-Agnostic Core β
β AuthClient + TokenStorage β
β (Works with ANY framework) β
ββββββββββββββββ¬βββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββ
β Backend API β
β Flask/Express/Django/FastAPI β
βββββββββββββββββββββββββββββββββββββββββββ
The React components are a thin wrapper around AuthClient, which handles all auth logic. This means:
- Easy to port to Vue, Svelte, Angular
- Can use directly in Node.js or React Native
- Business logic stays framework-independent
Authentication Flow
Login/Signup Flow:
1. User submits credentials
ββ> AuthClient.login(email, password)
ββ> POST /api/auth/login
ββ> Backend validates credentials
ββ> Returns { user, access_token, refresh_token }
ββ> TokenStorage detects cookie support
ββ> β
Cookies work β Store in httpOnly cookies
ββ> β Cookies blocked β Store in localStorage
ββ> Schedule automatic token refresh
ββ> User object stored in React Context
Token Refresh Flow:
1. AuthClient decodes JWT access_token
ββ> Reads expiry time (exp claim)
ββ> Schedules refresh 5 minutes before expiration
ββ> When time arrives:
ββ> POST /api/auth/token/refresh
ββ> Sends refresh_token
ββ> Backend validates & rotates tokens
ββ> Returns new access_token + refresh_token
ββ> Tokens updated silently (user stays logged in)
Key insight: No fixed intervals. The system reads your JWT's actual expiry and refreshes precisely when needed.
Backend Architecture (Flask)
Component Hierarchy:
βββββββββββββββββββββββββββββββββββββββββββ
β AuthSvc β
β (Main entry point - one line setup) β
ββββββββββββββββ¬βββββββββββββββββββββββββββ
β
ββ> UserManager (user CRUD, validation)
ββ> TokenManager (JWT, refresh, blacklist)
ββ> AuthManager (login, signup, password)
ββ> OAuthManager (Google, Microsoft)
ββ> RBACManager (roles, permissions)
Each manager is independent and composable
Why this matters:
- Want just OAuth? Import
OAuthManageronly - Custom user logic? Extend
UserManager - Different database? Swap
UserRepository
All managers follow a single-responsibility design, making the codebase maintainable and extensible.
Token Storage Strategy
Frontend Storage Decision Tree:
// On first login:
1. Try to set a test cookie
2. Try to read it back
3. If successful β use httpOnly cookies (secure)
4. If blocked β use localStorage (compatible)
5. Remember choice for session
Backend Token Management:
# Three-tier token system:
1. Access Token (short-lived, 15 min)
ββ> Used for API requests
2. Refresh Token (long-lived, 30 days)
ββ> Used to get new access tokens
3. Token Blacklist (Redis/DB)
ββ> Invalidated tokens on logout
This prevents the common problem where users with cookie blockers can't use your app, while maintaining maximum security for those who allow cookies.
State Management (React)
Context Architecture:
<AuthProvider> // Top-level wrapper
ββ> AuthContext // React Context
ββ> AuthClient // Core logic
ββ> TokenStorage // Storage abstraction
ββ> State:
ββ> user (object | null)
ββ> isAuthenticated (boolean)
ββ> isLoading (boolean)
ββ> error (object | null)
Why Context + Core Client?
- Context handles React-specific state updates
- AuthClient handles framework-agnostic logic
- Easy to test each layer independently
- Can use AuthClient without React
Database Schema (Flask)
Minimal required schema:
users
ββ id (Primary Key)
ββ email (Unique, Indexed)
ββ password_hash
ββ email_confirmed (Boolean)
ββ mfa_enabled (Boolean)
ββ mfa_secret
ββ created_at
ββ updated_at
token_blacklist
ββ jti (JWT ID)
ββ expires_at
Extensible: Add any fields you need. The library validates that required fields exist at startup.
Security Layers
Defense in Depth:
1. Transport Layer
ββ> HTTPS, Secure cookies, SameSite attribute
2. Storage Layer
ββ> httpOnly cookies (XSS-proof) or encrypted localStorage
3. Token Layer
ββ> JWT signing, expiry, rotation, blacklisting
4. Application Layer
ββ> bcrypt hashing, rate limiting, input validation
5. Database Layer
ββ> Parameterized queries (SQLAlchemy ORM)
No single point of failure. Multiple security mechanisms protect your auth system.
Extensibility Philosophy
The architecture is designed around progressive enhancement:
- Start simple - Default configuration works out of the box
- Extend when needed - Add hooks, custom models, or swap components
- Never locked in - Standard REST APIs and JWT tokens mean you can migrate away anytime
Every component has extension points: lifecycle hooks in the frontend, custom models in the backend, and swappable services (email, storage, database) throughout. The system uses composition over inheritance, making it easy to replace individual pieces without touching others.
See the Extensibility section below for detailed code examples and configuration options.
2-Minute Full Stack Setup
Let's see the full-stack approach in action.
Backend (10 lines = 20+ routes)
from flask import Flask
from flask_headless_auth import AuthSvc
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['JWT_SECRET_KEY'] = 'jwt-secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
# THIS IS THE MAGIC LINE πͺ
auth = AuthSvc(app)
if __name__ == '__main__':
app.run()
Frontend (Complete auth in 3 lines)
import { AuthProvider, useAuth } from '@headlesskits/react-headless-auth';
// 1. Wrap your app
<AuthProvider config={{ apiBaseUrl: 'http://localhost:5000' }}>
<App />
</AuthProvider>
// 2. Use anywhere in your components
function MyComponent() {
const { user, login, logout, isAuthenticated } = useAuth();
return (
<div>
{isAuthenticated ? (
<>
<h1>Welcome {user.email}!</h1>
<button onClick={logout}>Logout</button>
</>
) : (
<button onClick={() => login(email, password)}>Login</button>
)}
</div>
);
}
That's it! Full authentication in less than 50 lines total.
β‘ What You Get Instantly
Backend: 20+ Production-Ready Routes
One line of code (auth = AuthSvc(app)) gives you:
Core Authentication:
- β
POST /api/auth/signup- User registration with validation - β
POST /api/auth/login- JWT authentication - β
POST /api/auth/logout- Token blacklisting - β
GET /api/auth/user/@me- Get current user - β
POST /api/auth/token/refresh- Automatic token refresh
OAuth Providers:
- β
GET /api/auth/login/google- Google OAuth - β
GET /api/auth/login/microsoft- Microsoft OAuth - β
GET /api/auth/callback/google- OAuth callback handling - β
GET /api/auth/callback/microsoft- OAuth callback handling
Password Management:
- β
POST /api/auth/password/update- Change password - β
POST /api/auth/request-password-reset- Initiate reset flow - β
POST /api/auth/reset-password/<token>- Complete password reset - β
POST /api/auth/validate-reset-token- Verify reset token
Email & Verification:
- β
GET /api/auth/confirm/<token>- Email verification - β
POST /api/auth/resend-confirmation- Resend verification email
Multi-Factor Authentication:
- β
POST /api/auth/mfa/enable- Enable 2FA - β
POST /api/auth/mfa/verify- Verify 2FA code - β
POST /api/auth/mfa/disable- Disable 2FA
User Management:
- β
GET /api/auth/users- List users (admin) - β
DELETE /api/auth/user/<id>- Delete user (admin) - β
PUT /api/auth/user/<id>- Update user
π See full API documentation
Frontend: Complete Auth State Management
const {
// User data
user, // Current user object
isAuthenticated, // Boolean auth state
isLoading, // Loading state
// Actions
login, // (email, password) => Promise
signup, // (email, password) => Promise
logout, // () => Promise
updatePassword, // (old, new) => Promise
// Token management
refreshToken, // Manual refresh (auto-handled)
// Error handling
error // Last error object
} = useAuth();
Additional hooks:
// Just the user data
const user = useUser();
// Just the session state
const { isAuthenticated, isLoading } = useSession();
Security: Production-Ready, Zero Config
All of this comes built-in, no configuration needed:
- β httpOnly cookies - XSS-proof token storage
- β bcrypt hashing - Password hashing (cost factor 12)
- β JWT rotation - Automatic token refresh & rotation
- β Token blacklisting - Invalidate tokens on logout
- β CSRF protection - SameSite cookies
- β Rate limiting - Prevent brute force attacks
- β Input validation - SQL injection & XSS prevention
- β Secure password reset - Time-limited tokens
Secure by default. No security expertise required.
π Innovation #1: Smart Cookie Fallback
Here's a feature I'm particularly proud of.
The Problem Other Libraries Have
Most auth libraries force you to choose:
- Cookies (most secure, httpOnly, XSS-proof) OR
- localStorage (works when cookies blocked, but vulnerable to XSS)
Result: 1-2% of users with strict privacy settings get "Please enable cookies" errors and can't use your app.
My Solution: Intelligent Fallback
HeadlessKit uses BOTH automatically:
βββββββββββββββββββββββββββββββββββββββββββ
β User logs in β
ββββββββββββββββ¬βββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββ
β Test cookie support β
β (automatic, silent) β
ββββββββββββ¬ββββββββββββ
β
βββββββββ΄βββββββββ
β β
β
YES β NO
β β
βΌ βΌ
ββββββββββββββββ ββββββββββββββββββ
β httpOnly β β localStorage β
β Cookies β β Fallback β
β β β β
β 99% of users β β 1% of users β
β XSS-proof β
β β Still works β οΈ β
ββββββββββββββββ ββββββββββββββββββ
The Result
- β 99% of users get maximum security (httpOnly cookies)
- β 1% with blocked cookies still have a working app (localStorage)
- β Zero configuration needed
- β Automatic detection - happens silently on first login
- β No error screens - no "please enable cookies" messages
- β Graceful degradation - best security available for each user
Why This Matters
Most libraries either:
- Use cookies only (breaks for 1-2% of users with strict privacy settings)
- Use localStorage only (less secure for everyone)
- Make you choose (adds complexity)
This approach: Best security for each user, automatically.
π§ Innovation #2: JWT-Aware Token Refresh
Most libraries refresh tokens on a fixed interval (e.g., every 50 minutes). This is inefficient and can cause issues.
The Smart Approach
HeadlessKit decodes your JWT, reads the actual expiry time, and schedules refresh exactly when needed:
// Inside AuthClient.ts
private scheduleTokenRefresh(token: string): void {
const payload = this.decodeJWT(token);
if (payload?.exp) {
// JWT has expiry - refresh 5 min before expiration
const expiryTime = payload.exp * 1000;
const refreshTime = expiryTime - (5 * 60 * 1000);
const delay = Math.max(0, refreshTime - Date.now());
this.refreshTimeoutId = setTimeout(async () => {
await this.refreshToken();
}, delay);
}
}
Benefits
- β Precise timing - refreshes exactly when needed
- β No wasted requests - doesn't refresh too early
- β Seamless UX - users stay logged in without interruption
- β Respects your backend - uses the expiry time YOU set
- β
Works with any JWT - reads standard
expclaim
π The Comparison
| Feature | HeadlessKit | NextAuth | Clerk | Auth0 | Supabase |
|---|---|---|---|---|---|
| Setup Time | β‘ 2 minutes | 30 min | 15 min | 20 min | 15 min |
| Monthly Cost | β $0 | Free | $25-325+ | $240+ | Free tier limited |
| Annual Cost | β $0 | Free | $300-3,900+ | $2,880+ | Scales with usage |
| Smart Cookie Fallback | β Yes | β | β | β | β |
| Works Independently | β Mix & Match | β οΈ Backend DIY | β | β | β οΈ Complex |
| One-Line Backend | β AuthSvc(app) | β DIY | β | β | β |
| Custom User Models | β With validation | β | β | β | β οΈ Limited |
| Self-Hosted | β Full control | β | β | β | β οΈ Complex |
| Bundle Size | β 15KB | β Heavy | β Small | β Small | β οΈ Medium |
| JWT-Aware Refresh | β Smart | β οΈ Manual | β | β | β |
| Vendor Lock-in | β None | β None | β High | β High | β οΈ Medium |
| OAuth Providers | β Google, MS | β Many | β Many | β Many | β Many |
| MFA/2FA | β Built-in | β οΈ Manual | β | β | β |
| Email Verification | β Built-in | β οΈ Manual | β | β | β |
| RBAC | β Built-in | β οΈ Manual | β | β | β |
πΌ How I'm Using This in Production
I built this library because I needed it for my own projects. Here's how I'm using it:
PDFCourt.com - Legal Document Processing
- Custom user model with subscription tiers
- Quota tracking per user
- Self-hosted for data compliance
class User(db.Model, UserMixin):
subscription_tier = db.Column(db.String(50), default='free')
documents_processed = db.Column(db.Integer, default=0)
monthly_limit = db.Column(db.Integer, default=10)
auth = AuthSvc(app, user_model=User)
@app.route('/api/auth/check-quota')
@jwt_required()
def check_quota():
user = User.query.get(get_jwt_identity())
remaining = user.monthly_limit - user.documents_processed
return {'remaining': remaining, 'can_process': remaining > 0}
ShuffleTurn.com - Gaming Platform
- OAuth integration (Google, Microsoft)
- Analytics integration via lifecycle hooks
- Custom user fields for gaming stats
<AuthProvider
config={{ apiBaseUrl: 'https://api.shuffleturn.com' }}
hooks={{
afterLogin: ({ user }) => {
posthog.identify(user.id, {
username: user.username,
level: user.level
});
},
transformUser: ({ user }) => ({
...user,
rankTitle: getRankTitle(user.level),
isPro: user.level >= 50
})
}}
>
<App />
</AuthProvider>
Why I'm confident sharing this: I'm using this exact code in production. If it works for my projects, it can work for yours.
π¨ When You Need More: Extensibility
Start simple. Extend when needed. Here's how:
1. Lifecycle Hooks (Frontend)
Inject custom logic at any point in the auth flow:
<AuthProvider
config={{ apiBaseUrl: 'http://localhost:5000' }}
hooks={{
// After successful login
afterLogin: ({ user }) => {
analytics.identify(user.id);
posthog.track('User Logged In');
},
// On login error
onLoginError: ({ error }) => {
Sentry.captureException(error);
toast.error(error.message);
},
// After successful signup
afterSignup: ({ user }) => {
analytics.track('User Signed Up');
// Trigger onboarding flow
},
// Transform user data
transformUser: ({ user }) => ({
...user,
fullName: `${user.first_name} ${user.last_name}`,
isAdmin: user.roles?.includes('admin')
}),
// After token refresh
afterTokenRefresh: ({ access_token }) => {
console.log('Token refreshed silently');
},
// After logout
afterLogout: () => {
analytics.reset();
// Clear app state
},
// Global auth error handler
onAuthError: ({ error }) => {
if (error.status === 401) {
// Redirect to login
}
}
}}
>
<App />
</AuthProvider>
Available hooks:
-
afterLogin- Post-login logic -
afterSignup- Post-signup logic -
afterLogout- Post-logout cleanup -
onLoginError- Login error handling -
onSignupError- Signup error handling -
afterTokenRefresh- Token refresh callback -
transformUser- Transform user data -
onAuthError- Global error handler
Perfect for:
- Analytics (PostHog, Mixpanel, Amplitude)
- Error tracking (Sentry, LogRocket)
- Onboarding flows
- Data transformation
- Custom redirects
2. Custom User Models (Backend)
Add any fields you need to the user model:
from flask_headless_auth import AuthSvc, UserMixin
class User(db.Model, UserMixin):
__tablename__ = 'users'
# Add ANY custom fields
company = db.Column(db.String(200))
subscription_tier = db.Column(db.String(50), default='free')
monthly_quota = db.Column(db.Integer, default=100)
username = db.Column(db.String(50), unique=True)
level = db.Column(db.Integer, default=1)
avatar_url = db.Column(db.String(500))
bio = db.Column(db.Text)
preferences = db.Column(db.JSON)
auth = AuthSvc(app, user_model=User)
Bonus: Model Validation
We validate your model at startup. Missing required fields? You get a clear error with the exact fix:
β USER MODEL VALIDATION FAILED
Missing required field: mfa_enabled
Type: Boolean
Default: False
SQL Fix:
ALTER TABLE users ADD COLUMN mfa_enabled BOOLEAN DEFAULT FALSE;
No cryptic runtime errors in production!
3. Custom JWT Claims
Add custom data to JWT tokens:
@auth.additional_claims_loader
def add_claims_to_jwt(identity):
user = User.query.get(identity)
return {
'role': user.role,
'subscription': user.subscription_tier,
'company_id': user.company_id,
'permissions': user.get_permissions()
}
Access in protected routes:
from flask_jwt_extended import get_jwt
@app.route('/api/admin/users')
@jwt_required()
def admin_users():
claims = get_jwt()
if claims.get('role') != 'admin':
return {'error': 'Unauthorized'}, 403
# Admin logic here
4. Custom Routes & Endpoints
Add your own protected endpoints:
from flask_jwt_extended import jwt_required, get_jwt_identity
auth = AuthSvc(app)
@app.route('/api/auth/check-quota')
@jwt_required()
def check_quota():
user_id = get_jwt_identity()
user = User.query.get(user_id)
if user.subscription_tier == 'free' and user.usage > 100:
return {'error': 'Upgrade required'}, 403
return {'remaining': user.monthly_quota - user.usage}
@app.route('/api/auth/upgrade')
@jwt_required()
def upgrade_subscription():
user_id = get_jwt_identity()
user = User.query.get(user_id)
user.subscription_tier = 'pro'
user.monthly_quota = 1000
db.session.commit()
return {'message': 'Upgraded successfully'}
5. Framework-Agnostic Core (No React Required)
Use the core AuthClient with any framework:
import { AuthClient, TokenStorage } from '@headlesskits/react-headless-auth/core';
// Initialize
const storage = new TokenStorage('cookie-first');
const authClient = new AuthClient(
{ apiBaseUrl: 'https://api.example.com' },
storage
);
// Use anywhere
await authClient.login(email, password);
const user = await authClient.getUser();
await authClient.logout();
Works in:
- β Vue 3, Svelte, Angular, Solid.js
- β React Native (with AsyncStorage adapter)
- β Electron (with secure storage)
- β Vanilla JavaScript
- β Node.js (server-side)
Example: Vue 3 Composition API
import { ref, onMounted } from 'vue';
import { AuthClient, TokenStorage } from '@headlesskits/react-headless-auth/core';
export function useAuth() {
const user = ref(null);
const isAuthenticated = ref(false);
const storage = new TokenStorage('cookie-first');
const authClient = new AuthClient({ apiBaseUrl: '...' }, storage);
const login = async (email: string, password: string) => {
const result = await authClient.login(email, password);
user.value = result.user;
isAuthenticated.value = true;
};
onMounted(async () => {
try {
user.value = await authClient.getUser();
isAuthenticated.value = true;
} catch {
isAuthenticated.value = false;
}
});
return { user, isAuthenticated, login };
}
6. Email Service Configuration
Built-in support for Gmail and Brevo (SendInBlue):
# Gmail
app.config['EMAIL_SERVICE'] = 'gmail'
app.config['GMAIL_ADDRESS'] = 'your-email@gmail.com'
app.config['GMAIL_APP_PASSWORD'] = 'your-app-password'
# OR Brevo
app.config['EMAIL_SERVICE'] = 'brevo'
app.config['BREVO_API_KEY'] = 'your-api-key'
app.config['BREVO_SENDER_EMAIL'] = 'noreply@yourdomain.com'
app.config['BREVO_SENDER_NAME'] = 'Your App'
auth = AuthSvc(app)
Or bring your own email service:
from flask_headless_auth.interfaces import EmailServiceInterface
class CustomEmailService(EmailServiceInterface):
def send_email(self, to: str, subject: str, body: str):
# Your email logic here
pass
auth = AuthSvc(app, email_service=CustomEmailService())
7. OAuth Provider Configuration
Add Google and Microsoft OAuth:
# Google OAuth
app.config['GOOGLE_CLIENT_ID'] = 'your-google-client-id'
app.config['GOOGLE_CLIENT_SECRET'] = 'your-google-client-secret'
app.config['GOOGLE_REDIRECT_URI'] = 'http://localhost:5000/api/auth/callback/google'
# Microsoft OAuth
app.config['MICROSOFT_CLIENT_ID'] = 'your-microsoft-client-id'
app.config['MICROSOFT_CLIENT_SECRET'] = 'your-microsoft-client-secret'
app.config['MICROSOFT_REDIRECT_URI'] = 'http://localhost:5000/api/auth/callback/microsoft'
auth = AuthSvc(app)
Frontend usage:
function LoginPage() {
const handleGoogleLogin = () => {
window.location.href = 'http://localhost:5000/api/auth/login/google';
};
return (
<button onClick={handleGoogleLogin}>
Sign in with Google
</button>
);
}
π Security Deep Dive
What's Included Out of the Box
Password Security:
- bcrypt hashing with cost factor 12
- Minimum password requirements (8+ chars, uppercase, lowercase, number)
- Password strength validation
- Secure password reset with time-limited tokens
Token Security:
- JWT with RS256 or HS256 signing
- Automatic token rotation
- Token blacklisting on logout
- Refresh token rotation
- httpOnly cookies (XSS-proof)
- SameSite cookie attribute (CSRF protection)
API Security:
- Rate limiting on auth endpoints (10 requests/minute)
- Input validation and sanitization
- SQL injection prevention (SQLAlchemy ORM)
- CORS configuration support
- Request size limits
Session Security:
- Automatic session timeout
- Concurrent session management
- Device tracking (optional)
- IP-based validation (optional)
Security Best Practices
The library follows OWASP guidelines:
- β A01: Broken Access Control - JWT-based auth with role validation
- β A02: Cryptographic Failures - bcrypt, secure token generation
- β A03: Injection - Parameterized queries, input validation
- β A04: Insecure Design - Secure by default configuration
- β A05: Security Misconfiguration - Sensible defaults
- β A07: Identification and Authentication Failures - MFA support, secure password reset
π Get Started in 2 Minutes
Option 1: Full Stack (React + Flask)
Step 1: Install packages
# Backend
pip install flask-headless-auth
# Frontend
npm install @headlesskits/react-headless-auth
Step 2: Backend setup
from flask import Flask
from flask_headless_auth import AuthSvc
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['JWT_SECRET_KEY'] = 'jwt-secret-key'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///app.db'
auth = AuthSvc(app)
if __name__ == '__main__':
app.run()
Step 3: Frontend setup
import { AuthProvider, useAuth } from '@headlesskits/react-headless-auth';
function App() {
return (
<AuthProvider config={{ apiBaseUrl: 'http://localhost:5000' }}>
<YourApp />
</AuthProvider>
);
}
Done! You have full authentication.
Option 2: Backend Only
pip install flask-headless-auth
from flask import Flask
from flask_headless_auth import AuthSvc
app = Flask(__name__)
# ... config ...
auth = AuthSvc(app) # 20+ REST endpoints ready
Point your frontend (Vue, Angular, mobile app) to the API endpoints.
Option 3: Frontend Only
npm install @headlesskits/react-headless-auth
<AuthProvider config={{ apiBaseUrl: 'https://your-backend.com' }}>
<App />
</AuthProvider>
Implement 5 endpoints on your backend (Express, Django, etc.).
π Documentation & Resources
- π HeadlessKit Hub - Start here! All packages in one place
- π React Package (GitHub)
- π Flask Package (GitHub)
- π¦ React (NPM)
- π Flask (PyPI)
- π¬ Discussions
- π Report Issues
- π§ Email Me
πΊοΈ Roadmap
Coming soon:
- Vue.js & Svelte SDKs
- Magic links (passwordless auth)
- WebAuthn/Passkeys support
- Express.js & FastAPI backends
- React Native & Flutter SDKs
- GitHub & Apple OAuth
- Admin dashboard UI
Want to contribute? Check out CONTRIBUTING.md
π Join the Community
If this helps you, please:
- β Star the repos on GitHub
- π Report issues or suggest features
- π¬ Join Discussions
- π Share on Twitter/X, LinkedIn, or Reddit
@headlesskits/react-headless-auth
π Production-ready React authentication in 2 minutes. Smart cookie fallback, automatic token refresh, zero dependencies. The simplest way to add enterprise-grade auth to your React app.
npm install @headlesskits/react-headless-auth
π‘ Why Choose This?
The Problem: Authentication is hard. Auth0 costs $300/month. Building it yourself takes weeks. Most libraries force you to choose between security (cookies) OR compatibility (localStorage).
Our Solution: Best of both worlds. Maximum security for 99% of users (httpOnly cookies), automatic fallback for the 1% with blocked cookies (localStorage). Plus a complete backend SDK so you don't spend weeks building auth routes.
| Feature | react-headless-auth | NextAuth | Clerk | Auth0 | Supabase Auth |
|---|---|---|---|---|---|
| Setup Time | β‘ 2 minutes | 30 min | 15 min | 20 min | 15 min |
| Monthly Cost | β $0 | Free | $300 | $240 | Free tier limited |
| Smart Cookie Fallback | β Industry First | β | β | β | β |
| Zero Dependencies | β (~15KB) | β (heavy) | β | β |
|
| Backend Included | β flask-headless-auth |
|
Flask-Headless-Auth
π Production-ready Flask authentication in one line. Get 20+ auth routes instantly. JWT, OAuth, MFA, RBAC built-in. Works with React, Next.js, Vue, any frontend. The free, self-hosted alternative to Auth0/Clerk ($3,600/year saved).
π‘ What You Get
In one line of code (AuthSvc(app)), you get a complete authentication system that would take weeks to build:
auth = AuthSvc(app) # That's it! π
Instantly Available:
- β 20+ Production Routes - Login, signup, OAuth, password reset, MFA, profile management
- β JWT + httpOnly Cookies - Maximum security with automatic fallback
- β OAuth Ready - Google & Microsoft sign-in (GitHub, Apple coming soon)
- β MFA/2FA - Multi-factor authentication built-in
- β RBAC - Role-based access control
- β Email Services - Verification & password reset emails
- β Rate Limiting - Brute force protection
- β Token Blacklisting - Secure logout
- β Security Headers - CSRF, XSS, CORS protection
- β Custom Userβ¦
Your support helps other developers discover this project!
π― Final Thoughts
Authentication doesn't have to be hard, expensive, or lock you into a vendor. With HeadlessKit, you get:
- β Production-ready auth in 2 minutes
- β Smart cookie fallback for maximum compatibility
- β 20+ routes from one line of code
- β Complete control over your data
- β Zero recurring costs
Built by an indie dev for indie devs. No venture capital. No pricing tiers. No vendor lock-in. Just great open-source software.
Questions? Comments? Want to contribute?
Drop a comment below or reach out on GitHub!
Tags: #react #authentication #opensource #flask #jwt #oauth #webdev #javascript #python #nextjs #security #selfhosted #indiedev #auth0alternative #clerkalt
Top comments (0)