Invitation System
Overview
The invitation system provides a flexible authentication mechanism that allows communities to control user registration. It supports two modes:
- Open Registration Mode: Anyone can register without restrictions
- Invite-Only Mode: New users require an invitation code to register
The system includes a waitlist feature for users when invite-only mode is enabled, email notifications, and comprehensive management tools.
Key Features
For Administrators
- Toggle between open and invite-only registration
- Monitor waitlist entries
- View system-wide invitation statistics
For Users
- Generate invitation codes
- Share invitations via QR code, email, SMS, or direct links
- Manage sent invitations (view status, revoke pending invitations)
- Track invitation acceptance
For New Users
- Join waitlist when registration is restricted
- Register with valid invitation codes
- Receive clear messaging about registration requirements
Architecture
Backend Components
Models
Invitation(backend/app/Models/Invitation.php): Core invitation model with relationships to inviter and recipient usersWaitlistEntry(backend/app/Models/WaitlistEntry.php): Manages users waiting for invitationsSettings(backend/app/Models/Settings.php): Key-value store for system configuration
Services
InvitationService(backend/app/Services/InvitationService.php): Business logic for invitation generation, validation, and acceptanceWaitlistService(backend/app/Services/WaitlistService.php): Manages waitlist operationsSettingsService(backend/app/Services/SettingsService.php): Handles system settings access and caching
Controllers
InvitationController(backend/app/Http/Controllers/InvitationController.php): API endpoints for invitation managementWaitlistController(backend/app/Http/Controllers/WaitlistController.php): Waitlist join and managementSettingsController(backend/app/Http/Controllers/SettingsController.php): Public settings endpoint
Notifications
InvitationToEmail(backend/app/Notifications/InvitationToEmail.php): Email notifications for direct invitationsWaitlistConfirmation(backend/app/Notifications/WaitlistConfirmation.php): Confirmation emails for waitlist entries
Database
- Migrations:
create_invitations_table.php- Stores invitation codes and statuscreate_waitlist_entries_table.php- Manages waitlistcreate_settings_table.php- System configuration
Frontend Components
API Layer
invite-system.ts(frontend/src/api/invite-system.ts): TypeScript API client for all invitation endpoints
Components
WaitlistForm(frontend/src/components/WaitlistForm.tsx): Join waitlist formInvitationShare(frontend/src/components/InvitationShare.tsx): Share invitations via multiple channelsInvitationQRCode(frontend/src/components/InvitationQRCode.tsx): Generate and download QR codes
Pages
InvitationsPage(frontend/src/pages/invitations/InvitationsPage.tsx): Dashboard for managing invitations
Hooks
useInviteSystem(frontend/src/hooks/use-invite-system.ts): React hook for registration mode detection
API Endpoints
Public Endpoints
Get Public Settings
GET /api/settings/publicResponse:
{
"data": {
"invite_only_enabled": false
}
}Join Waitlist
POST /api/waitlist
Content-Type: application/json
{
"email": "user@example.com"
}Response (201 Created):
{
"data": {
"email": "user@example.com",
"status": "pending",
"created_at": "2024-01-01T00:00:00Z"
}
}Error Responses:
409 Conflict- Email already on waitlist or registered422 Unprocessable Entity- Validation errors
Validate Invitation Code
POST /api/invitations/validate
Content-Type: application/json
{
"code": "abc123xyz"
}Response:
{
"data": {
"valid": true,
"inviter": {
"name": "John Doe"
},
"expires_at": null
}
}Authenticated Endpoints
Generate Invitation
POST /api/invitations
Authorization: Bearer {token}
Content-Type: application/json
{
"email": "recipient@example.com", // optional
"expires_at": "2024-12-31T23:59:59Z" // optional
}Response (201 Created):
{
"data": {
"id": 1,
"code": "abc123xyz",
"status": "pending",
"expires_at": null,
"invitation_url": "https://example.com/register?invitation_code=abc123xyz",
"created_at": "2024-01-01T00:00:00Z",
"recipient": null
}
}Rate Limiting: 10/hour endpoint throttle plus 10/day business limit per user (revoked invitations excluded from daily count)
List User Invitations
GET /api/invitations
Authorization: Bearer {token}Response:
{
"data": [
{
"id": 1,
"code": "abc123xyz",
"status": "pending",
"expires_at": null,
"invitation_url": "https://example.com/register?invitation_code=abc123xyz",
"created_at": "2024-01-01T00:00:00Z",
"recipient": null
},
{
"id": 2,
"code": "def456abc",
"status": "accepted",
"expires_at": null,
"invitation_url": "https://example.com/register?invitation_code=def456abc",
"created_at": "2024-01-02T00:00:00Z",
"recipient": {
"id": 2,
"name": "Jane Doe",
"email": "jane@example.com"
}
}
]
}Revoke Invitation
DELETE /api/invitations/{id}
Authorization: Bearer {token}Response (200 OK):
{
"data": []
}Error Responses:
404 Not Found- Invitation not found or cannot be revoked
Get Invitation Statistics
GET /api/invitations/stats
Authorization: Bearer {token}Response:
{
"data": {
"total": 10,
"pending": 3,
"accepted": 5,
"expired": 1,
"revoked": 1
}
}Registration Integration
Authentication Flow
The invitation system integrates with the registration process through the RegisterRequest validation:
// When invite-only mode is enabled
if (Settings::isInviteOnlyEnabled()) {
$rules['invitation_code'] = ['required', new ValidInvitationCode()];
}Frontend Registration Detection
The useInviteSystem hook automatically detects the registration mode:
const { mode, invitationCode, invitationValidation, error } = useInviteSystem();
// mode can be:
// - 'open-registration': Anyone can register
// - 'invite-only-no-code': Restricted, show waitlist
// - 'invite-only-with-code': Restricted with valid codeRegistration Modes
Open Registration Mode
- Standard registration form
- Optional invitation code can still be provided
- No restrictions on new users
Invite-Only Mode (No Code)
- Registration form is hidden
- Waitlist form is displayed prominently
- Clear messaging about registration requirements
- Users can request invitations from existing members
Invite-Only Mode (With Valid Code)
- Registration form is displayed
- Invitation code is pre-filled from URL parameter
- Shows inviter information
- Validates code before allowing registration
Email Notifications
Invitation Email
Sent when a user generates an invitation to a specific email address:
Subject: You're invited to join Meo Mai Moi!
Content:
- Personalized greeting with inviter name
- Call-to-action button with invitation link
- Invitation code for manual entry
- Expiration information (if applicable)
Waitlist Confirmation Email
Sent when a user joins the waitlist:
Subject: You're on the waitlist!
Content:
- Confirmation of waitlist entry
- What to expect next
- Alternative options (ask existing members)
Email Templates
Custom email templates are located in:
backend/resources/views/vendor/mail/html/- HTML templatesbackend/resources/views/vendor/mail/text/- Plain text templates
Configuration
Environment Variables
# Enable invite-only mode (default: false)
INVITE_ONLY_ENABLED=false
# Email configuration for notifications
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=noreply@example.com
MAIL_FROM_NAME="${APP_NAME}"Settings Management
Settings are stored in the settings table and cached for performance:
// Get setting
$inviteOnly = Settings::get('invite_only_enabled', 'false');
// Set setting
Settings::set('invite_only_enabled', 'true');
// Toggle setting
$newValue = Settings::toggle('invite_only_enabled');
// Helper method
$isEnabled = Settings::isInviteOnlyEnabled();Rate Limiting
Invitation generation is rate-limited to prevent abuse:
- User limit: 10 invitations per day (revoked invitations are excluded from this count)
- Endpoint throttle: 10 requests/hour on creation, 20 requests/hour on revocation, plus the group-level
authenticatedlimiter
See Rate Limiting for the full API rate limiting reference.
Testing
Backend Tests
Feature Tests
InvitationControllerTest: API endpoint testingInviteSystemAuthTest: Registration flow with invitationsInviteSystemIntegrationTest: End-to-end workflowsSettingsControllerTest: Public settings endpointWaitlistControllerTest: Waitlist management
Unit Tests
InvitationServiceTest: Service layer logicInvitationTest: Model behavior and scopesSettingsTest: Settings model methodsSettingsServiceTest: Service layer cachingWaitlistEntryTest: Model validationWaitlistServiceTest: Waitlist operations
Running Tests
cd backend
./vendor/bin/phpunit --testsuite=Feature --filter=Invitation
./vendor/bin/phpunit --testsuite=Unit --filter=InvitationFrontend Tests
Component Tests
WaitlistForm.test.tsx: Waitlist form behaviorInvitationQRCode.test.tsx: QR code generationInvitationShare.test.tsx: Share functionality
Page Tests
InvitationsPage.test.tsx: Invitation management UI
Hook Tests
use-invite-system.test.ts: Registration mode detection
Running Tests
cd frontend
bun testUser Workflows
Workflow 1: User Joins Waitlist and Gets Invited
User visits registration page
- System detects invite-only mode is enabled
- Waitlist form is displayed
User joins waitlist
- Enters email address
- Receives confirmation email
- Status: "pending"
Existing user invites from waitlist
- Navigates to invitations page
- Generates invitation for waitlist email
- System sends invitation email
- Waitlist status changes to "invited"
User registers with invitation
- Clicks link in email
- Registration form appears with code pre-filled
- Completes registration
- Invitation status changes to "accepted"
Workflow 2: Direct Invitation
User generates invitation
- Navigates to invitations page
- Clicks "Generate Invitation"
- Receives invitation link
User shares invitation
- Copies link directly
- Or shares via QR code
- Or sends via email/SMS through share dialog
Recipient registers
- Opens invitation link
- Completes registration
- Invitation is marked as accepted
Workflow 3: Open Registration with Optional Invitation
User visits registration page
- Normal registration form is displayed
- No invitation code required
User optionally uses invitation code
- Enters invitation code (optional)
- Registration proceeds normally
- If code is provided, it's tracked
Security Considerations
Invitation Code Generation
- Codes are 32-character random strings
- Uniqueness is enforced at database level
- Codes are non-sequential and unpredictable
Validation Rules
- Invitation codes must be pending status
- Codes must not be expired
- Each code can only be used once
- Users can only revoke their own invitations
Rate Limiting
- Invitation generation limited per user
- API endpoints have standard throttling
- Waitlist submissions are rate-limited
Authorization
- Only authenticated users can generate invitations
- Users can only view and manage their own invitations
- Public endpoints only expose necessary information
Data Protection
- Email addresses in waitlist are validated
- Sensitive data is not exposed in public endpoints
- Settings cache is cleared on updates
Troubleshooting
Common Issues
Invitation Codes Not Working
Symptoms: Invalid code error during registration
Solutions:
- Check invitation status (must be "pending")
- Verify expiration date hasn't passed
- Ensure invite-only mode is actually enabled
- Clear settings cache:
php artisan cache:clear
Emails Not Sending
Symptoms: No confirmation emails received
Solutions:
- Verify mail configuration in
.env - Check mail logs:
storage/logs/laravel.log - Test mail connection:
php artisan tinkerphpMail::raw('Test', function($msg) { $msg->to('test@example.com')->subject('Test'); });
Frontend Not Detecting Invite Mode
Symptoms: Wrong form displayed
Solutions:
- Check
/api/settings/publicendpoint response - Verify settings in database
- Clear browser cache and cookies
- Check console for JavaScript errors
Database Issues
Symptoms: Migration errors
Solutions:
- Run migrations:
php artisan migrate - Check database connection
- Verify permissions on settings table
Future Enhancements
Potential improvements for the invitation system:
- Bulk Invitations: Generate multiple invitations at once
- Invitation Templates: Custom messages for invitations
- Invitation Groups: Tag invitations for organizational purposes
- Analytics Dashboard: Track invitation conversion rates
- Automatic Expiration: Background job to expire old invitations
- Invitation Limits: Per-user invitation quotas
- Waitlist Priority: Allow admins to prioritize waitlist entries
- Social Sharing: Direct integration with social media platforms
- Referral Tracking: Track which users bring in the most new members
- Invitation History: Audit log for invitation activities
Related Documentation
- Development Guide - Local setup and development workflow
- Deployment Guide - Production deployment instructions
- API Documentation - Complete API reference (if available)
Support
For issues or questions about the invitation system:
- Check this documentation first
- Review the test files for usage examples
- Check the GitHub issues
- Create a new issue with detailed information