···210210 .await
211211 .ok_or_else(|| AppError::Internal("no DID in OAuth session".into()))?;
212212213213+ // Check if the user is authorized to access the dashboard.
214214+ // Allow login when no users exist yet (first user will be bootstrapped as admin).
215215+ // Otherwise, only allow users already in the users table.
216216+ let user_count: (i64,) = sqlx::query_as("SELECT COUNT(*) FROM users")
217217+ .fetch_one(&state.db)
218218+ .await
219219+ .map_err(|e| AppError::Internal(format!("user count query failed: {e}")))?;
220220+221221+ if user_count.0 > 0 {
222222+ let user_exists: Option<(i32,)> = sqlx::query_as(&adapt_sql(
223223+ "SELECT 1 FROM users WHERE did = ?",
224224+ state.db_backend,
225225+ ))
226226+ .bind(did.as_ref())
227227+ .fetch_optional(&state.db)
228228+ .await
229229+ .map_err(|e| AppError::Internal(format!("user lookup failed: {e}")))?;
230230+231231+ if user_exists.is_none() {
232232+ return Ok((jar, Redirect::to("/login?error=not_authorized")));
233233+ }
234234+ }
235235+213236 // Look up the client_key for the API client so we can store it in the session cookie
214237 // for per-client rate limiting.
215238 let client_key = if let Some(ref cid) = client_id {