# Jitsi JWT Authentication Implementation ## Overview The system now uses JWT (JSON Web Token) authentication for Jitsi meetings, providing proper moderator privileges and secure meeting access. This replaces the previous URL parameter approach with industry-standard JWT tokens. ## How It Works ### JWT Token Structure Each meeting link includes a JWT token that contains: ```json { "context": { "user": { "name": "John Doe", "email": "john@example.com", "moderator": "true" // "true" for admin, "false" for regular users } }, "room": "booking-123-1234567890-abc123", "aud": "jitsi", "iss": "your-app-id", "sub": "your-jitsi-domain.com", "exp": 1234567890 // 24 hour expiry } ``` ### Meeting URL Format **With JWT (Recommended):** ``` https://meet.jit.si/booking-123-abc?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` **Fallback (No JWT configured):** ``` https://meet.jit.si/booking-123-abc#userInfo.displayName="John Doe" ``` ## Configuration ### Option 1: Public Jitsi (meet.jit.si) For testing or using public Jitsi servers, leave JWT configuration empty: ```env JITSI_BASE_URL=https://meet.jit.si JITSI_API_KEY= JITSI_APP_ID= ``` **Note:** Public Jitsi doesn't enforce JWT authentication, so the system will fall back to URL parameters. Moderator privileges won't work properly on public servers. ### Option 2: Self-Hosted Jitsi with JWT For production with proper moderator control, configure your self-hosted Jitsi: ```env JITSI_BASE_URL=https://meet.yourdomain.com JITSI_API_KEY=your_jwt_secret_key_here JITSI_APP_ID=your_app_id_here ``` ## Setting Up Self-Hosted Jitsi with JWT ### 1. Install Jitsi Meet Follow the official guide: https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-quickstart ### 2. Enable JWT Authentication Edit `/etc/prosody/conf.avail/yourdomain.com.cfg.lua`: ```lua VirtualHost "yourdomain.com" authentication = "token" app_id = "your_app_id" app_secret = "your_jwt_secret_key" allow_empty_token = false ``` ### 3. Configure Jicofo Edit `/etc/jitsi/jicofo/sip-communicator.properties`: ```properties org.jitsi.jicofo.auth.URL=XMPP:yourdomain.com ``` ### 4. Restart Services ```bash systemctl restart prosody systemctl restart jicofo systemctl restart jitsi-videobridge2 ``` ### 5. Update Your .env File ```env JITSI_BASE_URL=https://meet.yourdomain.com JITSI_API_KEY=your_jwt_secret_key JITSI_APP_ID=your_app_id ``` ## API Response Examples ### GET /api/bookings ```json { "bookings": [ { "id": 123, "jitsi_room_id": "booking-123-1234567890-abc123", "jitsi_room_url": "https://meet.jit.si/booking-123-1234567890-abc123", "personalized_meeting_url": "https://meet.jit.si/booking-123-1234567890-abc123?jwt=eyJhbGc..." } ] } ``` ### GET /api/admin/bookings ```json { "bookings": [ { "id": 123, "jitsi_room_id": "booking-123-1234567890-abc123", "jitsi_room_url": "https://meet.jit.si/booking-123-1234567890-abc123", "admin_meeting_url": "https://meet.jit.si/booking-123-1234567890-abc123?jwt=eyJhbGc..." } ] } ``` ### GET /api/meetings/:id/link ```json { "booking_id": 123, "meeting_url": "https://meet.jit.si/booking-123-1234567890-abc123?jwt=eyJhbGc...", "display_name": "John Doe", "is_admin": false } ``` ## User vs Admin Differences ### Regular User Token ```json { "context": { "user": { "name": "John Doe", "email": "john@example.com", "moderator": "false" } } } ``` **Permissions:** - Can join meeting - Can share screen - Can mute/unmute themselves - Cannot kick participants - Cannot end meeting for all ### Admin User Token ```json { "context": { "user": { "name": "Dr. Smith", "email": "dr.smith@example.com", "moderator": "true" } } } ``` **Permissions:** - All regular user permissions - Can kick participants - Can mute other participants - Can end meeting for all - Can start/stop recording (if configured) - Can enable/disable lobby ## Email Notifications All email notifications now include JWT-authenticated links: ### Meeting Info Email ```html Join Meeting ``` ### Reminder Email ```html Join Meeting ``` ## Security Features ### Token Expiration - Tokens expire after 24 hours - Users must request a new link after expiration - Prevents unauthorized access to old meetings ### Moderator Control - Only users with `is_admin: true` get moderator tokens - Moderator status is cryptographically signed in JWT - Cannot be tampered with by users ### Room Isolation - Each booking gets a unique room ID - Room name is embedded in JWT - Token only works for the specified room ## Testing ### Test JWT Generation ```bash # Start the server go run cmd/server/main.go # Get a meeting link as regular user curl -H "Authorization: Bearer " \ http://localhost:8080/api/meetings/123/link # Get a meeting link as admin curl -H "Authorization: Bearer " \ http://localhost:8080/api/admin/bookings ``` ### Verify JWT Token Use https://jwt.io to decode and verify your tokens: 1. Copy the JWT from the meeting URL 2. Paste into jwt.io debugger 3. Verify the payload contains correct user info 4. Check moderator field is "true" for admins ### Test Moderator Privileges 1. Create two bookings 2. Join one as regular user 3. Join another as admin 4. Verify admin can: - See moderator controls - Kick participants - End meeting for all ## Troubleshooting ### Issue: "Room locked" or "Authentication failed" **Cause:** JWT not configured or invalid **Solution:** 1. Check `JITSI_API_KEY` and `JITSI_APP_ID` are set 2. Verify they match your Jitsi server configuration 3. Ensure Jitsi server has JWT authentication enabled ### Issue: Admin doesn't have moderator privileges **Cause:** JWT not being used or moderator claim not set **Solution:** 1. Verify `JITSI_API_KEY` is configured 2. Check admin user has `is_admin: true` in database 3. Decode JWT token and verify `moderator: "true"` ### Issue: Token expired error **Cause:** JWT token older than 24 hours **Solution:** 1. Request a new meeting link 2. Tokens are generated fresh on each API call 3. Consider reducing expiry time if needed ### Issue: Fallback to URL parameters **Cause:** JWT configuration missing **Solution:** 1. This is expected behavior when JWT not configured 2. For production, configure JWT authentication 3. URL parameters work but don't enforce moderator privileges ## Migration from URL Parameters ### Before (URL Parameters) ``` https://meet.jit.si/room#userInfo.displayName="John"&config.startWithAudioMuted=false ``` **Problems:** - No real moderator enforcement - Anyone can modify URL parameters - No security ### After (JWT Authentication) ``` https://meet.jit.si/room?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` **Benefits:** - Cryptographically signed tokens - True moderator enforcement - Secure and tamper-proof - Industry standard ## Best Practices 1. **Use Self-Hosted Jitsi**: For production, always use self-hosted Jitsi with JWT 2. **Rotate Secrets**: Regularly rotate your `JITSI_API_KEY` 3. **Monitor Token Usage**: Log token generation for audit purposes 4. **Set Appropriate Expiry**: 24 hours is reasonable, adjust based on needs 5. **Test Moderator Controls**: Regularly verify admin privileges work correctly ## Advanced Configuration ### Custom Token Expiry Edit `internal/services/jitsi_service.go`: ```go ExpiresAt: jwt.NewNumericDate(time.Now().Add(2 * time.Hour)), // 2 hour expiry ``` ### Additional JWT Claims Add custom claims to the JWT: ```go type JitsiClaims struct { Context JitsiContext `json:"context"` Room string `json:"room"` Avatar string `json:"avatar,omitempty"` // User avatar URL jwt.RegisteredClaims } ``` ### Multiple Jitsi Servers Support multiple Jitsi servers by environment: ```go func (j *jitsiService) getJitsiURL() string { if os.Getenv("ENVIRONMENT") == "production" { return "https://meet.production.com" } return "https://meet.staging.com" } ``` ## References - [Jitsi JWT Documentation](https://jitsi.github.io/handbook/docs/devops-guide/devops-guide-docker#authentication) - [JWT.io](https://jwt.io) - [Jitsi Meet API](https://jitsi.github.io/handbook/docs/dev-guide/dev-guide-iframe)