Fixed inconsistency between IP-based rate limiting and per-account lockout. Previously, users would hit the IP rate limit at 5 attempts (15 min lockout) but the account wouldn't be marked as locked until 10 attempts (30 min). This caused confusion in the admin UI where locked accounts wouldn't show the unlock button until 10 attempts were reached. Changes: - Reduced account lockout threshold from 10 to 5 failed attempts - Reduced account lockout duration from 30 to 15 minutes - Updated error message to reflect 15 minute lockout period - Added detailed logging when account gets locked - Updated README documentation to reflect correct limits Both protection layers now work in harmony: - IP-based rate limiting: 5 attempts = 15 min lockout - Per-account lockout: 5 attempts = 15 min lock This ensures the admin UI accurately shows account lock status and provides the unlock option as soon as users hit the lockout threshold. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
344 lines
13 KiB
Markdown
344 lines
13 KiB
Markdown
# New Life Christian Church - Sermon Itinerary
|
|
|
|
A web application for managing and displaying weekly sermons for New Life Christian Church.
|
|
|
|
## Features
|
|
|
|
- 📝 **Sermon Management**: Create and manage sermon content with a user-friendly form
|
|
- 🔐 **Enterprise Security**: OWASP-compliant authentication with CSRF protection, session management, and account lockout
|
|
- 👥 **User Management**: Full admin panel for managing users, roles, and account security
|
|
- 🔒 **Password Security**: Bcrypt hashing, strong password requirements, and secure password reset with 8-character alphanumeric codes
|
|
- 🛡️ **Account Protection**: Dual-layer brute force protection (IP-based + per-account lockout)
|
|
- 📱 **QR Codes**: Generate QR codes for easy sermon sharing
|
|
- 📅 **Date-based URLs**: Sermons accessible via `sermon-MMDDYYYY` format
|
|
- 🎨 **Modern UI**: Clean, responsive design using Tailwind CSS and Inter font
|
|
- 📊 **Three Sections**: Bible References, Personal Appliance, and Pastor's Challenge
|
|
- 🗂️ **Smart Organization**: Recent sermons (last 3 months) displayed by default, older sermons in dropdown
|
|
- 📧 **Email Notifications**: Password reset codes and sermon notes via SMTP
|
|
- 🐳 **Docker Ready**: Fully containerized for easy deployment with auto-generated secrets
|
|
|
|
## Technology Stack
|
|
|
|
- **Frontend**: Nuxt 3 (Vue.js)
|
|
- **Styling**: Tailwind CSS with Inter font from Google Fonts
|
|
- **Database**: SQLite
|
|
- **QR Codes**: qrcode library
|
|
- **Deployment**: Docker & Docker Compose
|
|
|
|
## Configuration
|
|
|
|
This application uses environment variables configured directly in `docker-compose.yml`. Edit the file to customize your deployment settings.
|
|
|
|
### Environment Variables
|
|
|
|
| Variable | Description | Default | Required |
|
|
|----------|-------------|---------|----------|
|
|
| `SITE_URL` | Public URL where the app is hosted (used for QR codes) | `https://church.example.com` | Yes |
|
|
| `AUTH_SECRET` | Secret key for authentication sessions | **Auto-generated** | No |
|
|
| `ADMIN_USERNAME` | Initial admin login username | `admin` | No |
|
|
| `ADMIN_PASSWORD` | Initial admin login password | **Auto-generated** | No |
|
|
| `EMAIL_HOST` | SMTP server hostname | `smtp.example.com` | Yes |
|
|
| `EMAIL_PORT` | SMTP server port | `587` | Yes |
|
|
| `EMAIL_USER` | SMTP authentication username | `noreply@example.com` | Yes |
|
|
| `EMAIL_PASSWORD` | SMTP authentication password | Required | Yes |
|
|
| `EMAIL_FROM` | Email sender address and name | `New Life Christian Church <noreply@example.com>` | Yes |
|
|
|
|
**Security Note**: `AUTH_SECRET` and `ADMIN_PASSWORD` are now automatically generated on first launch using cryptographically secure random generation. They are stored in the database and logged once to container logs.
|
|
|
|
### Customizing Configuration
|
|
|
|
Edit `docker-compose.yml` and update the required values:
|
|
|
|
```yaml
|
|
services:
|
|
nlcc-itinerary:
|
|
environment:
|
|
- SITE_URL=https://your-church-domain.com
|
|
# Optional: customize admin username (default: "admin")
|
|
# - ADMIN_USERNAME=your-admin-username
|
|
# Optional: set custom admin password (otherwise auto-generated)
|
|
# - ADMIN_PASSWORD=your-secure-password
|
|
# Email configuration (required for password resets)
|
|
- EMAIL_HOST=smtp.gmail.com
|
|
- EMAIL_PORT=587
|
|
- EMAIL_USER=your-email@gmail.com
|
|
- EMAIL_PASSWORD=your-app-password
|
|
- EMAIL_FROM=Your Church Name <your-email@gmail.com>
|
|
```
|
|
|
|
**Note**: `AUTH_SECRET` is no longer needed in configuration - it's automatically generated and stored in the database on first launch.
|
|
|
|
## Getting Started
|
|
|
|
### Prerequisites
|
|
|
|
- Docker and Docker Compose installed on your system
|
|
|
|
### Installation & Deployment
|
|
|
|
1. Clone the repository:
|
|
```bash
|
|
git clone <repository-url>
|
|
cd nlcc-itinerary
|
|
```
|
|
|
|
2. Edit `docker-compose.yml` and configure your settings:
|
|
- Update `SITE_URL` to your public domain
|
|
- Configure email settings (required for password resets)
|
|
- Optionally set `ADMIN_USERNAME` and `ADMIN_PASSWORD` (otherwise auto-generated)
|
|
|
|
3. Build and run with Docker Compose:
|
|
```bash
|
|
docker-compose up -d --build
|
|
```
|
|
|
|
The Docker volume will be automatically created as `nlcc-itinerary_data`.
|
|
|
|
The application will be available at `http://localhost:3002` (or your configured port)
|
|
|
|
**Important**: The `SITE_URL` must be set correctly for QR codes to work. This should be the public URL where your application is accessible (e.g., `https://church.example.com`).
|
|
|
|
### Initial Admin Account
|
|
|
|
The initial admin account is created automatically on first run with secure auto-generated credentials:
|
|
|
|
**Default behavior**:
|
|
- **Username**: `admin` (can be customized with `ADMIN_USERNAME` env var)
|
|
- **Password**: **Auto-generated** (cryptographically secure 16-character password)
|
|
|
|
**Retrieving Auto-Generated Credentials**:
|
|
```bash
|
|
# View the auto-generated admin password from container logs
|
|
docker logs nlcc-itinerary 2>&1 | grep "ADMIN CREDENTIALS"
|
|
```
|
|
|
|
The output will show:
|
|
```
|
|
╔════════════════════════════════════════════════════════╗
|
|
║ 🔐 ADMIN CREDENTIALS (SAVE THESE!) ║
|
|
╠════════════════════════════════════════════════════════╣
|
|
║ Username: admin ║
|
|
║ Password: <randomly-generated-password> ║
|
|
╚════════════════════════════════════════════════════════╝
|
|
```
|
|
|
|
⚠️ **Important**:
|
|
- Credentials are logged **only once** on first startup
|
|
- Save the credentials immediately - they won't be shown again
|
|
- Alternatively, set `ADMIN_PASSWORD` in `docker-compose.yml` to use a custom password
|
|
- Change admin password after first login via the profile page
|
|
- Additional users and admins can be created through the user management interface
|
|
|
|
## Project Structure
|
|
|
|
```
|
|
nlcc-itinerary/
|
|
├── assets/css/ # Global styles
|
|
├── components/ # Vue components
|
|
│ ├── SermonCard.vue
|
|
│ ├── QRCodeButton.vue
|
|
│ └── QRCodeModal.vue
|
|
├── middleware/ # Route middleware
|
|
│ └── auth.ts
|
|
├── pages/ # Application pages
|
|
│ ├── index.vue # Main sermon listing
|
|
│ ├── login.vue # Login page
|
|
│ ├── register.vue # User registration
|
|
│ ├── admin.vue # Sermon creation form
|
|
│ ├── users.vue # User management (admin only)
|
|
│ ├── profile.vue # User profile settings
|
|
│ └── [slug].vue # Individual sermon page
|
|
├── plugins/ # Nuxt plugins
|
|
│ └── csrf.client.ts # CSRF auto-injection
|
|
├── server/
|
|
│ ├── api/ # API endpoints
|
|
│ │ ├── auth/ # Authentication endpoints
|
|
│ │ ├── sermons/ # Sermon CRUD endpoints
|
|
│ │ ├── users/ # User management endpoints
|
|
│ │ └── profile/ # Profile management
|
|
│ ├── middleware/ # Server middleware
|
|
│ │ └── csrf.ts # CSRF validation
|
|
│ └── utils/ # Server utilities
|
|
│ ├── database.ts # SQLite database functions
|
|
│ ├── auth.ts # Authentication helpers
|
|
│ ├── csrf.ts # CSRF protection utilities
|
|
│ └── email.ts # Email sending functions
|
|
├── logos/ # Church logos
|
|
├── Dockerfile # Docker configuration
|
|
├── docker-compose.yml # Docker Compose configuration
|
|
└── nuxt.config.ts # Nuxt configuration
|
|
```
|
|
|
|
## Usage
|
|
|
|
### Creating a Sermon
|
|
|
|
1. Navigate to `/login` and sign in with admin credentials
|
|
2. You'll be redirected to `/admin`
|
|
3. Fill in the sermon details:
|
|
- **Date**: Select the sermon date (URL will be auto-generated as `sermon-MMDDYYYY`)
|
|
- **Title**: Enter the sermon title
|
|
- **Bible References**: Add one or more Bible verses (use +/- buttons)
|
|
- **Personal Appliance**: Enter personal application content
|
|
- **Pastor's Challenge**: Enter the pastor's challenge content
|
|
4. Click "Create Sermon"
|
|
|
|
### Viewing Sermons
|
|
|
|
- **Main Page** (`/`): Shows recent sermons (last 3 months) with option to view older ones
|
|
- **Individual Sermon** (`/sermon-MMDDYYYY`): Full sermon details with QR code
|
|
- **QR Code**: Click the QR code button on any sermon card or page to generate a scannable code
|
|
|
|
## Database
|
|
|
|
The application uses SQLite with the following schema:
|
|
|
|
### Sermons Table
|
|
- `id`: Primary key
|
|
- `slug`: Unique sermon identifier (e.g., sermon-09282025)
|
|
- `title`: Sermon title
|
|
- `date`: Sermon date
|
|
- `bible_references`: Newline-separated Bible verses
|
|
- `personal_appliance`: Personal application content
|
|
- `pastors_challenge`: Pastor's challenge content
|
|
- `created_at`: Timestamp
|
|
|
|
### Users Table
|
|
- `id`: Primary key
|
|
- `username`: User's username (unique)
|
|
- `password`: User's password (bcrypt hashed)
|
|
- `first_name`: User's first name
|
|
- `last_name`: User's last name
|
|
- `email`: User's email address
|
|
- `is_admin`: Admin flag (0 or 1)
|
|
- `failed_login_attempts`: Failed login counter
|
|
- `locked_until`: Account lock expiration timestamp
|
|
- `created_at`: Account creation timestamp
|
|
|
|
### Sessions Table
|
|
- `id`: Primary key
|
|
- `token`: Session token (unique)
|
|
- `username`: Associated username
|
|
- `expires_at`: Session expiration timestamp
|
|
- `csrf_token`: CSRF token for request validation
|
|
- `created_at`: Session creation timestamp
|
|
|
|
### Settings Table
|
|
- `key`: Setting key (primary)
|
|
- `value`: Setting value
|
|
- `created_at`: Creation timestamp
|
|
- `updated_at`: Last update timestamp
|
|
|
|
## Security Features
|
|
|
|
This application implements enterprise-grade security following OWASP best practices:
|
|
|
|
### ✅ Implemented Security Features
|
|
|
|
1. **Auto-Generated Secrets**
|
|
- `AUTH_SECRET` automatically generated using cryptographic randomness
|
|
- Admin password auto-generated with 16 characters (uppercase, lowercase, numbers, symbols)
|
|
- All secrets stored securely in database
|
|
|
|
2. **Password Security**
|
|
- Bcrypt hashing with 10 salt rounds
|
|
- Strong password requirements (8+ chars, uppercase, lowercase, number/symbol)
|
|
- Secure password reset with 8-character alphanumeric codes (1.8 trillion combinations)
|
|
- Session invalidation on password changes
|
|
|
|
3. **CSRF Protection**
|
|
- Double-submit cookie pattern implementation
|
|
- Automatic token injection on all API requests
|
|
- Three-way validation (cookie, header, session database)
|
|
- Zero configuration needed in components
|
|
|
|
4. **Session Management**
|
|
- Secure session tokens with HTTP-only cookies
|
|
- Automatic session expiration (24 hours)
|
|
- Session invalidation on security events (password changes, admin resets)
|
|
- Protection against session fixation attacks
|
|
|
|
5. **Account Lockout (Dual-Layer Brute Force Protection)**
|
|
- IP-based rate limiting: 5 failed attempts = 15 minute lockout
|
|
- Per-account lockout: 5 failed attempts = 15 minute lock
|
|
- Automatic unlock after expiration
|
|
- Admin manual unlock capability via UI
|
|
|
|
6. **User Management**
|
|
- Role-based access control (admin/user)
|
|
- Admin dashboard for user management
|
|
- Account lock status visibility
|
|
- Password reset functionality
|
|
|
|
### 🔒 Production Recommendations
|
|
|
|
1. ✅ Strong passwords - Enforced by application
|
|
2. ✅ Password hashing - Bcrypt with salt rounds
|
|
3. ✅ CSRF protection - Implemented
|
|
4. ✅ Session security - HTTP-only cookies with expiration
|
|
5. ✅ Account lockout - Dual-layer protection
|
|
6. ✅ Secrets management - Auto-generated and stored securely
|
|
7. **Enable HTTPS** - Configure reverse proxy (nginx/Caddy)
|
|
8. **Regular backups** - Backup `/app/data` directory
|
|
9. **Email security** - Use app-specific passwords for SMTP
|
|
10. **Monitor logs** - Review failed login attempts
|
|
|
|
### 🛡️ Security Compliance
|
|
|
|
- **OWASP Top 10 2021**: Addressed broken authentication, injection, security misconfiguration
|
|
- **Password Storage**: NIST-compliant bcrypt hashing
|
|
- **CSRF Protection**: Industry-standard double-submit pattern
|
|
- **Session Security**: Secure cookie attributes and expiration
|
|
- **Brute Force Protection**: Account lockout + rate limiting
|
|
|
|
## Docker Commands
|
|
|
|
```bash
|
|
# Build and start
|
|
docker-compose up -d
|
|
|
|
# View logs
|
|
docker-compose logs -f
|
|
|
|
# Stop containers
|
|
docker-compose down
|
|
|
|
# Rebuild after changes
|
|
docker-compose up -d --build
|
|
|
|
# Access container shell
|
|
docker exec -it nlcc-itinerary sh
|
|
```
|
|
|
|
## Data Persistence
|
|
|
|
The SQLite database is stored in a Docker named volume (`nlcc-itinerary_data`) which is mounted to `/app/data` in the container. This ensures:
|
|
- Data persists across container restarts and rebuilds
|
|
- Better portability across different environments
|
|
- Easier backup and restore operations
|
|
- Managed by Docker volume system
|
|
- Automatic creation on first `docker-compose up`
|
|
|
|
**Volume Management Commands**:
|
|
```bash
|
|
# Inspect volume location and details
|
|
docker volume inspect nlcc-itinerary_data
|
|
|
|
# Backup the database
|
|
docker run --rm -v nlcc-itinerary_data:/data -v $(pwd):/backup alpine tar czf /backup/nlcc-backup.tar.gz -C /data .
|
|
|
|
# Restore from backup
|
|
docker run --rm -v nlcc-itinerary_data:/data -v $(pwd):/backup alpine tar xzf /backup/nlcc-backup.tar.gz -C /data
|
|
|
|
# Remove volume (WARNING: deletes all data!)
|
|
docker volume rm nlcc-itinerary_data
|
|
```
|
|
|
|
## License
|
|
|
|
This project is created for New Life Christian Church.
|
|
|
|
## Support
|
|
|
|
For issues or questions, please contact the development team.
|