Cal.com Security Testing: Default Credentials, API Key, Database, and .env NEXTAUTH_SECRET

Cal.com is a widely deployed open-source scheduling platform (Calendly alternative) built on Next.js, used by businesses and individuals to manage meeting bookings, availability, and calendar integrations — it stores attendee data, business meeting schedules, and payment information. Key assessment areas: NEXTAUTH_SECRET in .env is used by NextAuth.js to sign JWT session tokens, and its compromise enables forging sessions as any Cal.com user; GOOGLE_CLIENT_SECRET authenticates the Google OAuth app for calendar and SSO integration; STRIPE_PRIVATE_KEY provides live Stripe API access; DATABASE_URL contains the PostgreSQL connection string; and the Cal.com API allows enumerating all bookings and user accounts. This guide covers systematic Cal.com security assessment.

Table of Contents

  1. Authentication and API Key Testing
  2. API Booking and User Enumeration
  3. .env Credential Extraction: NEXTAUTH, Google, Stripe, Database
  4. Cal.com Security Hardening

Authentication and API Key Testing

# Cal.com — authentication and API key testing
CALCOM_URL="https://cal.example.com"

# Cal.com API key — generated in Settings > Security > API Keys
API_KEY="cal_live_your_api_key_here"

# Verify API key and get current user
curl -s "${CALCOM_URL}/api/v1/me" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if 'user' in d:
    u = d['user']
    print(f'Authenticated: {u.get(\"name\")} ({u.get(\"email\")})')
    print(f'Role: {u.get(\"role\")} id={u.get(\"id\")}')
else:
    print(f'Failed: {d}')
" 2>/dev/null

# Test admin-level API keys — look for endpoints that require admin scope
# Cal.com API key scopes vary by key type
curl -s "${CALCOM_URL}/api/v1/users" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
if 'users' in d:
    users = d.get('users',[])
    print(f'Users: {len(users)}')
    for u in users[:5]:
        print(f'  [{u.get(\"id\")}] {u.get(\"name\")} email={u.get(\"email\")} role={u.get(\"role\")}')
else:
    print(f'Response: {str(d)[:200]}')
" 2>/dev/null

# Check for .env exposure via Next.js config endpoints
curl -s "${CALCOM_URL}/api/auth/csrf" 2>/dev/null | python3 -c "
import json,sys
print(json.load(sys.stdin))
" 2>/dev/null

API Booking and User Enumeration

# Cal.com REST API — booking and user enumeration
CALCOM_URL="https://cal.example.com"
API_KEY="cal_live_your_api_key_here"

# List all bookings — all scheduled meetings with attendee data
curl -s "${CALCOM_URL}/api/v1/bookings" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
bookings = d.get('bookings',[])
print(f'Bookings: {len(bookings)}')
for b in bookings[:5]:
    attendees = b.get('attendees',[])
    print(f'  [{b.get(\"id\")}] {b.get(\"title\")} start={str(b.get(\"startTime\",\"\"))[:19]}')
    for a in attendees:
        print(f'    Attendee: {a.get(\"name\")} ({a.get(\"email\")})')
" 2>/dev/null

# List all event types
curl -s "${CALCOM_URL}/api/v1/event-types" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
event_types = d.get('event_types',[])
print(f'Event types: {len(event_types)}')
for e in event_types[:10]:
    print(f'  [{e.get(\"id\")}] {e.get(\"title\")} length={e.get(\"length\")}min slug={e.get(\"slug\")}')
" 2>/dev/null

# List all schedules (availability)
curl -s "${CALCOM_URL}/api/v1/schedules" \
  -H "Authorization: Bearer ${API_KEY}" 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
schedules = d.get('schedules',[])
print(f'Schedules: {len(schedules)}')
for s in schedules[:5]:
    print(f'  [{s.get(\"id\")}] {s.get(\"name\")} timeZone={s.get(\"timeZone\")} isDefault={s.get(\"isDefault\")}')
" 2>/dev/null

.env Credential Extraction: NEXTAUTH, Google, Stripe, Database

# Cal.com .env credential extraction

# .env file — all application secrets
cat /app/.env 2>/dev/null | grep -E "NEXTAUTH|GOOGLE|STRIPE|DATABASE|SMTP|ZOOM|TWILIO|SECRET"
# NEXTAUTH_SECRET — Next.js/NextAuth.js session JWT signing key
# GOOGLE_CLIENT_SECRET — Google OAuth2 client secret (Calendar + SSO)
# STRIPE_PRIVATE_KEY — Stripe live secret key (sk_live_...)
# DATABASE_URL — PostgreSQL connection string
# EMAIL_SERVER_PASSWORD — SMTP password for transactional email
# ZOOM_CLIENT_SECRET — Zoom OAuth integration secret

# Docker deployment
docker inspect calcom 2>/dev/null | python3 -c "
import json,sys
d=json.load(sys.stdin)
for c in d:
    for e in c.get('Config',{}).get('Env',[]):
        if any(k in e for k in ['NEXTAUTH','GOOGLE','STRIPE','DATABASE','SECRET','ZOOM']):
            print(e)
" 2>/dev/null

# PostgreSQL direct access — users and credentials
DATABASE_URL="postgresql://cal:PASSWORD@localhost/cal"
psql "$DATABASE_URL" 2>/dev/null << 'EOF'
-- All Cal.com users with email and role
SELECT id, name, email, role, bio, "createdDate",
       "identityProvider", "twoFactorEnabled"
FROM "User"
ORDER BY role, "createdDate" DESC
LIMIT 20;
EOF

# credential_keys — OAuth tokens for integrations (Google Calendar, Zoom, etc.)
psql "$DATABASE_URL" 2>/dev/null << 'EOF'
SELECT ck.id, ck."userId", u.email, ck.type,
       ck.key::text  -- Contains access_token, refresh_token, expiry_date
FROM "CredentialKey" ck
JOIN "User" u ON ck."userId" = u.id
ORDER BY u.email
LIMIT 20;
EOF
# key column: JSON with access_token and refresh_token for Google Calendar, Zoom, etc.

# API keys table
psql "$DATABASE_URL" 2>/dev/null << 'EOF'
SELECT ak.id, ak."userId", u.email, ak."hashedKey", ak."note",
       ak."createdAt", ak."lastUsedAt", ak."expiresAt"
FROM "ApiKey" ak
JOIN "User" u ON ak."userId" = u.id
ORDER BY ak."lastUsedAt" DESC
LIMIT 20;
EOF

Cal.com Security Hardening

Cal.com Security Hardening Checklist:
Security TestMethodRisk
NEXTAUTH_SECRET extraction and session token forgeryRead .env NEXTAUTH_SECRET — NextAuth.js JWT signing key; forge valid session cookies as any Cal.com user bypassing OAuth authentication entirelyCritical
API booking and attendee enumerationGET /api/v1/bookings — all scheduled meetings with attendee names and emails; complete view of all business meetings with external partiesHigh
CredentialKey table OAuth token extractionPostgreSQL SELECT from CredentialKey — access_token and refresh_token for Google Calendar, Zoom, etc.; enables reading/writing user calendars and creating unauthorized Zoom meetingsCritical
STRIPE_PRIVATE_KEY extraction and payment API accessRead .env STRIPE_PRIVATE_KEY — Stripe live secret key; direct Stripe API access for listing customers, creating charges, processing refunds, and modifying subscriptionsCritical
Google OAuth credential extractionRead .env GOOGLE_CLIENT_SECRET — OAuth application secret; enables authorization code interception and impersonation of the Cal.com Google OAuth applicationCritical

Automate Cal.com Security Testing

Ironimo tests Cal.com deployments for NEXTAUTH_SECRET extraction and JWT session token forgery, STRIPE_PRIVATE_KEY live payment API access, Google OAuth credential extraction, CredentialKey table OAuth token extraction (Google Calendar, Zoom), API booking and attendee data enumeration, user email harvesting, API key audit and expiration verification, DATABASE_URL PostgreSQL credential extraction, Docker environment variable credential exposure, and SMTP password extraction for email service impersonation.

Start free scan