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

feat: use styled error page on bad client id or url

dunkirk.sh 234a091c 59912e94

verified
+98 -1
+98 -1
src/routes/indieauth.ts
··· 263 263 const appResult = ensureApp(clientId, redirectUri); 264 264 265 265 if (appResult.error) { 266 - return new Response(appResult.error, { status: 400 }); 266 + return new Response( 267 + `<!DOCTYPE html> 268 + <html lang="en"> 269 + <head> 270 + <meta charset="UTF-8"> 271 + <meta name="viewport" content="width=device-width, initial-scale=1.0"> 272 + <title>Invalid Client ID • Indiko</title> 273 + <link rel="preconnect" href="https://fonts.googleapis.com"> 274 + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> 275 + <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300..700&display=swap" rel="stylesheet"> 276 + <style> 277 + :root { 278 + --mahogany: #26242b; 279 + --lavender: #d9d0de; 280 + --old-rose: #bc8da0; 281 + --rosewood: #a04668; 282 + --berry-crush: #ab4967; 283 + } 284 + * { margin: 0; padding: 0; box-sizing: border-box; } 285 + body { 286 + font-family: "Space Grotesk", sans-serif; 287 + background: var(--mahogany); 288 + color: var(--lavender); 289 + min-height: 100vh; 290 + display: flex; 291 + align-items: center; 292 + justify-content: center; 293 + padding: 2rem; 294 + } 295 + .error-box { 296 + max-width: 600px; 297 + background: rgba(188, 141, 160, 0.05); 298 + border: 2px solid var(--rosewood); 299 + padding: 2.5rem; 300 + } 301 + h1 { 302 + font-size: 2rem; 303 + font-weight: 700; 304 + background: linear-gradient(135deg, var(--old-rose), var(--rosewood)); 305 + -webkit-background-clip: text; 306 + -webkit-text-fill-color: transparent; 307 + background-clip: text; 308 + margin-bottom: 1.5rem; 309 + letter-spacing: -0.05rem; 310 + } 311 + p { 312 + line-height: 1.8; 313 + margin-bottom: 1rem; 314 + color: var(--lavender); 315 + } 316 + code { 317 + background: rgba(12, 23, 19, 0.8); 318 + padding: 0.25rem 0.5rem; 319 + color: var(--berry-crush); 320 + font-size: 0.875rem; 321 + word-break: break-all; 322 + display: inline-block; 323 + max-width: 100%; 324 + } 325 + .error-details { 326 + background: rgba(160, 70, 104, 0.1); 327 + border-left: 4px solid var(--rosewood); 328 + padding: 1rem; 329 + margin: 1.5rem 0; 330 + } 331 + .error-details strong { 332 + display: block; 333 + margin-bottom: 0.5rem; 334 + color: var(--old-rose); 335 + } 336 + </style> 337 + </head> 338 + <body> 339 + <div class="error-box"> 340 + <h1>Invalid Client ID</h1> 341 + <p> 342 + The OAuth authorization request failed because the provided <code>client_id</code> is not valid. 343 + </p> 344 + <div class="error-details"> 345 + <strong>Error:</strong> 346 + <p>${appResult.error}</p> 347 + </div> 348 + <div class="error-details"> 349 + <strong>Provided client_id:</strong> 350 + <code>${clientId}</code> 351 + </div> 352 + <p style="margin-top: 1.5rem; font-size: 0.875rem; color: var(--old-rose);"> 353 + For auto-registration, the client_id must be a valid URL (e.g., https://example.com). 354 + Non-URL client IDs (like <code>ikc_xxxxx</code>) must be pre-registered by an administrator. 355 + </p> 356 + </div> 357 + </body> 358 + </html>`, 359 + { 360 + status: 400, 361 + headers: { "Content-Type": "text/html" } 362 + } 363 + ); 267 364 } 268 365 269 366 const app = appResult.app!;