6.1 KiB
Authentication Service
Status: ✅ Implemented | Plex OAuth + Plex Home profile support + JWT sessions + RBAC
Handles authentication and authorization: Plex OAuth integration with Plex Home profile support, JWT session management, role-based access control.
Authentication: Plex OAuth
- No password management needed
- Users already have Plex accounts
- Seamless integration
- Automatic profile pictures/metadata
- Plex Home support: Each profile = separate user
Session Management: JWT Tokens
- Stateless authentication
- No server-side session storage
- Easy horizontal scaling
- Includes user claims (role, permissions)
Access Control: RBAC
Roles:
- user - Request audiobooks, view own requests, search
- admin - Full system access (settings, users, all requests)
OAuth Flow (with Plex Home Support)
- User clicks "Login with Plex"
- Redirect to Plex OAuth
- User authorizes app
- Redirect back with PIN code
- Exchange code for main account token
- Get main account user info
- Verify user has access to configured Plex server (uses stored machineIdentifier from config)
- Check for Plex Home profiles:
- If profiles exist → Redirect to profile selection page
- If no profiles → Continue with main account
- Profile Selection (if applicable):
- User selects profile from grid
- Enter PIN if profile is protected
- Switch to profile, get profile's auth token
- Create/update user in DB (with profile details)
- Generate JWT
- Return JWT to client
- Client includes JWT in subsequent requests
OAuth Endpoints
GET /api/auth/plex/login - Redirect to Plex OAuth GET /api/auth/plex/callback?pinId=... - Exchange PIN, check for profiles, return JWT or redirect to profile selection GET /api/auth/plex/home-users - Get list of Plex Home profiles (requires X-Plex-Token header) POST /api/auth/plex/switch-profile - Switch to selected profile and complete authentication POST /api/auth/refresh - Get new access token (refresh token in header) POST /api/auth/logout - Clear client-side token GET /api/auth/me - Get current user (JWT in header)
JWT Structure
Access Token (1hr):
{
"sub": "user-uuid",
"plexId": "plex-user-id",
"username": "john_doe",
"role": "admin",
"iat": 1234567890,
"exp": 1234571490
}
Refresh Token (7d):
{
"sub": "user-uuid",
"type": "refresh",
"iat": 1234567890,
"exp": 1234971490
}
Storage:
- Access: HTTP-only cookie + localStorage
- Refresh: HTTP-only secure cookie only
- SameSite=Strict (CSRF protection)
Middleware
requireAuth() - Verifies JWT exists/valid, adds user to request, returns 401 if invalid
requireAdmin() - Checks user.role === 'admin', returns 403 if not, chains after requireAuth
First User Setup
- First user created during setup automatically promoted to admin
- Marked as "setup admin" with
isSetupAdmin=trueflag - Setup admin role is protected - cannot be changed to prevent lockout
- Ensures someone always has admin access after fresh install
- Subsequent users default to 'user' role
- Local admin uses username/password authentication (stored in
authTokenfield as bcrypt hash) plexIdformat:local-{username}for local admin accounts
Local Admin Authentication
Local Admin (Setup Admin):
- Created during setup wizard (step 2)
- Username/password authentication (separate from Plex OAuth)
- Password hashed with bcrypt (10 rounds) and stored in
authTokenfield - Login: POST
/api/auth/admin/loginwith username/password - Identified by:
isSetupAdmin=trueANDplexIdstarts withlocal-
Password Management:
- POST
/api/admin/settings/change-password- Change local admin password - Requires: current password, new password (min 8 chars), confirmation
- Security: Only accessible to local admin (verified via
requireLocalAdminmiddleware) - Validates current password before allowing change
Plex Home Profile Support
Overview:
- Plex Home accounts can have multiple profiles (managed users, family members)
- Each profile has its own library ratings, watch history, restrictions
- Architecture: Each profile = separate user in ReadMeABook system
Profile Selection Flow:
- User authenticates with main Plex account
- System fetches list of profiles via
GET https://plex.tv/api/home/users - If profiles exist → Show profile selection page (
/auth/select-profile) - User selects their profile (enters PIN if protected)
- System switches to profile via
POST https://plex.tv/api/home/users/{id}/switch - Profile's auth token is stored (encrypted)
- User record created with profile's details
Profile Data Storage:
plexId: Profile's unique ID (not main account ID)plexUsername: Profile's friendlyNameauthToken: Profile's auth token (encrypted)avatarUrl: Profile's avatarplexHomeUserId: Profile ID for reference (null = main account, set = home profile)
User Isolation:
- Each profile is a completely separate user
- Separate requests, separate BookDate recommendations, separate ratings
- Admin sees all profiles as independent users (no grouping)
- Profile switching = logout and login again
Profile Protection:
- Protected profiles require PIN on login
- PIN validated by Plex API during switch
- PIN not stored (only needed at login)
Benefits:
- Accurate request attribution ("Requested by Dad" vs "Requested by Kids")
- Personalized BookDate recommendations based on each profile's ratings
- Separate "My Requests" per family member
- Accurate logs and analytics
Security
- Never log tokens
- HTTPS only in production
- Short access token expiry (1hr)
- Optional refresh token rotation
- Track valid tokens for revocation
- Server access verification: Uses stored
machineIdentifierfrom config (no API call needed)- Only users with access to the configured Plex server can authenticate
- Prevents any Plex user from accessing the instance
- machineIdentifier stored during setup/settings configuration (architectural optimization)
Tech Stack
- Custom Plex OAuth (direct API)
- jsonwebtoken (npm)
- Node.js crypto