backend-service/docs/JITSI_JWT_AUTHENTICATION.md

373 lines
8.3 KiB
Markdown
Raw Permalink Normal View History

# 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
<a href="https://meet.jit.si/booking-123?jwt=eyJhbGc...">Join Meeting</a>
```
### Reminder Email
```html
<a href="https://meet.jit.si/booking-123?jwt=eyJhbGc...">Join Meeting</a>
```
## 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 <user_token>" \
http://localhost:8080/api/meetings/123/link
# Get a meeting link as admin
curl -H "Authorization: Bearer <admin_token>" \
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)