Files
phaseflow/specs/authentication.md
Petru Paler 3567fbafd7
Some checks failed
Deploy / deploy (push) Failing after 6m37s
Expose PocketBase URL to client-side for OIDC auth
POCKETBASE_URL was only available server-side, causing the login page
to fall back to localhost:8090 in the browser. Renamed to
NEXT_PUBLIC_POCKETBASE_URL so Next.js bundles it into client code.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-11 17:33:52 +00:00

182 lines
4.3 KiB
Markdown

# Authentication Specification
## Job to Be Done
When I access PhaseFlow, I want to securely log in with my identity provider, so that my personal health data remains private.
## Auth Provider
Using PocketBase for authentication and data storage, with OIDC (Pocket-ID) as the primary identity provider.
**Connection:**
- `NEXT_PUBLIC_POCKETBASE_URL` environment variable
- `src/lib/pocketbase.ts` initializes client
## Login Flow
### OIDC Authentication (Pocket-ID)
1. User clicks "Sign In" on `/login`
2. App redirects to Pocket-ID authorization endpoint
3. User authenticates with Pocket-ID (handles MFA if configured)
4. Pocket-ID redirects back with authorization code
5. PocketBase exchanges code for tokens
6. User redirected to dashboard
### OIDC Configuration
Configure in PocketBase Admin UI: **Settings → Auth providers → OpenID Connect**
**Required Settings:**
- `Client ID` - From Pocket-ID application
- `Client Secret` - From Pocket-ID application
- `Issuer URL` - Your Pocket-ID instance URL (e.g., `https://id.yourdomain.com`)
**Environment Variables:**
```env
POCKETBASE_OIDC_CLIENT_ID=phaseflow
POCKETBASE_OIDC_CLIENT_SECRET=xxx
POCKETBASE_OIDC_ISSUER_URL=https://id.yourdomain.com
```
### Session Management
- PocketBase manages session tokens automatically
- Auth state persisted in browser (cookie/localStorage)
- Session expires after 14 days of inactivity
## Pages
### `/login`
**Elements:**
- "Sign In with Pocket-ID" button
- Error message display
**Behavior:**
- Redirect to `/` on successful login
- Show error message on failed attempt
- Rate limit: 5 attempts per minute
### Protected Routes
All routes except `/login` require authentication.
**Middleware Check:**
1. Check for valid PocketBase auth token
2. If invalid/missing, redirect to `/login`
3. If valid, proceed to requested page
## API Authentication
### User Context
API routes access current user via:
```typescript
const pb = new PocketBase(process.env.NEXT_PUBLIC_POCKETBASE_URL);
// Auth token from request cookies
const user = pb.authStore.model;
```
### Protected Endpoints
All `/api/*` routes except:
- `/api/calendar/[userId]/[token].ics` (token-based auth)
- `/api/cron/*` (CRON_SECRET auth)
## User API
### GET `/api/user`
Returns current authenticated user profile.
**Response:**
```json
{
"id": "user123",
"email": "user@example.com",
"garminConnected": true,
"cycleLength": 31,
"lastPeriodDate": "2024-01-01",
"notificationTime": "07:00",
"timezone": "America/New_York"
}
```
### PATCH `/api/user`
Updates user profile fields.
**Request Body (partial update):**
```json
{
"cycleLength": 28,
"notificationTime": "06:30"
}
```
## User Schema
See `src/types/index.ts` for full `User` interface.
**Auth-related fields:**
- `id` - PocketBase record ID
- `email` - Login email
**Profile fields:**
- `cycleLength` - Personal cycle length (days)
- `notificationTime` - Preferred notification hour
- `timezone` - User's timezone
## PocketBase Client (`src/lib/pocketbase.ts`)
**Exports:**
- `pb` - Initialized PocketBase client
- `getCurrentUser()` - Get authenticated user
- `isAuthenticated()` - Check auth status
## Settings Page (`/settings`)
User profile management:
- View/edit cycle length
- View/edit notification time
- View/edit timezone
- Link to Garmin settings
## Success Criteria
1. OIDC login flow completes in under 5 seconds (including redirect)
2. Session persists across browser refreshes
3. Unauthorized access redirects to login
4. User data isolated by authentication
## Acceptance Tests
- [ ] OIDC redirect initiates correctly
- [ ] Successful OIDC callback creates/updates user
- [ ] Session persists after page refresh
- [ ] Protected routes redirect when not authenticated
- [ ] GET `/api/user` returns current user data
- [ ] PATCH `/api/user` updates user record
- [ ] Logout clears session completely
- [ ] Auth cookie is HttpOnly and Secure
## Future Enhancements
### Open Registration
When open registration is enabled:
- Add `/signup` page with OIDC provider selection
- New users created automatically on first OIDC login
- Admin approval workflow (optional)
### Additional OAuth2 Providers
PocketBase supports multiple OAuth2 providers. Future options:
- Google
- GitHub
- Apple
- Other OIDC-compliant providers
Each provider configured separately in PocketBase Admin UI.