3 Commits

Author SHA1 Message Date
Dan Milne
5137a25626 Add remainging rate limits. Add docker compose production example. Update beta-checklist.
Some checks failed
CI / scan_ruby (push) Has been cancelled
CI / scan_js (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / system-test (push) Has been cancelled
2026-01-02 12:14:13 +11:00
Dan Milne
fed7c3cedb Some beta-checklist updates 2026-01-02 11:53:41 +11:00
Dan Milne
e288fcad7c Remove old docs 2026-01-01 21:04:26 +11:00
9 changed files with 122 additions and 2108 deletions

148
README.md
View File

@@ -306,21 +306,100 @@ bin/dev
## Production Deployment
### Docker
### Docker Compose (Recommended)
Create a `docker-compose.yml` file:
```yaml
services:
clinch:
image: ghcr.io/dkam/clinch:latest
ports:
- "127.0.0.1:3000:3000" # Bind to localhost only (reverse proxy on same host)
# Use "3000:3000" if reverse proxy is in Docker network or different host
environment:
# Rails Configuration
RAILS_ENV: production
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
# Application Configuration
CLINCH_HOST: ${CLINCH_HOST}
CLINCH_FROM_EMAIL: ${CLINCH_FROM_EMAIL:-noreply@example.com}
# SMTP Configuration
SMTP_ADDRESS: ${SMTP_ADDRESS}
SMTP_PORT: ${SMTP_PORT}
SMTP_DOMAIN: ${SMTP_DOMAIN}
SMTP_USERNAME: ${SMTP_USERNAME}
SMTP_PASSWORD: ${SMTP_PASSWORD}
SMTP_AUTHENTICATION: ${SMTP_AUTHENTICATION:-plain}
SMTP_ENABLE_STARTTLS: ${SMTP_ENABLE_STARTTLS:-true}
# OIDC Configuration (optional - generates temporary key if not provided)
OIDC_PRIVATE_KEY: ${OIDC_PRIVATE_KEY}
# Optional Configuration
FORCE_SSL: ${FORCE_SSL:-false}
volumes:
- ./storage:/rails/storage
restart: unless-stopped
```
Create a `.env` file in the same directory:
```bash
# Build image
docker build -t clinch .
# Generate with: openssl rand -hex 64
SECRET_KEY_BASE=your-secret-key-here
# Run container
docker run -p 3000:3000 \
-v clinch-storage:/rails/storage \
-e SECRET_KEY_BASE=your-secret-key \
-e SMTP_ADDRESS=smtp.example.com \
-e SMTP_PORT=587 \
-e SMTP_USERNAME=your-username \
-e SMTP_PASSWORD=your-password \
clinch
# Application URLs
CLINCH_HOST=https://auth.yourdomain.com
CLINCH_FROM_EMAIL=noreply@yourdomain.com
# SMTP Settings
SMTP_ADDRESS=smtp.example.com
SMTP_PORT=587
SMTP_DOMAIN=yourdomain.com
SMTP_USERNAME=your-smtp-username
SMTP_PASSWORD=your-smtp-password
# OIDC (optional - generates temporary key if not set)
# Generate with: openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
# Then: OIDC_PRIVATE_KEY=$(cat private_key.pem)
OIDC_PRIVATE_KEY=
# Optional: Force SSL redirects (if not behind a reverse proxy handling SSL)
FORCE_SSL=false
```
Start Clinch:
```bash
docker compose up -d
```
**First Run:**
1. Visit `http://localhost:3000` (or your configured domain)
2. Complete the first-run wizard to create your admin account
3. Configure applications and invite users
**Upgrading:**
```bash
# Pull latest image
docker compose pull
# Restart with new image (migrations run automatically)
docker compose up -d
```
**Logs:**
```bash
# View logs
docker compose logs -f clinch
# View last 100 lines
docker compose logs --tail=100 clinch
```
### Backup & Restore
@@ -336,9 +415,6 @@ Use SQLite's `VACUUM INTO` command for safe, atomic backups of a running databas
```bash
# Local development
sqlite3 storage/production.sqlite3 "VACUUM INTO 'backup.sqlite3';"
# Docker
docker exec clinch sqlite3 /rails/storage/production.sqlite3 "VACUUM INTO '/rails/storage/backup.sqlite3';"
```
This creates an optimized copy of the database that's safe to make even while Clinch is running.
@@ -354,9 +430,9 @@ sqlite3 storage/production.sqlite3 "VACUUM INTO 'backup-$(date +%Y%m%d).sqlite3'
# 2. Backup uploaded files (ActiveStorage files are immutable)
tar -czf uploads-backup-$(date +%Y%m%d).tar.gz storage/uploads/
# Docker equivalent
docker exec clinch sqlite3 /rails/storage/production.sqlite3 "VACUUM INTO '/rails/storage/backup-$(date +%Y%m%d).sqlite3';"
docker exec clinch tar -czf /rails/storage/uploads-backup-$(date +%Y%m%d).tar.gz /rails/storage/uploads/
# Docker Compose equivalent
docker compose exec clinch sqlite3 /rails/storage/production.sqlite3 "VACUUM INTO '/rails/storage/backup-$(date +%Y%m%d).sqlite3';"
docker compose exec clinch tar -czf /rails/storage/uploads-backup-$(date +%Y%m%d).tar.gz /rails/storage/uploads/
```
**Restore:**
@@ -383,13 +459,13 @@ sqlite3 /host/path/production.sqlite3 "VACUUM INTO '/host/path/backup-$(date +%Y
rsync -av /host/path/backup-*.sqlite3 /host/path/uploads/ remote:/backups/clinch/
```
b) **Docker volumes** (e.g., `-v clinch_storage:/rails/storage`):
b) **Docker volumes** (e.g., using named volumes in compose):
```bash
# Database backup (safe while running)
docker exec clinch sqlite3 /rails/storage/production.sqlite3 "VACUUM INTO '/rails/storage/backup.sqlite3';"
docker compose exec clinch sqlite3 /rails/storage/production.sqlite3 "VACUUM INTO '/rails/storage/backup.sqlite3';"
# Copy out of container
docker cp clinch:/rails/storage/backup.sqlite3 ./backup-$(date +%Y%m%d).sqlite3
docker compose cp clinch:/rails/storage/backup.sqlite3 ./backup-$(date +%Y%m%d).sqlite3
```
**Option 2: While Stopped (Offline Backup)**
@@ -414,35 +490,7 @@ docker compose up -d
## Configuration
### Environment Variables
Create a `.env` file (see `.env.example`):
```bash
# Rails
SECRET_KEY_BASE=generate-with-bin-rails-secret
RAILS_ENV=production
# Database
# SQLite database stored in storage/ directory (Docker volume mount point)
# SMTP (for sending emails)
SMTP_ADDRESS=smtp.example.com
SMTP_PORT=587
SMTP_DOMAIN=example.com
SMTP_USERNAME=your-username
SMTP_PASSWORD=your-password
SMTP_AUTHENTICATION=plain
SMTP_ENABLE_STARTTLS=true
# Application
CLINCH_HOST=https://auth.example.com
CLINCH_FROM_EMAIL=noreply@example.com
# OIDC (optional - generates temporary key in development)
# Generate with: openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
OIDC_PRIVATE_KEY=<contents-of-private-key.pem>
```
All configuration is handled via environment variables (see the `.env` file in the Docker Compose section above).
### First Run
1. Visit Clinch at `http://localhost:3000` (or your configured domain)

View File

@@ -3,6 +3,7 @@ class InvitationsController < ApplicationController
allow_unauthenticated_access
before_action :set_user_by_invitation_token, only: %i[show update]
rate_limit to: 10, within: 10.minutes, only: :update, with: -> { redirect_to signin_path, alert: "Too many attempts. Try again later." }
def show
# Show the password setup form

View File

@@ -2,6 +2,7 @@ class PasswordsController < ApplicationController
allow_unauthenticated_access
before_action :set_user_by_token, only: %i[edit update]
rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_password_path, alert: "Try again later." }
rate_limit to: 10, within: 10.minutes, only: :update, with: -> { redirect_to new_password_path, alert: "Too many attempts. Try again later." }
def new
end

View File

@@ -4,5 +4,5 @@
# Use this to limit dissemination of sensitive information.
# See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors.
Rails.application.config.filter_parameters += [
:passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc
:passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :cvv, :cvc, :backup
]

View File

@@ -1,275 +0,0 @@
# Rodauth-OAuth Analysis Documents
This directory contains a comprehensive analysis of rodauth-oauth and how it compares to your custom OIDC implementation in Clinch.
## Start Here
### 1. **RODAUTH_DECISION_GUIDE.md** (15-minute read)
**Purpose:** Help you make a decision about your OAuth/OIDC implementation
**Contains:**
- TL;DR of three options
- Decision flowchart
- Feature roadmap scenarios
- Effort estimates for each path
- Security comparison
- Real-world questions to ask your team
- Next actions for each option
**Best for:** Deciding whether to keep your implementation, migrate, or use a hybrid approach
---
### 2. **rodauth-oauth-quick-reference.md** (20-minute read)
**Purpose:** Quick lookup guide and architecture overview
**Contains:**
- What Rodauth-OAuth is (concise)
- Key statistics and certifications
- Feature advantages & disadvantages
- Architecture diagrams (text-based)
- Database schema comparison
- Feature matrix with implementation effort
- Performance considerations
- Getting started guide
- Code examples (minimal setup)
**Best for:** Understanding what you're looking at, quick decision support
---
### 3. **rodauth-oauth-analysis.md** (45-minute deep-dive)
**Purpose:** Comprehensive technical analysis for decision-making
**Contains:**
- Complete architecture breakdown (12 sections)
- All 34 features detailed and explained
- Full database schema documentation
- Request flow diagrams
- Feature dependency graphs
- Integration paths with Rails
- Security analysis
- Migration procedures
- Code comparisons
- Performance metrics
**Best for:** Deep understanding before making technical decisions, planning migrations
---
## How to Use These Documents
### Scenario 1: "I have 15 minutes"
1. Read: RODAUTH_DECISION_GUIDE.md (sections: TL;DR + Decision Matrix)
2. Go to: Next Actions for your chosen option
3. Done: You have a direction
### Scenario 2: "I have 45 minutes"
1. Read: RODAUTH_DECISION_GUIDE.md (complete)
2. Skim: rodauth-oauth-quick-reference.md (focus on code examples)
3. Decide: Which path interests you most
4. Plan: Team discussion using decision matrix
### Scenario 3: "I'm doing technical deep-dive"
1. Read: RODAUTH_DECISION_GUIDE.md (complete)
2. Read: rodauth-oauth-quick-reference.md (complete)
3. Read: rodauth-oauth-analysis.md (sections 1-6)
4. Reference: rodauth-oauth-analysis.md (sections 7-12 as needed)
### Scenario 4: "I'm planning a migration"
1. Read: RODAUTH_DECISION_GUIDE.md (effort estimates section)
2. Read: rodauth-oauth-analysis.md (migration path section)
3. Reference: rodauth-oauth-analysis.md (database schema section)
4. Plan: Detailed migration steps
---
## Three Options Explained (Very Brief)
### Option A: Keep Your Implementation
- **Time:** Ongoing (add features incrementally)
- **Effort:** 4-6 months to reach feature parity
- **Maintenance:** 8-10 hours/month
- **Best if:** Auth Code + PKCE is sufficient forever
### Option B: Switch to Rodauth-OAuth
- **Time:** 5-9 weeks (one-time migration)
- **Learning:** 1-2 weeks (Roda framework)
- **Maintenance:** 1-2 hours/month
- **Best if:** Need enterprise features, want low maintenance
### Option C: Hybrid Approach (Microservices)
- **Time:** 3-5 weeks (independent setup)
- **Learning:** Low (Roda is isolated)
- **Maintenance:** 2-3 hours/month
- **Best if:** Want Option B benefits without full Rails→Roda migration
---
## Key Findings
**What Rodauth-OAuth Provides That You Don't Have:**
- Refresh tokens
- Token revocation (RFC 7009)
- Token introspection (RFC 7662)
- Client Credentials grant (machine-to-machine)
- Device Code flow (IoT/smart TV)
- JWT Access Tokens (stateless)
- Session Management
- Front & Back-Channel Logout
- Token hashing (bcrypt security)
- DPoP support (token binding)
- TLS mutual authentication
- Dynamic Client Registration
- 20+ more optional features
**Security Differences:**
- Your impl: Tokens stored in plaintext (DB breach = token theft)
- Rodauth: Tokens hashed with bcrypt (secure even if DB breached)
**Maintenance Burden:**
- Your impl: YOU maintain everything
- Rodauth: Community maintains, you maintain config only
---
## Document Structure
### RODAUTH_DECISION_GUIDE.md Sections:
```
1. TL;DR - Three options
2. Decision Matrix - Flowchart
3. Feature Roadmap Comparison
4. Architecture Diagrams (visual)
5. Effort Estimates
6. Real-World Questions
7. Security Comparison
8. Cost-Benefit Summary
9. Decision Scorecard
10. Next Actions
```
### rodauth-oauth-quick-reference.md Sections:
```
1. What Is It? (overview)
2. Key Stats
3. Why Consider It? (advantages)
4. Architecture Overview (your impl vs rodauth)
5. Database Schema Comparison
6. Feature Comparison Matrix
7. Code Examples
8. Integration Paths
9. Getting Started
10. Next Steps
```
### rodauth-oauth-analysis.md Sections:
```
1. Executive Summary
2. What Rodauth-OAuth Is
3. File Structure & Organization
4. OIDC/OAuth Features
5. Architecture: How It Works
6. Database Schema Requirements
7. Integration with Rails
8. Architectural Comparison
9. Feature Matrix
10. Integration Complexity
11. Key Findings & Recommendations
12. Migration Path & Code Examples
```
---
## For Your Team
### Sharing with Stakeholders
- **Non-technical:** Use RODAUTH_DECISION_GUIDE.md (TL;DR section)
- **Technical leads:** Use rodauth-oauth-quick-reference.md
- **Engineers:** Use rodauth-oauth-analysis.md (sections 1-6)
- **Security team:** Use rodauth-oauth-analysis.md (security sections)
### Team Discussion
Print out the decision matrix from RODAUTH_DECISION_GUIDE.md and:
1. Walk through each option
2. Discuss team comfort with framework learning
3. Check against feature roadmap
4. Decide on maintenance philosophy
5. Vote on preferred option
---
## Next Steps After Reading
### If Choosing Option A (Keep Custom):
- [ ] Plan feature roadmap (refresh tokens first)
- [ ] Allocate team capacity
- [ ] Add token hashing security
- [ ] Set up security monitoring
### If Choosing Option B (Full Migration):
- [ ] Assign team member to learn Roda/Rodauth
- [ ] Run examples from `/tmp/rodauth-oauth/examples`
- [ ] Plan database migration
- [ ] Prepare rollback plan
- [ ] Schedule migration window
### If Choosing Option C (Hybrid):
- [ ] Evaluate microservices capability
- [ ] Review service communication plan
- [ ] Set up service infrastructure
- [ ] Plan gradual deployment
---
## Bonus: Running the Example
Rodauth-OAuth includes a working OIDC server example you can run:
```bash
cd /Users/dkam/Development/clinch/tmp/rodauth-oauth/examples/oidc
ruby authentication_server.rb
# Then visit: http://localhost:9292
# Login with: foo@bar.com / password
# See: Full OIDC provider in action
```
---
## Questions?
These documents should answer:
- What is rodauth-oauth?
- How does it compare to my implementation?
- What features would we gain?
- What would we lose?
- How much effort is a migration?
- Should we switch?
If questions remain, reference the specific section in the analysis documents.
---
## Document Generation Info
**Generated:** November 12, 2025
**Analysis Duration:** Complete codebase exploration of rodauth-oauth gem
**Sources Analyzed:**
- 34 feature files (10,000+ lines of code)
- 7 database migrations
- 6 complete example applications
- Comprehensive test suite
- README and migration guides
**Analysis Includes:**
- Line-by-line code structure review
- Database schema comparison
- Feature cross-reference analysis
- Integration complexity assessment
- Security analysis
- Effort estimation models
---
**Start with RODAUTH_DECISION_GUIDE.md and go from there!**

View File

@@ -1,426 +0,0 @@
# Rodauth-OAuth Decision Guide
## TL;DR - Make Your Choice Here
### Option A: Keep Your Rails Implementation
**Best if:** Authorization Code + PKCE is all you need, forever
- Keep your current 450 lines of OIDC controller code
- Maintain incrementally as needs change
- Stay 100% in Rails ecosystem
- Time investment: Ongoing (2-3 months to feature parity)
- Learning curve: None (already know Rails)
### Option B: Switch to Rodauth-OAuth
**Best if:** You need enterprise features, standards compliance, low maintenance
- Replace 450 lines with plugin config
- Get 34 optional features on demand
- OpenID Certified, production-hardened
- Time investment: 4-8 weeks (one-time)
- Learning curve: Medium (learn Roda/Rodauth)
### Option C: Hybrid (Recommended if Option B appeals you)
**Best if:** You want rodauth-oauth benefits without framework change
- Run Rodauth-OAuth as separate microservice
- Keep your Rails app unchanged
- Services talk via HTTP APIs
- Time investment: 2-3 weeks (independent services)
- Learning curve: Low (Roda is isolated)
---
## Decision Matrix
```
┌─────────────────────────────────────────────────────────────────┐
│ Do you need features beyond Authorization Code + PKCE? │
├─────────────────────────────────────────────────────────────────┤
│ YES ─→ Go to Question 2 │
│ NO ─→ KEEP YOUR IMPLEMENTATION │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Can your team learn Roda (different from Rails)? │
├─────────────────────────────────────────────────────────────────┤
│ YES ─→ SWITCH TO RODAUTH-OAUTH │
│ NO ─→ Go to Question 3 │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Can you run separate services (microservices)? │
├─────────────────────────────────────────────────────────────────┤
│ YES ─→ USE HYBRID APPROACH │
│ NO ─→ KEEP YOUR IMPLEMENTATION │
└─────────────────────────────────────────────────────────────────┘
```
---
## Feature Roadmap Comparison
### Scenario 1: You Need Refresh Tokens (Common)
**Option A (Keep Custom):**
- Implement refresh token endpoints
- Add refresh_token columns to DB
- Token rotation logic
- Estimate: 1-2 weeks of work
- Ongoing: Maintain refresh token security
**Option B (Rodauth-OAuth):**
- Already built and tested
- Just enable: `:oauth_authorization_code_grant` (includes refresh)
- Token rotation: Configurable options
- Estimate: Already included
- Ongoing: Community maintains
**Option C (Hybrid):**
- Rodauth-OAuth handles it
- Your app unchanged
- Same as Option B for this feature
### Scenario 2: You Need Token Revocation
**Option A (Keep Custom):**
- Build `/oauth/revoke` endpoint
- Implement token blacklist or DB update
- Handle race conditions
- Estimate: 1-2 weeks
- Ongoing: Monitor revocation leaks
**Option B (Rodauth-OAuth):**
- Enable `:oauth_token_revocation` feature
- RFC 7009 compliant out of the box
- Estimate: Already included
- Ongoing: Community handles RFC updates
**Option C (Hybrid):**
- Same as Option B
### Scenario 3: You Need Client Credentials Grant
**Option A (Keep Custom):**
- New endpoint logic
- Client authentication (different from user auth)
- Token generation for apps without users
- Estimate: 2-3 weeks
- Ongoing: Test with external clients
**Option B (Rodauth-OAuth):**
- Enable `:oauth_client_credentials_grant` feature
- All edge cases handled
- Estimate: Already included
- Ongoing: Community maintains
**Option C (Hybrid):**
- Same as Option B
---
## Architecture Diagrams
### Current Setup (Your Implementation)
```
┌─────────────────────────────┐
│ Your Rails Application │
├─────────────────────────────┤
│ app/controllers/ │
│ oidc_controller.rb │ ← 450 lines of OAuth logic
│ │
│ app/models/ │
│ OidcAuthorizationCode │
│ OidcAccessToken │
│ OidcUserConsent │
│ │
│ app/services/ │
│ OidcJwtService │
├─────────────────────────────┤
│ Rails ActiveRecord │
├─────────────────────────────┤
│ PostgreSQL Database │
│ - oidc_authorization_codes
│ - oidc_access_tokens
│ - oidc_user_consents
│ - applications
└─────────────────────────────┘
```
### Option B: Full Migration
```
┌──────────────────────────────┐
│ Roda + Rodauth-OAuth App │
├──────────────────────────────┤
│ lib/rodauth_app.rb │ ← Config (not code!)
│ enable :oidc, │
│ enable :oauth_pkce, │
│ enable :oauth_token_... │
│ │
│ [Routes auto-mounted] │
│ /.well-known/config │
│ /oauth/authorize │
│ /oauth/token │
│ /oauth/userinfo │
│ /oauth/revoke │
│ /oauth/introspect │
├──────────────────────────────┤
│ Sequel ORM │
├──────────────────────────────┤
│ PostgreSQL Database │
│ - accounts (rodauth)
│ - oauth_applications
│ - oauth_grants (unified!)
│ - optional feature tables
└──────────────────────────────┘
```
### Option C: Microservices Architecture (Hybrid)
```
┌──────────────────────────┐ ┌──────────────────────────┐
│ Your Rails App │ │ Rodauth-OAuth Service │
├──────────────────────────┤ ├──────────────────────────┤
│ Normal Rails Controllers │ │ lib/rodauth_app.rb │
│ & Business Logic │ │ [OAuth Features] │
│ │ │ │
│ HTTP Calls to →──────────┼─────→ /.well-known/config │
│ OAuth Service OAuth │ │ /oauth/authorize │
│ HTTP API │ │ /oauth/token │
│ │ │ /oauth/userinfo │
│ Verify Tokens via →──────┼─────→ /oauth/introspect │
│ /oauth/introspect │ │ │
├──────────────────────────┤ ├──────────────────────────┤
│ Rails ActiveRecord │ │ Sequel ORM │
├──────────────────────────┤ ├──────────────────────────┤
│ PostgreSQL │ │ PostgreSQL │
│ [business tables] │ │ [oauth tables] │
└──────────────────────────┘ └──────────────────────────┘
```
---
## Effort Estimates
### Option A: Keep & Enhance Custom Implementation
```
Refresh Tokens: 1-2 weeks
Token Revocation: 1-2 weeks
Token Introspection: 1-2 weeks
Client Credentials: 2-3 weeks
Device Code: 3-4 weeks
JWT Access Tokens: 1-2 weeks
Session Management: 2-3 weeks
Front-Channel Logout: 1-2 weeks
Back-Channel Logout: 2-3 weeks
─────────────────────────────────
TOTAL FOR PARITY: 15-25 weeks
(4-6 months of work)
ONGOING MAINTENANCE: ~8-10 hours/month
(security updates, RFC changes, bug fixes)
```
### Option B: Migrate to Rodauth-OAuth
```
Learn Roda/Rodauth: 1-2 weeks
Migrate Database Schema: 1-2 weeks
Replace OIDC Code: 1-2 weeks
Test & Validation: 2-3 weeks
─────────────────────────────────
ONE-TIME EFFORT: 5-9 weeks
(1-2 months)
ONGOING MAINTENANCE: ~1-2 hours/month
(dependency updates, config tweaks)
```
### Option C: Hybrid Approach
```
Set up Rodauth service: 1-2 weeks
Configure integration: 1-2 weeks
Test both services: 1 week
─────────────────────────────────
ONE-TIME EFFORT: 3-5 weeks
(less than Option B)
ONGOING MAINTENANCE: ~2-3 hours/month
(maintain two services, but Roda handles OAuth)
```
---
## Real-World Questions to Ask Your Team
### Question 1: Feature Needs
- "Do we need refresh tokens?"
- "Will clients ask for token revocation?"
- "Do we support service-to-service auth (client credentials)?"
- "Will we ever need device code flow (IoT)?"
If YES to any: **Option B or C makes sense**
### Question 2: Maintenance Philosophy
- "Do we want to own the OAuth code?"
- "Can we afford to maintain OAuth compliance?"
- "Do we have experts in OAuth/OIDC?"
If NO to all: **Option B or C is better**
### Question 3: Framework Flexibility
- "Is Rails non-negotiable for this company?"
- "Can our team learn a new framework?"
- "Can we run microservices?"
If Rails is required: **Option C (hybrid)**
### Question 4: Time Constraints
- "Do we have 4-8 weeks for a migration?"
- "Can we maintain OAuth for years?"
- "What if specs change?"
If time-constrained: **Option B is fastest path to full features**
---
## Security Comparison
### Your Implementation
- ✓ PKCE support
- ✓ JWT signing
- ✓ HTTPS recommended
- ✗ Token hashing (stores tokens in plaintext)
- ✗ Token rotation
- ✗ DPoP (token binding)
- ✗ Automatic spec compliance
- Risk: Token theft if DB compromised
### Rodauth-OAuth
- ✓ PKCE support
- ✓ JWT signing
- ✓ Token hashing (bcrypt by default)
- ✓ Token rotation policies
- ✓ DPoP support (RFC 9449)
- ✓ TLS mutual authentication
- ✓ Automatic spec updates
- ✓ Certified compliance
- Risk: Minimal (industry-standard)
---
## Cost-Benefit Summary
### Keep Your Implementation
```
Costs:
- 15-25 weeks to feature parity
- Ongoing security monitoring
- Spec compliance tracking
- Bug fixes & edge cases
Benefits:
- No framework learning
- Full code understanding
- Rails-native patterns
- Minimal dependencies
```
### Switch to Rodauth-OAuth
```
Costs:
- 5-9 weeks migration effort
- Learn Roda/Rodauth
- Database schema changes
- Test all flows
Benefits:
- Get 34 features immediately
- Certified compliance
- Community-maintained
- Security best practices
- Ongoing support
```
### Hybrid Approach
```
Costs:
- 3-5 weeks setup
- Learn Roda basics
- Operate two services
- Service communication
Benefits:
- All Rodauth-OAuth features
- Rails app unchanged
- Independent scaling
- Clear separation of concerns
```
---
## Decision Scorecard
| Factor | Option A | Option B | Option C |
|--------|----------|----------|----------|
| Initial Time | Low | Medium | Medium-Low |
| Ongoing Effort | High | Low | Medium |
| Feature Completeness | Low | High | High |
| Framework Learning | None | Medium | Low |
| Standards Compliance | Manual | Auto | Auto |
| Deployment Complexity | Simple | Simple | Complex |
| Team Preference | ??? | ??? | ??? |
---
## Next Actions
### For Option A (Keep Custom):
1. Plan feature roadmap (refresh tokens first)
2. Allocate team capacity for implementation
3. Document OAuth decisions
4. Set up security monitoring
### For Option B (Full Migration):
1. Assign someone to learn Roda/Rodauth
2. Run rodauth-oauth examples
3. Plan database migration
4. Schedule migration window
5. Prepare rollback plan
### For Option C (Hybrid):
1. Evaluate microservices capability
2. Run Rodauth-OAuth example
3. Plan service boundaries
4. Set up service communication
5. Plan infrastructure for two services
---
## Still Can't Decide?
Ask these questions:
1. **Will you add features beyond Auth Code + PKCE in next 12 months?**
- YES → Option B or C
- NO → Option A
2. **Do you have maintenance bandwidth?**
- YES → Option A
- NO → Option B or C
3. **Can you run multiple services?**
- YES → Option C (best of both)
- NO → Option B (if framework is OK) or Option A (stay Rails)
---
## Document Files
You now have three documents:
1. **rodauth-oauth-analysis.md** - Deep technical analysis (12 sections)
2. **rodauth-oauth-quick-reference.md** - Quick lookup guide
3. **RODAUTH_DECISION_GUIDE.md** - This decision framework
Read in this order:
1. This guide (make a decision)
2. Quick reference (understand architecture)
3. Analysis (deep dive on your choice)
---
**Made Your Decision?** Create an issue/commit to document your choice and next steps!

View File

@@ -153,24 +153,25 @@ This checklist ensures Clinch meets security, quality, and documentation standar
### Deployment
- [x] Docker support
- [x] Docker Compose example
- [ ] Production deployment guide
- [x] Production deployment guide (Docker Compose with .env configuration, upgrading, logs)
- [x] Backup and restore documentation
- [ ] Migration strategy documentation
## Security Hardening
### Headers & CSP
- [ ] Review Content Security Policy
- [ ] HSTS configuration
- [ ] X-Frame-Options
- [ ] X-Content-Type-Options
- [ ] Referrer-Policy
- [x] Content Security Policy (comprehensive policy in config/initializers/content_security_policy.rb)
- [x] X-Frame-Options (DENY in production config)
- [x] X-Content-Type-Options (nosniff - Rails default)
- [x] Referrer-Policy (strict-origin-when-cross-origin in production config)
### Rate Limiting
- [ ] Login attempt rate limiting
- [ ] API endpoint rate limiting
- [ ] Token endpoint rate limiting
- [ ] Password reset rate limiting
- [x] Login attempt rate limiting (20/3min on sessions#create)
- [x] TOTP verification rate limiting (10/3min on sessions#verify_totp)
- [x] WebAuthn rate limiting (10/1min on webauthn endpoints, 10/3min on session endpoints)
- [x] Password reset rate limiting (10/3min on request, 10/10min on completion)
- [x] Invitation acceptance rate limiting (10/10min)
- [x] OAuth token endpoint rate limiting (60/1min on token, 30/1min on authorize)
- [x] Backup code rate limiting (5 failed attempts per hour, model-level)
### Secrets Management
- [x] No secrets in code
@@ -180,8 +181,7 @@ This checklist ensures Clinch meets security, quality, and documentation standar
### Logging & Monitoring
- [x] Sentry integration (optional)
- [ ] Document what should be logged
- [ ] Document what should NOT be logged (tokens, passwords)
- [x] Parameter filtering configured (passwords, tokens, secrets, backup codes, emails filtered from logs)
- [ ] Audit log for admin actions
## Known Limitations & Risks
@@ -225,15 +225,15 @@ To move from "experimental" to "Beta", the following must be completed:
- [x] All tests passing
- [x] Core features implemented and tested
- [x] Basic documentation complete
- [x] Backup/restore documentation
- [x] Production deployment guide
- [ ] At least one external security review or penetration test
- [ ] Production deployment guide
- [ ] Backup/restore documentation
**Important (Should have for Beta):**
- [ ] Rate limiting on auth endpoints
- [ ] Security headers configuration documented
- [x] Rate limiting on auth endpoints
- [x] Security headers configuration documented (CSP, X-Frame-Options, X-Content-Type-Options, Referrer-Policy)
- [x] Known limitations documented (ForwardAuth same-domain requirement in README)
- [ ] Admin audit logging
- [ ] Known limitations documented
**Nice to have (Can defer to post-Beta):**
- [ ] Bug bounty program
@@ -253,16 +253,12 @@ To move from "experimental" to "Beta", the following must be completed:
**Before Beta Release:**
- 🔶 External security review recommended
- 🔶 Rate limiting implementation needed
- 🔶 Production deployment documentation
- 🔶 Security hardening checklist completion
- 🔶 Admin audit logging (optional)
**Recommendation:** Consider Beta status after:
1. External security review or penetration testing
2. Rate limiting implementation
3. Production hardening documentation
4. 1-2 months of real-world testing
2. Real-world testing period
---
Last updated: 2026-01-01
Last updated: 2026-01-02

View File

@@ -1,913 +0,0 @@
# Rodauth-OAuth Analysis: Comprehensive Comparison with Clinch's Custom Implementation
## Executive Summary
**Rodauth-OAuth** is a production-ready Ruby gem that implements the OAuth 2.0 framework and OpenID Connect on top of the `rodauth` authentication library. It's architected as a modular feature-based system that integrates with Roda (a routing library) and provides extensive OAuth/OIDC capabilities.
Your current Clinch implementation is a **custom, minimalist Rails-based OIDC provider** focusing on the authorization code grant with PKCE support. Switching to rodauth-oauth would provide significantly more features and standards compliance but requires architectural changes.
---
## 1. What Rodauth-OAuth Is
### Core Identity
- **Type**: Ruby gem providing OAuth 2.0 & OpenID Connect implementation
- **Framework**: Built on top of `rodauth` (a dedicated authentication library)
- **Web Framework**: Designed for Roda framework (lightweight, routing-focused)
- **Rails Support**: Available via `rodauth-rails` wrapper
- **Maturity**: Production-ready, OpenID-Certified for multiple profiles
- **Author**: Tiago Cardoso (tiago.cardoso@gmail.com)
- **License**: Apache 2.0
### Architecture Philosophy
- **Feature-based**: Modular "features" that can be enabled/disabled
- **Database-agnostic**: Uses Sequel ORM, works with any SQL database
- **Highly configurable**: Override methods to customize behavior
- **Standards-focused**: Implements RFCs and OpenID specs strictly
---
## 2. File Structure and Organization
### Directory Layout in `/tmp/rodauth-oauth`
```
rodauth-oauth/
├── lib/
│ └── rodauth/
│ ├── oauth.rb # Main module entry point
│ ├── oauth/
│ │ ├── version.rb
│ │ ├── database_extensions.rb
│ │ ├── http_extensions.rb
│ │ ├── jwe_extensions.rb
│ │ └── ttl_store.rb
│ └── features/ # 34 feature files!
│ ├── oauth_base.rb # Foundation
│ ├── oauth_authorization_code_grant.rb
│ ├── oauth_pkce.rb
│ ├── oauth_jwt*.rb # JWT support (5 files)
│ ├── oidc.rb # OpenID Core
│ ├── oidc_*logout.rb # Logout flows (3 files)
│ ├── oauth_client_credentials_grant.rb
│ ├── oauth_device_code_grant.rb
│ ├── oauth_token_revocation.rb
│ ├── oauth_token_introspection.rb
│ ├── oauth_dynamic_client_registration.rb
│ ├── oauth_dpop.rb # DPoP support
│ ├── oauth_tls_client_auth.rb
│ ├── oauth_pushed_authorization_request.rb
│ ├── oauth_assertion_base.rb
│ └── ... (more features)
├── test/
│ ├── migrate/ # Database migrations
│ │ ├── 001_accounts.rb
│ │ ├── 003_oauth_applications.rb
│ │ ├── 004_oauth_grants.rb
│ │ ├── 005_pushed_requests.rb
│ │ ├── 006_saml_settings.rb
│ │ └── 007_dpop_proofs.rb
│ └── [multiple test directories with hundreds of tests]
├── examples/ # Full working examples
│ ├── authorization_server/
│ ├── oidc/
│ ├── jwt/
│ ├── device_grant/
│ ├── saml_assertion/
│ └── mtls/
├── templates/ # HTML/ERB templates
├── locales/ # i18n translations
├── doc/
└── [Gemfile, README, MIGRATION-GUIDE, etc.]
```
### Feature Count: 34 Features!
The gem is completely modular. Each feature can be independently enabled:
**Core OAuth Features:**
- `oauth_base` - Foundation
- `oauth_authorization_code_grant` - Authorization Code Flow
- `oauth_implicit_grant` - Implicit Flow
- `oauth_client_credentials_grant` - Client Credentials Flow
- `oauth_device_code_grant` - Device Code Flow
**Token Management:**
- `oauth_token_revocation` - RFC 7009
- `oauth_token_introspection` - RFC 7662
- `oauth_refresh_token` - Refresh tokens
**Security & Advanced:**
- `oauth_pkce` - RFC 7636 (what Clinch is using!)
- `oauth_jwt` - JWT Access Tokens
- `oauth_jwt_bearer_grant` - RFC 7523
- `oauth_saml_bearer_grant` - RFC 7522
- `oauth_tls_client_auth` - Mutual TLS
- `oauth_dpop` - Demonstrating Proof-of-Possession
- `oauth_jwt_secured_authorization_request` - Request Objects
- `oauth_resource_indicators` - RFC 8707
- `oauth_pushed_authorization_request` - RFC 9126
**OpenID Connect:**
- `oidc` - Core OpenID Connect
- `oidc_session_management` - Session Management
- `oidc_rp_initiated_logout` - RP-Initiated Logout
- `oidc_frontchannel_logout` - Front-Channel Logout
- `oidc_backchannel_logout` - Back-Channel Logout
- `oidc_dynamic_client_registration` - Dynamic Registration
- `oidc_self_issued` - Self-Issued Provider
**Management & Discovery:**
- `oauth_application_management` - Client app dashboard
- `oauth_grant_management` - Grant management dashboard
- `oauth_dynamic_client_registration` - RFC 7591/7592
- `oauth_jwt_jwks` - JWKS endpoint
---
## 3. OIDC/OAuth Features Provided
### Grant Types Supported (15 types!)
| Grant Type | Status | RFC/Spec |
|-----------|--------|----------|
| Authorization Code | Yes | RFC 6749 |
| Implicit | Optional | RFC 6749 |
| Client Credentials | Optional | RFC 6749 |
| Device Code | Optional | RFC 8628 |
| Refresh Token | Yes | RFC 6749 |
| JWT Bearer | Optional | RFC 7523 |
| SAML Bearer | Optional | RFC 7522 |
### Response Types & Modes
**Response Types:**
- `code` (Authorization Code) - Default
- `id_token` (OIDC Implicit) - Optional
- `token` (Implicit) - Optional
- `id_token token` (Hybrid) - Optional
- `code id_token` (Hybrid) - Optional
- `code token` (Hybrid) - Optional
- `code id_token token` (Hybrid) - Optional
**Response Modes:**
- `query` (URL parameters)
- `fragment` (URL fragment)
- `form_post` (HTML form)
- `jwt` (JWT-based response)
### OpenID Connect Features
**Certified for:**
- Basic OP (OpenID Provider)
- Implicit OP
- Hybrid OP
- Config OP (Discovery)
- Dynamic OP (Dynamic Client Registration)
- Form Post OP
- 3rd Party-Init OP
- Session Management OP
- RP-Initiated Logout OP
- Front-Channel Logout OP
- Back-Channel Logout OP
**Standard Claims Support:**
- `openid`, `email`, `profile`, `address`, `phone` scopes
- Automatic claim mapping per OpenID spec
- Custom claims via extension
**Token Features:**
- JWT ID Tokens
- JWT Access Tokens
- Encrypted JWTs (JWE support)
- HMAC-SHA256 signing
- RSA/EC signing
- Custom token formats
### Security Features
| Feature | Details |
|---------|---------|
| PKCE | RFC 7636 - Proof Key for Public Clients |
| Token Hashing | Bcrypt-based token storage (plain text optional) |
| DPoP | RFC 9449 - Demonstrating Proof-of-Possession |
| TLS Client Auth | RFC 8705 - Mutual TLS authentication |
| Request Objects | JWT-signed/encrypted authorization requests |
| Pushed Auth Requests | RFC 9126 - Pushed Authorization Requests |
| Token Introspection | RFC 7662 - Token validation without DB lookup |
| Token Revocation | RFC 7009 - Revoke tokens on demand |
### Scopes & Authorization
- Configurable scope list per application
- Offline access support (refresh tokens)
- Scope-based access control
- Custom scope handlers
- Consent UI for user authorization
---
## 4. Architecture: How It Works
### As a Plugin System
Rodauth-OAuth integrates with Roda as a **plugin**:
```ruby
# This is how you configure it
class AuthServer < Roda
plugin :rodauth do
db database_connection
# Enable features
enable :login, :logout, :create_account, :oidc, :oidc_session_management,
:oauth_pkce, :oauth_authorization_code_grant
# Configure
oauth_application_scopes %w[openid email profile]
oauth_require_pkce true
hmac_secret "SECRET"
# Customize with blocks
oauth_jwt_keys("RS256" => [private_key])
oauth_jwt_public_keys("RS256" => [public_key])
end
end
```
### Request Flow Architecture
```
1. Authorization Request
rodauth validates params
(if not auth'd) user logs in via rodauth
(if first use) consent page rendered
create oauth_grant (code, nonce, PKCE challenge, etc.)
redirect with auth code
2. Token Exchange
rodauth validates client (Basic/POST auth)
validates code, redirect_uri, PKCE verifier
creates access token (plain or JWT)
creates refresh token
returns JSON with tokens
3. UserInfo
validate access token
lookup grant/account
return claims as JSON
```
### Feature Composition
Features depend on each other. For example:
- `oidc` depends on: `active_sessions`, `oauth_jwt`, `oauth_jwt_jwks`, `oauth_authorization_code_grant`, `oauth_implicit_grant`
- `oauth_pkce` depends on: `oauth_authorization_code_grant`
- `oidc_rp_initiated_logout` depends on: `oidc`
This is a **strong dependency injection pattern**.
---
## 5. Database Schema Requirements
### Rodauth-OAuth Tables
#### `accounts` table (from rodauth)
```sql
CREATE TABLE accounts (
id INTEGER PRIMARY KEY,
status_id INTEGER DEFAULT 1, -- unverified/verified/closed
email VARCHAR UNIQUE NOT NULL,
-- password-related columns (added by rodauth features)
password_hash VARCHAR,
-- other rodauth-managed columns
);
```
#### `oauth_applications` table (75+ columns!)
```sql
CREATE TABLE oauth_applications (
id INTEGER PRIMARY KEY,
account_id INTEGER FOREIGN KEY,
-- Basic info
name VARCHAR NOT NULL,
description VARCHAR,
homepage_url VARCHAR,
logo_uri VARCHAR,
tos_uri VARCHAR,
policy_uri VARCHAR,
-- OAuth credentials
client_id VARCHAR UNIQUE NOT NULL,
client_secret VARCHAR UNIQUE NOT NULL,
registration_access_token VARCHAR,
-- OAuth config
redirect_uri VARCHAR NOT NULL,
scopes VARCHAR NOT NULL,
token_endpoint_auth_method VARCHAR,
grant_types VARCHAR,
response_types VARCHAR,
response_modes VARCHAR,
-- JWT/JWKS
jwks_uri VARCHAR,
jwks TEXT,
jwt_public_key TEXT,
-- OIDC-specific
sector_identifier_uri VARCHAR,
application_type VARCHAR,
initiate_login_uri VARCHAR,
subject_type VARCHAR,
-- Token encryption algorithms
id_token_signed_response_alg VARCHAR,
id_token_encrypted_response_alg VARCHAR,
id_token_encrypted_response_enc VARCHAR,
userinfo_signed_response_alg VARCHAR,
userinfo_encrypted_response_alg VARCHAR,
userinfo_encrypted_response_enc VARCHAR,
-- Request object handling
request_object_signing_alg VARCHAR,
request_object_encryption_alg VARCHAR,
request_object_encryption_enc VARCHAR,
request_uris VARCHAR,
require_signed_request_object BOOLEAN,
-- PAR (Pushed Auth Requests)
require_pushed_authorization_requests BOOLEAN DEFAULT FALSE,
-- DPoP
dpop_bound_access_tokens BOOLEAN DEFAULT FALSE,
-- TLS Client Auth
tls_client_auth_subject_dn VARCHAR,
tls_client_auth_san_dns VARCHAR,
tls_client_auth_san_uri VARCHAR,
tls_client_auth_san_ip VARCHAR,
tls_client_auth_san_email VARCHAR,
tls_client_certificate_bound_access_tokens BOOLEAN DEFAULT FALSE,
-- Logout URIs
post_logout_redirect_uris VARCHAR,
frontchannel_logout_uri VARCHAR,
frontchannel_logout_session_required BOOLEAN DEFAULT FALSE,
backchannel_logout_uri VARCHAR,
backchannel_logout_session_required BOOLEAN DEFAULT FALSE,
-- Response encryption
authorization_signed_response_alg VARCHAR,
authorization_encrypted_response_alg VARCHAR,
authorization_encrypted_response_enc VARCHAR,
contact_info VARCHAR,
software_id VARCHAR,
software_version VARCHAR
);
```
#### `oauth_grants` table (everything in one table!)
```sql
CREATE TABLE oauth_grants (
id INTEGER PRIMARY KEY,
account_id INTEGER FOREIGN KEY, -- nullable for client credentials
oauth_application_id INTEGER FOREIGN KEY,
sub_account_id INTEGER, -- for context-based ownership
type VARCHAR, -- 'authorization_code', 'refresh_token', etc.
-- Authorization code flow
code VARCHAR UNIQUE (per app),
redirect_uri VARCHAR,
-- Tokens (stored hashed or plain)
token VARCHAR UNIQUE,
token_hash VARCHAR UNIQUE,
refresh_token VARCHAR UNIQUE,
refresh_token_hash VARCHAR UNIQUE,
-- Expiry
expires_in TIMESTAMP NOT NULL,
revoked_at TIMESTAMP,
-- Scopes
scopes VARCHAR NOT NULL,
access_type VARCHAR DEFAULT 'offline', -- 'offline' or 'online'
-- PKCE
code_challenge VARCHAR,
code_challenge_method VARCHAR, -- 'plain' or 'S256'
-- Device Code Grant
user_code VARCHAR UNIQUE,
last_polled_at TIMESTAMP,
-- TLS Client Auth
certificate_thumbprint VARCHAR,
-- Resource Indicators
resource VARCHAR,
-- OpenID Connect
nonce VARCHAR,
acr VARCHAR, -- Authentication Context Class
claims_locales VARCHAR,
claims VARCHAR, -- custom OIDC claims
-- DPoP
dpop_jkt VARCHAR -- DPoP key thumbprint
);
```
#### Optional Tables for Advanced Features
```sql
-- For Pushed Authorization Requests
CREATE TABLE oauth_pushed_requests (
request_uri VARCHAR UNIQUE PRIMARY KEY,
oauth_application_id INTEGER FOREIGN KEY,
params TEXT, -- JSON params
created_at TIMESTAMP
);
-- For SAML Assertion Grant
CREATE TABLE oauth_saml_settings (
id INTEGER PRIMARY KEY,
oauth_application_id INTEGER FOREIGN KEY,
idp_url VARCHAR,
certificate TEXT,
-- ...
);
-- For DPoP
CREATE TABLE oauth_dpop_proofs (
id INTEGER PRIMARY KEY,
oauth_grant_id INTEGER FOREIGN KEY,
jti VARCHAR UNIQUE,
created_at TIMESTAMP
);
```
### Key Differences from Your Implementation
| Aspect | Your Implementation | Rodauth-OAuth |
|--------|-------------------|----------------|
| Authorization Codes | Separate table | In oauth_grants |
| Access Tokens | Separate table | In oauth_grants |
| Refresh Tokens | Not implemented | In oauth_grants |
| Token Hashing | Not done | Bcrypt (default) |
| Applications | Basic (name, client_id, secret) | 75+ columns for full spec |
| PKCE | Simple columns | Built-in feature |
| Account Data | In users table | In accounts table |
| Session Management | Session model | Rodauth's account_active_session_keys |
| User Consent | OidcUserConsent table | In memory or via hooks |
---
## 6. Integration Points with Rails
### Via Rodauth-Rails Wrapper
Rodauth-OAuth can be used in Rails through the `rodauth-rails` gem:
```bash
# Install generator
gem 'rodauth-rails'
bundle install
rails generate rodauth:install
rails generate rodauth:oauth:install # Generates OIDC tables/migrations
rails generate rodauth:oauth:views # Generates templates
```
### Generated Components
1. **Migration**: `db/migrate/*_create_rodauth_oauth.rb`
- Creates all OAuth tables
- Customizable column names via config
2. **Models**: `app/models/`
- `RodauthApp` (configuration)
- `OauthApplication` (client app)
- `OauthGrant` (grants/tokens)
- Customizable!
3. **Views**: `app/views/rodauth/`
- Authorization consent form
- Application management dashboard
- Grant management dashboard
4. **Lib**: `lib/rodauth_app.rb`
- Main rodauth configuration
### Rails Controller Integration
```ruby
class BooksController < ApplicationController
before_action :require_oauth_authorization, only: %i[create update]
before_action :require_oauth_authorization_scopes, only: %i[create update]
private
def require_oauth_authorization(scope = "books.read")
rodauth.require_oauth_authorization(scope)
end
end
```
Or for route protection:
```ruby
# config/routes.rb
namespace :api do
resources :books, only: [:index] # protected by rodauth
end
```
---
## 7. Architectural Comparison
### Your Custom Implementation
**Pros:**
- Simple, easy to understand
- Minimal dependencies (just JWT, OpenSSL)
- Lightweight database (small tables)
- Direct Rails integration
- Minimal features = less surface area
**Cons:**
- Only supports Authorization Code + PKCE
- No refresh tokens
- No token revocation/introspection
- No client credentials grant
- No JWT access tokens
- Manual consent management
- Not standards-compliant (missing many OIDC features)
- Will need continuous custom development
**Architecture:**
```
Rails Controller
OidcController (450 lines)
OidcAuthorizationCode Model
OidcAccessToken Model
OidcUserConsent Model
Database
```
### Rodauth-OAuth Implementation
**Pros:**
- 34 built-in features
- OpenID-Certified
- Production-tested
- Highly configurable
- Comprehensive token management
- Standards-compliant (RFCs & OpenID specs)
- Strong test coverage (hundreds of tests)
- Active maintenance
**Cons:**
- More complex (needs Roda/Rodauth knowledge)
- Larger codebase to learn
- Rails integration via wrapper (extra layer)
- Different paradigm (Roda vs Rails)
- More database columns to manage
**Architecture:**
```
Roda App
Rodauth Plugin (configurable)
├── oauth_base (foundation)
├── oauth_authorization_code_grant
├── oauth_pkce
├── oauth_jwt
├── oidc (all OpenID features)
├── [other optional features]
Sequel ORM
Database (flexible schema)
```
---
## 8. Feature Comparison Matrix
| Feature | Your Impl | Rodauth-OAuth | Notes |
|---------|-----------|---------------|-------|
| **Authorization Code** | ✓ | ✓ | Both support |
| **PKCE** | ✓ | ✓ | Both support |
| **Refresh Tokens** | ✗ | ✓ | You'd need to add |
| **Implicit Flow** | ✗ | ✓ Optional | Legacy, not recommended |
| **Client Credentials** | ✗ | ✓ Optional | Machine-to-machine |
| **Device Code** | ✗ | ✓ Optional | IoT devices |
| **JWT Bearer Grant** | ✗ | ✓ Optional | Service accounts |
| **SAML Bearer Grant** | ✗ | ✓ Optional | Enterprise SAML |
| **JWT Access Tokens** | ✗ | ✓ Optional | Stateless tokens |
| **Token Revocation** | ✗ | ✓ | RFC 7009 |
| **Token Introspection** | ✗ | ✓ | RFC 7662 |
| **Pushed Auth Requests** | ✗ | ✓ Optional | RFC 9126 |
| **DPoP** | ✗ | ✓ Optional | RFC 9449 |
| **TLS Client Auth** | ✗ | ✓ Optional | RFC 8705 |
| **OpenID Connect** | ✓ Basic | ✓ Full | Yours is minimal |
| **ID Tokens** | ✓ | ✓ | Both support |
| **UserInfo Endpoint** | ✓ | ✓ | Both support |
| **Discovery** | ✓ | ✓ | Both support |
| **Session Management** | ✗ | ✓ Optional | Check session iframe |
| **RP-Init Logout** | ✓ | ✓ | Both support |
| **Front-Channel Logout** | ✗ | ✓ | Iframe-based |
| **Back-Channel Logout** | ✗ | ✓ | Server-to-server |
| **Dynamic Client Reg** | ✗ | ✓ Optional | RFC 7591/7592 |
| **Token Hashing** | ✗ | ✓ | Security best practice |
| **Scopes** | ✓ | ✓ | Both support |
| **Custom Claims** | ✓ Manual | ✓ Built-in | Yours via JWT service |
| **Consent UI** | ✓ | ✓ | Both support |
| **Client App Dashboard** | ✗ | ✓ Optional | Built-in |
| **Grant Management Dashboard** | ✗ | ✓ Optional | Built-in |
---
## 9. Integration Complexity Analysis
### Switching to Rodauth-OAuth
#### Medium Complexity (Not Trivial, but Doable)
**What you'd need to do:**
1. **Learn Roda + Rodauth**
- Move from pure Rails to Roda-based architecture
- Understand rodauth feature system
- Time: 1-2 weeks for Rails developers
2. **Migrate Database Schema**
- Consolidate tables: authorization codes + access tokens → oauth_grants
- Rename columns to match rodauth conventions
- Add many new columns for feature support
- Migration script needed: ~100-300 lines
- Time: 1 week development + testing
3. **Replace Your OIDC Code**
- Replace your 450-line OidcController
- Remove your 3 model files
- Keep your OidcJwtService (mostly compatible)
- Add rodauth configuration
- Time: 1-2 weeks
4. **Update Application/Client Model**
- Expand `Application` model properties
- Support all OAuth scopes, grant types, response types
- Time: 3-5 days
5. **Create Migrations from Template**
- Use rodauth-oauth migration templates
- Customize for your database
- Time: 2-3 days
6. **Testing**
- Write integration tests
- Verify all OAuth flows still work
- Check token validation logic
- Time: 2-3 weeks
**Total Effort:** 4-8 weeks for experienced team
### Keeping Your Implementation (Custom Path)
#### What You'd Need to Add
To reach feature parity with rodauth-oauth (for common use cases):
1. **Refresh Token Support** (1-2 weeks)
- Database schema
- Token refresh endpoint
- Token validation logic
2. **Token Revocation** (1 week)
- Revocation endpoint
- Token blacklist/invalidation
3. **Token Introspection** (1 week)
- Introspection endpoint
- Token validation without DB lookup
4. **Client Credentials Grant** (2 weeks)
- Endpoint logic
- Client authentication
- Token generation for apps
5. **Improved Security** (ongoing)
- Token hashing (bcrypt)
- Rate limiting
- Additional validation
6. **Advanced OIDC Features**
- Session Management
- Logout endpoints (front/back-channel)
- Dynamic client registration
- Device code flow
**Total Effort:** 2-3 months ongoing
---
## 10. Key Findings & Recommendations
### What Rodauth-OAuth Does Better
1. **Standards Compliance**
- Certified for 11 OpenID Connect profiles
- Implements 20+ RFCs and specs
- Regular spec updates
2. **Security**
- Token hashing by default
- DPoP support (token binding)
- TLS client auth
- Proper scope enforcement
3. **Features**
- 34 optional features (you get what you need)
- No bloat - only enable what you use
- Mature refresh token handling
4. **Production Readiness**
- Thousands of test cases
- Open source (auditable)
- Active maintenance
- Real-world deployments
5. **Flexibility**
- Works with any SQL database
- Highly configurable column names
- Custom behavior via overrides
- Multiple app types support
### What Your Implementation Does Better
1. **Simplicity**
- Fewer dependencies
- Smaller codebase
- Easier to reason about
2. **Rails Integration**
- Direct Rails ActiveRecord
- No Roda learning curve
- Familiar patterns
3. **Control**
- Full control of every line
- No surprises
- Easy to debug
### Recommendation
**Use Rodauth-OAuth IF:**
- You need a production OIDC/OAuth provider
- You want standards compliance
- You plan to support multiple grant types
- You need token revocation/introspection
- You want a maintained codebase
**Keep Your Custom Implementation IF:**
- Authorization Code + PKCE only is sufficient
- You're avoiding Roda/Rodauth learning curve
- Your org standardizes on Rails patterns
- You have time to add features incrementally
- You need maximum control and simplicity
**Hybrid Approach:**
- Use rodauth-oauth for OIDC/OAuth server components
- Keep your Rails app for other features
- They can coexist (separate services)
---
## 11. Migration Path (If You Decide to Switch)
### Phase 1: Preparation (Week 1-2)
- Set up separate Roda app with rodauth-oauth
- Run alongside your existing service
- Parallel user testing
### Phase 2: Data Migration (Week 2-3)
- Create migration script for oauth_grants table
- Backfill existing auth codes and tokens
- Verify data integrity
### Phase 3: Gradual Cutover (Week 4-6)
- Direct some OAuth clients to new server
- Monitor for issues
- Swap over when confident
### Phase 4: Cleanup (Week 6+)
- Remove custom OIDC code
- Decommission old tables
- Document new architecture
---
## 12. Code Examples
### Rodauth-OAuth: Minimal Setup
```ruby
# Gemfile
gem 'roda'
gem 'rodauth-oauth'
gem 'sequel'
# lib/auth_server.rb
class AuthServer < Roda
plugin :render, views: 'views'
plugin :sessions, secret: 'SECRET'
plugin :rodauth do
db DB
enable :login, :logout, :create_account, :oidc, :oauth_pkce,
:oauth_authorization_code_grant, :oauth_token_introspection
oauth_application_scopes %w[openid email profile]
oauth_require_pkce true
hmac_secret 'HMAC_SECRET'
oauth_jwt_keys('RS256' => [private_key])
end
route do |r|
r.rodauth # All OAuth routes automatically mounted
# Your custom routes
r.get 'api' do
rodauth.require_oauth_authorization('api.read')
# return data
end
end
end
```
### Your Current Approach: Manual
```ruby
# app/controllers/oidc_controller.rb
def authorize
validate_params
find_application
check_authentication
handle_consent
generate_code
redirect_with_code
end
def token
extract_client_credentials
find_application
validate_code
check_pkce
generate_tokens
return_json
end
```
---
## Summary Table
| Aspect | Your Implementation | Rodauth-OAuth |
|--------|-------------------|----------------|
| **Framework** | Rails | Roda |
| **Database ORM** | ActiveRecord | Sequel |
| **Grant Types** | 1 (Auth Code) | 7+ options |
| **Token Types** | Opaque | Opaque or JWT |
| **Security Features** | Basic | Advanced (DPoP, MTLS, etc.) |
| **OIDC Compliance** | Partial | Full (Certified) |
| **Lines of Code** | ~1000 | ~10,000+ |
| **Features** | 2-3 | 34 optional |
| **Maintenance Burden** | High | Low (OSS) |
| **Learning Curve** | Low | Medium (Roda) |
| **Production Ready** | Yes | Yes |
| **Community** | Just you | Active |

View File

@@ -1,418 +0,0 @@
# Rodauth-OAuth: Quick Reference Guide
## What Is It?
A production-ready Ruby gem implementing OAuth 2.0 and OpenID Connect. Think of it as a complete, standards-certified OAuth/OIDC server library for Ruby apps.
## Key Stats
- **Framework**: Roda (not Rails, but works with Rails via wrapper)
- **Features**: 34 modular features you can enable/disable
- **Certification**: Officially certified for 11 OpenID Connect profiles
- **Test Coverage**: Hundreds of tests
- **Status**: Production-ready, actively maintained
## Why Consider It?
### Advantages Over Your Implementation
1. **Complete OAuth/OIDC Implementation**
- All major grant types supported
- Certified compliance with standards
- 20+ RFC implementations
2. **Security Features**
- Token hashing (bcrypt) by default
- DPoP support (token binding)
- TLS mutual authentication
- Proper scope enforcement
3. **Advanced Token Management**
- Refresh tokens (you don't have)
- Token revocation
- Token introspection
- Token rotation policies
4. **Low Maintenance**
- Well-tested codebase
- Active community
- Regular spec updates
- Battle-tested in production
5. **Extensible**
- Highly configurable
- Override any behavior you need
- Database-agnostic
- Works with any SQL DB
### What Your Implementation Does Better
1. **Simplicity** - Fewer lines of code, easier to understand
2. **Rails Native** - No need to learn Roda
3. **Control** - Full ownership of the codebase
4. **Minimal Dependencies** - Just JWT and OpenSSL
## Architecture Overview
### Your Current Setup
```
Rails App
└─ OidcController (450 lines)
├─ /oauth/authorize
├─ /oauth/token
├─ /oauth/userinfo
└─ /logout
Models:
├─ OidcAuthorizationCode
├─ OidcAccessToken
└─ OidcUserConsent
Features Supported:
├─ Authorization Code Flow ✓
├─ PKCE ✓
└─ Basic OIDC ✓
NOT Supported:
├─ Refresh Tokens
├─ Token Revocation
├─ Token Introspection
├─ Client Credentials Grant
├─ Device Code Flow
├─ Session Management
├─ Front/Back-Channel Logout
└─ Dynamic Client Registration
```
### Rodauth-OAuth Setup
```
Roda App (web framework)
└─ Rodauth Plugin (authentication/authorization)
├─ oauth_base (foundation)
├─ oauth_authorization_code_grant
├─ oauth_pkce
├─ oauth_jwt (optional)
├─ oidc (OpenID core)
├─ oidc_session_management (optional)
├─ oidc_rp_initiated_logout (optional)
├─ oidc_frontchannel_logout (optional)
├─ oidc_backchannel_logout (optional)
├─ oauth_token_revocation (optional)
├─ oauth_token_introspection (optional)
├─ oauth_client_credentials_grant (optional)
└─ ... (28+ more optional features)
Routes Generated Automatically:
├─ /.well-known/openid-configuration ✓
├─ /.well-known/jwks.json ✓
├─ /oauth/authorize ✓
├─ /oauth/token ✓
├─ /oauth/userinfo ✓
├─ /oauth/introspect (optional)
├─ /oauth/revoke (optional)
└─ /logout ✓
```
## Database Schema Comparison
### Your Current Tables
```
oidc_authorization_codes
├─ id
├─ user_id
├─ application_id
├─ code (unique)
├─ redirect_uri
├─ scope
├─ nonce
├─ code_challenge
├─ code_challenge_method
├─ used (boolean)
├─ expires_at
└─ created_at
oidc_access_tokens
├─ id
├─ user_id
├─ application_id
├─ token (unique)
├─ scope
├─ expires_at
└─ created_at
oidc_user_consents
├─ user_id
├─ application_id
├─ scopes_granted
└─ granted_at
applications
├─ id
├─ name
├─ client_id (unique)
├─ client_secret
├─ redirect_uris (JSON)
├─ app_type
└─ ... (few more fields)
```
### Rodauth-OAuth Tables
```
accounts (from rodauth)
├─ id
├─ status_id
├─ email
└─ password_hash
oauth_applications (75+ columns!)
├─ Basic: id, account_id, name, description
├─ OAuth: client_id, client_secret, redirect_uri, scopes
├─ Config: token_endpoint_auth_method, grant_types, response_types
├─ JWT/JWKS: jwks_uri, jwks, jwt_public_key
├─ OIDC: subject_type, id_token_signed_response_alg, etc.
├─ PAR: require_pushed_authorization_requests
├─ DPoP: dpop_bound_access_tokens
├─ TLS: tls_client_auth_* fields
└─ Logout: post_logout_redirect_uris, frontchannel_logout_uri, etc.
oauth_grants (consolidated - replaces your two tables!)
├─ id, account_id, oauth_application_id
├─ type (authorization_code, refresh_token, etc.)
├─ code, token, refresh_token (with hashed versions)
├─ expires_in, revoked_at
├─ scopes, access_type
├─ code_challenge, code_challenge_method (PKCE)
├─ user_code, last_polled_at (Device code grant)
├─ nonce, acr, claims (OIDC)
├─ dpop_jkt (DPoP)
└─ certificate_thumbprint, resource (advanced)
[Optional tables for features you enable]
```
## Feature Comparison Matrix
| Feature | Your Code | Rodauth-OAuth | Effort to Add* |
|---------|-----------|---------------|--------|
| Authorization Code Flow | ✓ | ✓ | N/A |
| PKCE | ✓ | ✓ | N/A |
| Refresh Tokens | ✗ | ✓ | 1-2 weeks |
| Token Revocation | ✗ | ✓ | 1 week |
| Token Introspection | ✗ | ✓ | 1 week |
| Client Credentials Grant | ✗ | ✓ | 2 weeks |
| Device Code Flow | ✗ | ✓ | 3 weeks |
| JWT Access Tokens | ✗ | ✓ | 1 week |
| Session Management | ✗ | ✓ | 2-3 weeks |
| Front-Channel Logout | ✗ | ✓ | 1-2 weeks |
| Back-Channel Logout | ✗ | ✓ | 2 weeks |
| Dynamic Client Reg | ✗ | ✓ | 3-4 weeks |
| Token Hashing | ✗ | ✓ | 1 week |
*Time estimates for adding to your implementation
## Code Examples
### Rodauth-OAuth: Minimal OAuth Server
```ruby
# Gemfile
gem 'roda'
gem 'rodauth-oauth'
gem 'sequel'
# lib/auth_server.rb
class AuthServer < Roda
plugin :sessions, secret: ENV['SESSION_SECRET']
plugin :rodauth do
db DB
enable :login, :logout, :create_account,
:oidc, :oauth_pkce, :oauth_authorization_code_grant,
:oauth_token_revocation
oauth_application_scopes %w[openid email profile]
oauth_require_pkce true
end
route do |r|
r.rodauth # All OAuth endpoints auto-mounted!
# Your app logic here
end
end
```
That's it! All these endpoints are automatically available:
- GET /.well-known/openid-configuration
- GET /.well-known/jwks.json
- GET /oauth/authorize
- POST /oauth/token
- POST /oauth/revoke
- GET /oauth/userinfo
- GET /logout
### Your Current Approach
```ruby
# app/controllers/oidc_controller.rb
class OidcController < ApplicationController
def authorize
# 150 lines of validation logic
end
def token
# 100 lines of token generation logic
end
def userinfo
# 50 lines of claims logic
end
def logout
# 50 lines of logout logic
end
private
def validate_pkce(auth_code, code_verifier)
# 50 lines of PKCE validation
end
end
```
## Integration Paths
### Option 1: Stick with Your Implementation
- Keep building features incrementally
- Effort: 2-3 months to reach feature parity
- Pro: Rails native, full control
- Con: Continuous maintenance burden
### Option 2: Switch to Rodauth-OAuth
- Learn Roda/Rodauth (1-2 weeks)
- Migrate database (1 week)
- Replace 450 lines of code with config (1 week)
- Testing & validation (2-3 weeks)
- Effort: 4-8 weeks total
- Pro: Production-ready, certified, maintained
- Con: Different framework (Roda)
### Option 3: Hybrid Approach
- Keep your Rails app for business logic
- Use rodauth-oauth as separate OAuth/OIDC service
- Services communicate via HTTP/APIs
- Effort: 2-3 weeks (independent services)
- Pro: Best of both worlds
- Con: Operational complexity
## Decision Matrix
### Use Rodauth-OAuth If You Need...
- [x] Standards compliance (OpenID certified)
- [x] Multiple grant types (Client Credentials, Device Code, etc.)
- [x] Token revocation/introspection
- [x] Refresh tokens
- [x] Advanced logout (front/back-channel)
- [x] Session management
- [x] Token hashing/security best practices
- [x] Hands-off maintenance
- [x] Production-battle-tested code
### Keep Your Implementation If You...
- [x] Only need Authorization Code + PKCE
- [x] Want zero Roda/external framework learning
- [x] Value Rails patterns over standards
- [x] Like to understand every line of code
- [x] Can allocate time for ongoing maintenance
- [x] Prefer minimal dependencies
## Key Differences You'll Notice
### 1. Framework Paradigm
- **Your impl**: Rails (MVC, familiar)
- **Rodauth**: Roda (routing-focused, lightweight)
### 2. Database ORM
- **Your impl**: ActiveRecord (Rails native)
- **Rodauth**: Sequel (lighter, more control)
### 3. Configuration Style
- **Your impl**: Rails initializers, environment variables
- **Rodauth**: Plugin block with DSL
### 4. Model Management
- **Your impl**: Rails models with validations, associations
- **Rodauth**: Minimal models, logic in database
### 5. Testing Approach
- **Your impl**: RSpec, model/controller tests
- **Rodauth**: Request-based integration tests
## File Locations (If You Switch)
```
Current Structure
├── app/controllers/oidc_controller.rb
├── app/models/
│ ├── oidc_authorization_code.rb
│ ├── oidc_access_token.rb
│ └── oidc_user_consent.rb
├── app/services/oidc_jwt_service.rb
├── db/migrate/*oidc*.rb
Rodauth-OAuth Equivalent
├── lib/rodauth_app.rb # Configuration (replaces most controllers)
├── app/views/rodauth/ # Templates (consent form, etc.)
├── config/routes.rb # Simple: routes mount rodauth
└── db/migrate/*rodauth_oauth*.rb
```
## Performance Considerations
### Your Implementation
- Small tables → fast queries
- Fewer columns → less overhead
- Simple token validation
- Estimated: 5-10ms per token validation
### Rodauth-OAuth
- More columns, but same queries
- Optional token hashing (slight overhead)
- More features = more options checked
- Estimated: 10-20ms per token validation
- Can be optimized: disable unused features
## Getting Started (If You Want to Explore)
1. **Review the code**
```bash
cd /Users/dkam/Development/clinch/tmp/rodauth-oauth
ls -la lib/rodauth/features/ # See all features
cat examples/oidc/authentication_server.rb # Full working example
```
2. **Run the example**
```bash
cd /Users/dkam/Development/clinch/tmp/rodauth-oauth/examples
ruby oidc/authentication_server.rb # Starts server on http://localhost:9292
```
3. **Read the key files**
- README.md: Overview
- MIGRATION-GUIDE-v1.md: Version migration (shows architecture)
- test/migrate/*.rb: Database schema
- examples/oidc/*.rb: Complete working implementation
## Next Steps
1. **If keeping your implementation:**
- Prioritize refresh token support
- Add token revocation endpoint
- Consider token hashing
2. **If exploring rodauth-oauth:**
- Run the example server
- Review the feature files
- Check if hybrid approach works for your org
3. **For either path:**
- Document your decision
- Plan feature roadmap
- Set up appropriate monitoring
---
**Bottom Line**: Rodauth-OAuth is the "production-grade" option if you need comprehensive OAuth/OIDC. Your implementation is fine if you keep features minimal and have maintenance bandwidth.