my own indieAuth provider! indiko.dunkirk.sh/docs
indieauth oauth2-server
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add security.md

dunkirk.sh f8988746 73dc5e55

verified
+188 -2
+2 -2
README.md
··· 62 62 63 63 On first run, you'll need to create an admin user: 64 64 65 - 1. Visit `https://your-indiko-domain.com/login?invite=bootstrap` 65 + 1. Visit `https://your-indiko-domain.com/login` 66 66 2. Register with a passkey 67 67 3. This first user will automatically be an admin 68 68 ··· 152 152 add_header Referrer-Policy "strict-origin-when-cross-origin" always; 153 153 add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always; 154 154 add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; 155 - 155 + 156 156 # Content Security Policy 157 157 add_header Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; script-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self';" always; 158 158
+186
SECURITY.md
··· 1 + # Security Policy 2 + 3 + ## Reporting Security Vulnerabilities 4 + 5 + If you discover a security vulnerability in Indiko, please report it privately: 6 + 7 + - **Email:** security@dunkirk.sh 8 + - **Do not** open public issues for security vulnerabilities 9 + - You will receive a response within 48 hours 10 + 11 + We appreciate responsible disclosure and will credit researchers who report vulnerabilities (unless you prefer to remain anonymous). 12 + 13 + --- 14 + 15 + ## Security Architecture 16 + 17 + ### Authentication Model 18 + 19 + Indiko uses **WebAuthn/Passkeys** for passwordless authentication: 20 + 21 + - **No passwords stored** - eliminates credential stuffing, dictionary attacks, and password database breaches 22 + - **Public key cryptography** - private keys never leave the user's device 23 + - **Phishing resistant** - cryptographic binding to domain prevents phishing 24 + - **Platform authenticators** - biometric authentication (Face ID, Touch ID, Windows Hello) 25 + 26 + ### OAuth 2.0 Implementation 27 + 28 + **Authorization Code Flow with PKCE:** 29 + 30 + - All authorization codes are **single-use** and expire in 60 seconds 31 + - **PKCE (S256)** required for public clients (auto-registered apps) 32 + - **Client secrets** (SHA-256 hashed) required for pre-registered confidential clients 33 + - Redirect URIs validated against registered allowlist 34 + 35 + **Session Management:** 36 + 37 + - Session tokens: cryptographically random UUIDs (128-bit entropy) 38 + - 24-hour session expiry with automatic cleanup (runs hourly) 39 + - HttpOnly cookies prevent XSS token theft 40 + - Secure flag enabled in production (HTTPS-only transmission) 41 + - SameSite=Lax provides CSRF protection 42 + 43 + ### Database Security 44 + 45 + - **SQLite with WAL mode** for concurrent access 46 + - **Foreign keys enabled** for referential integrity 47 + - **Parameterized queries** throughout - no SQL injection vectors 48 + - **Credential data** stored as Buffers with proper encoding 49 + 50 + --- 51 + 52 + ## Known Security Considerations 53 + 54 + ### Rate Limiting ⚠️ 55 + 56 + Indiko does **not** currently implement rate limiting. This is acceptable for: 57 + 58 + - Personal homelab deployments 59 + - Small team internal authentication 60 + - Deployments behind a reverse proxy with rate limiting (nginx, Caddy, Cloudflare) 61 + 62 + If exposing Indiko to the public internet, implement rate limiting: 63 + 64 + ```typescript 65 + // Suggested limits per IP: 66 + - Challenge generation: 10 requests/minute 67 + - Login/registration: 5 attempts/minute 68 + - Token exchange: 20 requests/minute 69 + - Admin endpoints: 20 requests/minute 70 + ``` 71 + 72 + ### Bootstrap Mode 73 + 74 + **First user registration** automatically becomes admin without invite code. 75 + 76 + **Security implications:** 77 + 78 + - Expose Indiko only after creating your admin account 79 + - Consider disabling bootstrap mode after first user (future feature) 80 + 81 + ### Invite Code Security 82 + 83 + Invite codes use 16 bytes of cryptographic randomness (base64url encoded, ~128 bits entropy). 84 + 85 + ### Client Secret Management 86 + 87 + Client secrets for pre-registered OAuth apps use 43-character nanoid (~256 bits entropy). 88 + 89 + - Secrets shown **only once** at creation 90 + - Stored as SHA-256 hashes 91 + - No recovery mechanism - regenerate if lost 92 + - Rotate secrets if compromised or annually 93 + 94 + --- 95 + 96 + ## Production Deployment 97 + 98 + Indiko is designed to run behind a reverse proxy in production. See the [README deployment section](README.md#production-deployment) for complete nginx/Caddy configurations with security headers. 99 + 100 + **Environment Variables:** 101 + 102 + - `ORIGIN` must be HTTPS URL in production (validated at startup) 103 + - `RP_ID` must match ORIGIN domain (validated at startup) 104 + - `NODE_ENV=production` enables Secure cookie flag 105 + 106 + --- 107 + 108 + ## Notes 109 + 110 + ### For Developers (OAuth Clients) 111 + 112 + 1. **Use PKCE:** 113 + 114 + - Always generate code_verifier (43+ character random string) 115 + - Use S256 code_challenge_method 116 + - Never reuse verifiers across authorization flows 117 + 118 + 2. **Protect client secrets:** 119 + 120 + - Store in environment variables or secret manager 121 + - Never commit to version control 122 + - Never expose in client-side code 123 + - Rotate if compromised 124 + 125 + 3. **Validate tokens:** 126 + 127 + - Check `me` field matches expected user identity 128 + - Verify `scope` includes required permissions 129 + - Handle token errors gracefully 130 + - Don't cache profile data indefinitely 131 + 132 + 4. **Respect user privacy:** 133 + - Request minimum necessary scopes 134 + - Provide clear app description and logo 135 + - Implement logout/revoke functionality 136 + - Don't share user data with third parties 137 + 138 + --- 139 + 140 + ## Compliance Notes 141 + 142 + ### GDPR (EU General Data Protection Regulation) 143 + 144 + **Personal data stored:** 145 + 146 + - Username, name, email (optional), photo URL (optional) 147 + - Passkey credential data (public keys only) 148 + - Session tokens (temporary) 149 + - OAuth authorization history 150 + 151 + **User rights:** 152 + 153 + - **Right to access:** Users can view profile at `/profile` 154 + - **Right to erasure:** Admins can delete user accounts (cascading delete) 155 + - **Right to data portability:** Export database manually (SQLite dump) 156 + - **Right to rectification:** Users can edit profile at `/profile` 157 + 158 + **Data retention:** 159 + 160 + - Active data retained while account exists 161 + - Expired sessions/challenges automatically cleaned hourly 162 + - Deleted accounts immediately remove all personal data (foreign key cascade) 163 + 164 + ### CCPA (California Consumer Privacy Act) 165 + 166 + Indiko does not sell personal information. Users can request data deletion via admin. 167 + 168 + ### Cookie Policy 169 + 170 + | Cookie Name | Purpose | Duration | Type | 171 + | ---------------- | ---------------------- | -------- | --------- | 172 + | `indiko_session` | Authentication session | 24 hours | Essential | 173 + 174 + No tracking or analytics cookies are set by Indiko. 175 + 176 + --- 177 + 178 + ## Contact 179 + 180 + - **Security Issues:** security@dunkirk.sh 181 + - **General Support:** https://tangled.org/@dunkirk.sh/indiko 182 + - **Maintainer:** Kieran Klukas (@taciturnaxolotl) 183 + 184 + --- 185 + 186 + _Last Updated: December 18, 2025_