# OIDC Authentication Implementation Plan for Claude Web UI

## Project Overview

**Project:** Claude Web UI - Web interface for Claude Code CLI
**URL:** https://agents.sneakercloud.de
**Auth Provider:** Authentik (https://auth.sneakercloud.de)
**Implementation Goal:** Add production-ready OIDC authentication with group-based access control

## Current Architecture Analysis

### Backend (Node.js/Express)
- **File:** `backend/server.js` (999 lines)
- **Framework:** Express with WebSocket (ws library)
- **Current State:** No authentication
- **Endpoints:**
  - REST: `/api/hosts`, `/api/projects`, `/api/health`, `/api/browse`, `/api/upload/:sessionId`, `/api/history/:project`
  - WebSocket: Single endpoint on root path
- **Sessions:** In-memory Map with UUIDs, no user association

### Frontend (React/Vite)
- **Main:** `frontend/src/App.jsx` - Application shell
- **State:** `frontend/src/contexts/SessionContext.jsx` - Session management via React Context
- **WebSocket:** Direct connections stored in refs
- **Storage:** localStorage for session persistence
- **Current State:** No authentication UI or logic

### Key Constraints
1. Single-file backend (consider modular refactoring)
2. WebSocket architecture must be preserved
3. No localStorage for tokens (XSS risk)
4. Production security requirements
5. Group-based access control needed

---

## Phase 1: Authentik Configuration & Backend Foundation

### Objectives
- Configure Authentik OIDC Provider and Application
- Refactor backend into modular structure
- Add core authentication dependencies
- Implement session storage mechanism

### Tasks

#### 1.1 Authentik Setup
**Provider Configuration:**
```yaml
Name: Claude Web UI
Client Type: Confidential
Authorization Flow: Authorization Code
Redirect URIs:
  - https://agents.sneakercloud.de/auth/callback
  - http://localhost:5173/auth/callback (dev)
Scopes: openid, profile, email, groups
```

**Application Configuration:**
```yaml
Name: Claude Web UI
Provider: [linked to above]
Launch URL: https://agents.sneakercloud.de
```

**Group Mappings:**
- Create groups: `agent-admin`, `agent-users`
- Configure scope mappings to include groups in ID token
- Test with user accounts

**Deliverables:**
- Client ID and Client Secret
- Discovery URL: `https://auth.sneakercloud.de/application/o/claude-web-ui/.well-known/openid-configuration`
- Documented group structure

#### 1.2 Backend Refactoring
**Current:** Single `backend/server.js` (999 lines)

**New Structure:**
```
backend/
├── server.js              # Main entry, app initialization
├── config/
│   └── auth.js           # Auth config from env
├── middleware/
│   ├── auth.js           # Authentication middleware
│   └── session.js        # Session management
├── routes/
│   ├── api.js            # Existing API routes
│   ├── auth.js           # OIDC auth routes
│   └── websocket.js      # WebSocket handler
└── utils/
    └── oidc.js           # OIDC client wrapper
```

**Migration Strategy:**
1. Create new file structure
2. Extract route handlers into modules
3. Extract WebSocket logic
4. Update server.js to import modules
5. Test that existing functionality works

#### 1.3 Dependencies
**Add to `backend/package.json`:**
```json
{
  "express-session": "^1.18.0",
  "connect-redis": "^7.1.0",
  "redis": "^4.6.0",
  "openid-client": "^5.6.0",
  "cookie-parser": "^1.4.6"
}
```

**Rationale:**
- `express-session`: Session management
- `connect-redis`: Session store (persistent, production-ready)
- `redis`: Session backend
- `openid-client`: Official OIDC client (certified)
- `cookie-parser`: Cookie handling

#### 1.4 Session Storage
**Redis Configuration:**
- Use existing Redis instance or add to docker-compose
- Configure session store with httpOnly cookies
- Session TTL: 24 hours (configurable)
- Secure flag in production

**Session Schema:**
```javascript
{
  sessionId: "uuid",
  userId: "oidc-sub",
  email: "user@example.com",
  name: "User Name",
  groups: ["agent-users"],
  accessToken: "encrypted",
  refreshToken: "encrypted",
  expiresAt: timestamp,
  createdAt: timestamp
}
```

### Verification Criteria
- [ ] Authentik provider and application created
- [ ] Test user can authenticate via Authentik UI
- [ ] Backend refactored, all tests pass
- [ ] Dependencies installed
- [ ] Redis session store connected
- [ ] Session CRUD operations working

---

## Phase 2: OIDC Authentication Flow

### Objectives
- Implement authorization code flow
- Create auth routes (login, callback, logout)
- Secure session cookies
- Handle token refresh

### Tasks

#### 2.1 OIDC Client Setup
**File:** `backend/utils/oidc.js`

**Functionality:**
- Discover OIDC configuration from Authentik
- Initialize Issuer and Client
- Generate authorization URL
- Handle token exchange
- Validate ID tokens
- Refresh access tokens

**Configuration (from env):**
```bash
OIDC_ISSUER=https://auth.sneakercloud.de/application/o/claude-web-ui/
OIDC_CLIENT_ID=<from_authentik>
OIDC_CLIENT_SECRET=<from_authentik>
OIDC_REDIRECT_URI=https://agents.sneakercloud.de/auth/callback
SESSION_SECRET=<generate_with_openssl>
REDIS_URL=redis://localhost:6379
```

#### 2.2 Authentication Routes
**File:** `backend/routes/auth.js`

**Routes:**

**GET `/auth/login`**
- Generate OIDC authorization URL with PKCE
- Store state and nonce in session
- Redirect to Authentik

**GET `/auth/callback`**
- Validate state parameter
- Exchange authorization code for tokens
- Validate ID token signature and claims
- Extract user info and groups
- Create session with user data
- Redirect to frontend (`/`)

**POST `/auth/logout`**
- Destroy session
- Optionally redirect to Authentik logout
- Clear cookies

**GET `/auth/user`**
- Return current user info from session
- Return 401 if not authenticated

**POST `/auth/refresh`**
- Use refresh token to get new access token
- Update session
- Return success/failure

#### 2.3 Session Cookie Configuration
```javascript
{
  name: 'claude.sid',
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,        // No JavaScript access
    secure: true,          // HTTPS only in production
    sameSite: 'lax',       // CSRF protection
    maxAge: 24 * 60 * 60 * 1000  // 24 hours
  },
  store: redisStore      // Redis session store
}
```

#### 2.4 Token Management
- Store access token in Redis session (encrypted)
- Store refresh token in Redis session (encrypted)
- Implement token refresh 5 minutes before expiry
- Handle refresh failures (force re-login)

### Verification Criteria
- [ ] Login redirects to Authentik
- [ ] Callback successfully exchanges code for tokens
- [ ] ID token validated and parsed
- [ ] Groups extracted from token
- [ ] Session created in Redis
- [ ] Cookie set with correct flags
- [ ] `/auth/user` returns user info
- [ ] Logout destroys session
- [ ] Token refresh works

---

## Phase 3: Backend API Protection

### Objectives
- Protect all API endpoints with authentication
- Secure WebSocket connections
- Implement group-based authorization
- Handle unauthorized access gracefully

### Tasks

#### 3.1 Authentication Middleware
**File:** `backend/middleware/auth.js`

**Middleware Functions:**

**`requireAuth`**
- Check if session exists and is valid
- Verify session in Redis
- Attach `req.user` with user data
- Return 401 if not authenticated

**`requireGroup(groups)`**
- Check if `req.user.groups` includes required group
- Return 403 if insufficient permissions
- Usage: `requireGroup(['agent-admin'])`

**`optionalAuth`**
- Attach user if authenticated
- Continue if not authenticated
- For endpoints that support both modes

#### 3.2 Protect REST Endpoints
**Apply Middleware:**
```javascript
// All API routes require auth
app.use('/api', requireAuth);

// Admin-only endpoints
app.get('/api/admin/*', requireGroup(['agent-admin']));

// Health check remains public
app.get('/api/health', (req, res) => ...);
```

**Protected Endpoints:**
- `/api/hosts` - Read: agent-users, Write: agent-admin
- `/api/projects` - Read: agent-users
- `/api/browse` - agent-users
- `/api/upload/:sessionId` - agent-users
- `/api/history/:project` - agent-users (own history only)

#### 3.3 WebSocket Authentication
**Challenge:** WebSocket upgrade happens before HTTP middleware

**Solution: Cookie-based Auth**
1. Parse cookies from upgrade request
2. Load session from Redis
3. Validate session
4. Attach user to WebSocket connection
5. Reject upgrade if not authenticated

**Implementation:**
```javascript
wss.on('connection', async (ws, req) => {
  // Parse cookies from req.headers.cookie
  // Load session from Redis
  // Validate user
  if (!user) {
    ws.close(1008, 'Unauthorized');
    return;
  }

  // Attach user to connection
  ws.user = user;

  // Continue with Claude session logic
});
```

#### 3.4 Session Association
**Current:** Sessions stored by UUID, no user association

**New:**
- Associate Claude sessions with authenticated user
- Store user ID in session metadata
- Allow users to only access their own sessions
- Admin users can view all sessions (optional)

**Session Schema Update:**
```javascript
{
  sessionId: "uuid",
  userId: "oidc-sub",          // NEW
  userEmail: "user@example.com", // NEW
  project: "/path/to/project",
  host: "neko",
  // ... existing fields
}
```

#### 3.5 Error Handling
- 401 Unauthorized: Not authenticated
- 403 Forbidden: Insufficient permissions
- Redirect to `/auth/login` for API calls
- WebSocket: Close with error code
- Frontend: Display login prompt

### Verification Criteria
- [ ] Unauthenticated API calls return 401
- [ ] WebSocket upgrades require valid session
- [ ] Group-based access control works
- [ ] Admin users have extended permissions
- [ ] Sessions isolated by user
- [ ] Error responses are clear

---

## Phase 4: Frontend Authentication UI

### Objectives
- Create AuthContext for authentication state
- Implement login/logout UI
- Add protected routes
- Handle authentication errors
- Integrate with existing SessionContext

### Tasks

#### 4.1 AuthContext
**File:** `frontend/src/contexts/AuthContext.jsx`

**State:**
```javascript
{
  user: null | {
    id: string,
    email: string,
    name: string,
    groups: string[]
  },
  isLoading: boolean,
  error: string | null
}
```

**Methods:**
- `login()` - Redirect to `/auth/login`
- `logout()` - Call `/auth/logout`, clear state
- `refreshUser()` - Call `/auth/user` to get current user
- `checkAuth()` - Verify authentication on mount

**Auto-refresh:**
- Poll `/auth/user` every 5 minutes
- Handle 401 by redirecting to login
- Update user state on success

#### 4.2 Login Flow
**Current:** App loads directly to ChatPanel

**New:**
1. App mounts
2. AuthContext checks `/auth/user`
3. If 401: Show login page
4. If 200: Show app
5. User clicks "Login with Authentik"
6. Redirect to `/auth/login`
7. Authentik authentication
8. Callback redirects to `/`
9. AuthContext refreshes user
10. App shows

#### 4.3 Login Page Component
**File:** `frontend/src/components/LoginPage.jsx`

**UI:**
- Centered card
- Claude Web UI branding
- "Login with Authentik" button
- Loading state during redirect
- Error messages

**Design:**
- Match existing dark theme
- Simple, professional
- No local credentials (OIDC only)

#### 4.4 Protected App Wrapper
**File:** `frontend/src/App.jsx` (update)

**Logic:**
```javascript
function App() {
  return (
    <AuthProvider>
      <AuthenticatedApp />
    </AuthProvider>
  );
}

function AuthenticatedApp() {
  const { user, isLoading } = useAuth();

  if (isLoading) return <LoadingSpinner />;
  if (!user) return <LoginPage />;

  return (
    <SessionProvider>
      <AppContent />
    </SessionProvider>
  );
}
```

#### 4.5 User Menu
**Location:** Header component

**Elements:**
- User name/email display
- Group badges (admin/user)
- Logout button
- Account settings link (optional)

**Functionality:**
- Dropdown menu
- Logout calls `logout()` method
- Shows current user info

#### 4.6 Session Integration
**Update:** `frontend/src/contexts/SessionContext.jsx`

**Changes:**
- Include user in WebSocket connection metadata
- Filter sessions by current user
- Add user info to session creation
- Handle 401 errors by triggering AuthContext logout

**WebSocket Headers:**
- Session cookie automatically included
- Backend validates on upgrade

### Verification Criteria
- [ ] Unauthenticated users see login page
- [ ] Login redirects to Authentik
- [ ] Successful login shows app
- [ ] User info displayed in UI
- [ ] Logout clears session and returns to login
- [ ] 401 errors trigger re-authentication
- [ ] WebSocket connects with auth
- [ ] Sessions filtered by user

---

## Phase 5: Production Hardening & Polish

### Objectives
- Implement security best practices
- Add monitoring and logging
- Handle edge cases
- Document configuration
- Test thoroughly

### Tasks

#### 5.1 Security Enhancements

**CSRF Protection:**
- Use `sameSite: 'lax'` on cookies
- Add CSRF tokens for state-changing operations
- Validate origin headers on WebSocket upgrade

**Rate Limiting:**
- Add `express-rate-limit` to auth endpoints
- Limit login attempts per IP
- Limit token refresh attempts

**Token Security:**
- Encrypt tokens at rest in Redis (using `crypto`)
- Validate token expiry before use
- Clear tokens on logout

**Content Security Policy:**
- Add CSP headers
- Allow only Authentik domain for redirects
- Restrict inline scripts

#### 5.2 Logging & Monitoring

**Authentication Events:**
- Log successful logins (user ID, IP, timestamp)
- Log failed auth attempts
- Log token refresh failures
- Log logout events

**Metrics:**
- Active sessions count
- Authentication failures rate
- Token refresh rate
- WebSocket auth failures

**Integration:**
- Use existing Gotify webhook for alerts
- Log to stdout (Docker logs)
- Consider structured logging (winston)

#### 5.3 Error Handling

**Token Expiry:**
- Detect access token expiry
- Auto-refresh using refresh token
- Prompt re-login if refresh fails
- Show clear error messages

**Session Expiry:**
- Handle expired sessions gracefully
- Show "Session expired, please login again"
- Preserve unsaved work if possible

**Network Errors:**
- Retry logic for auth endpoint calls
- Offline detection
- Clear error messages

**Authentik Downtime:**
- Cache user info for graceful degradation
- Allow continued use of valid sessions
- Queue auth checks

#### 5.4 Environment Configuration

**Required Env Vars:**
```bash
# OIDC
OIDC_ISSUER=https://auth.sneakercloud.de/application/o/claude-web-ui/
OIDC_CLIENT_ID=<client_id>
OIDC_CLIENT_SECRET=<client_secret>
OIDC_REDIRECT_URI=https://agents.sneakercloud.de/auth/callback

# Session
SESSION_SECRET=<generate_with_openssl_rand_hex_32>
SESSION_DOMAIN=.sneakercloud.de
SESSION_SECURE=true
SESSION_MAX_AGE=86400000  # 24 hours

# Redis
REDIS_URL=redis://localhost:6379
REDIS_PASSWORD=<optional>

# App
NODE_ENV=production
FRONTEND_URL=https://agents.sneakercloud.de
```

**docker-compose.yml Updates:**
```yaml
services:
  backend:
    environment:
      - OIDC_ISSUER=${OIDC_ISSUER}
      - OIDC_CLIENT_ID=${OIDC_CLIENT_ID}
      - OIDC_CLIENT_SECRET=${OIDC_CLIENT_SECRET}
      - SESSION_SECRET=${SESSION_SECRET}
      - REDIS_URL=redis://redis:6379
    depends_on:
      - redis

  redis:
    image: redis:7-alpine
    networks:
      - claude-internal
    volumes:
      - redis-data:/data

volumes:
  redis-data:
```

#### 5.5 Documentation

**Create Files:**
- `docs/AUTHENTICATION.md` - Overview of auth system
- `docs/SETUP.md` - Step-by-step Authentik setup
- `docs/CONFIGURATION.md` - Environment variables
- `docs/TROUBLESHOOTING.md` - Common issues

**Update README.md:**
- Add authentication section
- Document required groups
- Link to setup docs

#### 5.6 Testing

**Manual Tests:**
- [ ] Fresh login flow
- [ ] Logout and re-login
- [ ] Session persistence across browser refresh
- [ ] Token refresh
- [ ] Expired session handling
- [ ] WebSocket auth
- [ ] Group-based access control
- [ ] Multiple users simultaneously
- [ ] Admin vs regular user permissions

**Edge Cases:**
- [ ] Authentik downtime during login
- [ ] Redis downtime (session loss)
- [ ] Concurrent logins from same user
- [ ] Token refresh during active WebSocket
- [ ] Cookie disabled in browser
- [ ] CORS issues

**Security Tests:**
- [ ] Cannot access API without auth
- [ ] Cannot access other users' sessions
- [ ] XSS attacks blocked (httpOnly cookies)
- [ ] CSRF protection works
- [ ] Token replay attacks prevented

### Verification Criteria
- [ ] All security enhancements implemented
- [ ] Logging captures auth events
- [ ] Error handling covers all cases
- [ ] Documentation complete
- [ ] All tests pass
- [ ] Production deployment successful

---

## Implementation Timeline

**Phase 1:** Authentik Configuration & Backend Foundation
**Duration:** 2-3 days
**Blockers:** None

**Phase 2:** OIDC Authentication Flow
**Duration:** 2-3 days
**Blockers:** Phase 1 complete

**Phase 3:** Backend API Protection
**Duration:** 2-3 days
**Blockers:** Phase 2 complete

**Phase 4:** Frontend Authentication UI
**Duration:** 3-4 days
**Blockers:** Phase 3 complete

**Phase 5:** Production Hardening & Polish
**Duration:** 2-3 days
**Blockers:** Phase 4 complete

**Total Estimated Time:** 11-16 days

---

## Success Metrics

1. **Security:**
   - Zero XSS vulnerabilities (httpOnly cookies)
   - Zero unauthorized access incidents
   - All tokens encrypted at rest

2. **User Experience:**
   - Seamless login flow (<5 seconds)
   - Clear error messages
   - No unnecessary re-authentication

3. **Reliability:**
   - 99.9% authentication availability
   - Session persistence across restarts
   - Graceful degradation on errors

4. **Performance:**
   - <100ms auth middleware overhead
   - No impact on WebSocket latency
   - Redis session lookups <10ms

---

## Rollback Plan

**If Critical Issues Arise:**

1. **Immediate:** Disable authentication middleware
2. **Revert:** Return to unauthenticated mode
3. **Investigate:** Review logs and error reports
4. **Fix:** Address issues in isolated environment
5. **Redeploy:** With fixes applied

**Feature Flags:**
- `AUTH_ENABLED=false` to disable auth
- Keep unauthenticated code path intact during initial rollout

---

## Future Enhancements

**Post-MVP:**
- Multi-factor authentication (MFA) via Authentik
- API keys for programmatic access
- OAuth2 scopes for fine-grained permissions
- Audit log for all user actions
- Session management UI (view/revoke sessions)
- SSO with other services via Authentik
- User preferences storage
- Role-based access control (RBAC) expansion

---

## References

- **Authentik Docs:** https://docs.goauthentik.io/
- **OIDC Spec:** https://openid.net/connect/
- **openid-client:** https://github.com/panva/node-openid-client
- **express-session:** https://github.com/expressjs/session
- **OWASP Auth Cheatsheet:** https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
