auth_middleware.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. #include "auth_middleware.h"
  2. #include <httplib.h>
  3. #include <chrono>
  4. #include <iomanip>
  5. #include <nlohmann/json.hpp>
  6. #include <regex>
  7. #include <sstream>
  8. #include <utility>
  9. #include "logger.h"
  10. AuthMiddleware::AuthMiddleware(AuthConfig config, std::shared_ptr<UserManager> userManager)
  11. : m_config(std::move(config)), m_userManager(std::move(userManager)) {
  12. }
  13. AuthMiddleware::~AuthMiddleware() = default;
  14. bool AuthMiddleware::initialize() {
  15. try {
  16. // Validate configuration
  17. if (!validateConfig(m_config)) {
  18. return false;
  19. }
  20. // Initialize JWT auth if needed
  21. if (m_config.authMethod == AuthMethod::JWT) {
  22. m_jwtAuth = std::make_unique<JWTAuth>(m_config.jwtSecret,
  23. m_config.jwtExpirationMinutes,
  24. m_config.authRealm,
  25. m_config.jwtAudience);
  26. }
  27. // Initialize default paths
  28. initializeDefaultPaths();
  29. return true;
  30. } catch (const std::exception& e) {
  31. return false;
  32. }
  33. }
  34. AuthContext AuthMiddleware::authenticate(const httplib::Request& req, httplib::Response& /*res*/) {
  35. AuthContext context;
  36. context.authenticated = false;
  37. try {
  38. // Check if authentication is completely disabled
  39. if (isAuthenticationDisabled()) {
  40. context = createGuestContext();
  41. context.authenticated = true;
  42. return context;
  43. }
  44. // Check if path requires authentication
  45. if (!requiresAuthentication(req.path)) {
  46. context = createGuestContext();
  47. // Only allow guest access if authentication is completely disabled or guest access is explicitly enabled
  48. context.authenticated = (isAuthenticationDisabled() || m_config.enableGuestAccess);
  49. return context;
  50. }
  51. // Try different authentication methods based on configuration
  52. switch (m_config.authMethod) {
  53. case AuthMethod::JWT:
  54. context = authenticateJwt(req);
  55. break;
  56. case AuthMethod::API_KEY:
  57. context = authenticateApiKey(req);
  58. break;
  59. case AuthMethod::UNIX:
  60. context = authenticateUnix(req);
  61. break;
  62. case AuthMethod::PAM:
  63. context = authenticatePam(req);
  64. break;
  65. case AuthMethod::OPTIONAL:
  66. // Try JWT first, then API key, then allow guest
  67. context = authenticateJwt(req);
  68. if (!context.authenticated) {
  69. context = authenticateApiKey(req);
  70. }
  71. if (!context.authenticated && m_config.enableGuestAccess) {
  72. context = createGuestContext();
  73. context.authenticated = true;
  74. }
  75. break;
  76. case AuthMethod::NONE:
  77. default:
  78. context = createGuestContext();
  79. context.authenticated = true;
  80. break;
  81. }
  82. // Check if user has required permissions for this path
  83. if (context.authenticated && !hasPathAccess(req.path, context.permissions)) {
  84. context.authenticated = false;
  85. context.errorMessage = "Insufficient permissions for this endpoint";
  86. context.errorCode = "INSUFFICIENT_PERMISSIONS";
  87. }
  88. // Log authentication attempt
  89. logAuthAttempt(req, context, context.authenticated);
  90. } catch (const std::exception& e) {
  91. context.authenticated = false;
  92. context.errorMessage = "Authentication error: " + std::string(e.what());
  93. context.errorCode = "AUTH_ERROR";
  94. }
  95. return context;
  96. }
  97. bool AuthMiddleware::requiresAuthentication(const std::string& path) const {
  98. // First check if authentication is completely disabled
  99. if (isAuthenticationDisabled()) {
  100. return false;
  101. }
  102. // Authentication is enabled, check if path is explicitly public
  103. // Only paths in publicPaths are accessible without authentication
  104. if (pathMatchesPattern(path, m_config.publicPaths)) {
  105. return false;
  106. }
  107. // All other paths require authentication when auth is enabled
  108. return true;
  109. }
  110. bool AuthMiddleware::requiresAdminAccess(const std::string& path) const {
  111. return pathMatchesPattern(path, m_config.adminPaths);
  112. }
  113. bool AuthMiddleware::requiresUserAccess(const std::string& path) const {
  114. return pathMatchesPattern(path, m_config.userPaths);
  115. }
  116. bool AuthMiddleware::hasPathAccess(const std::string& path,
  117. const std::vector<std::string>& permissions) const {
  118. // Check admin paths
  119. if (requiresAdminAccess(path)) {
  120. return JWTAuth::hasPermission(permissions, UserManager::Permissions::ADMIN);
  121. }
  122. // Check user paths
  123. if (requiresUserAccess(path)) {
  124. return JWTAuth::hasAnyPermission(permissions, {UserManager::Permissions::USER_MANAGE,
  125. UserManager::Permissions::ADMIN});
  126. }
  127. // Default: allow access if authenticated
  128. return true;
  129. }
  130. AuthMiddleware::AuthHandler AuthMiddleware::createMiddleware(AuthHandler handler) {
  131. return [this, handler](const httplib::Request& req, httplib::Response& res, const AuthContext& /*context*/) {
  132. // Authenticate request
  133. AuthContext authContext = authenticate(req, res);
  134. // Check if authentication failed
  135. if (!authContext.authenticated) {
  136. sendAuthError(res, authContext.errorMessage, authContext.errorCode);
  137. return;
  138. }
  139. // Call the next handler
  140. handler(req, res, authContext);
  141. };
  142. }
  143. void AuthMiddleware::sendAuthError(httplib::Response& res,
  144. const std::string& message,
  145. const std::string& errorCode,
  146. int statusCode) {
  147. nlohmann::json error = {
  148. {"error", {{"message", message}, {"code", errorCode}, {"timestamp", std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count()}}}};
  149. res.set_header("Content-Type", "application/nlohmann::json");
  150. res.set_header("WWW-Authenticate", "Bearer realm=\"" + m_config.authRealm + "\"");
  151. res.status = statusCode;
  152. res.body = error.dump();
  153. }
  154. void AuthMiddleware::sendAuthzError(httplib::Response& res,
  155. const std::string& message,
  156. const std::string& errorCode) {
  157. nlohmann::json error = {
  158. {"error", {{"message", message}, {"code", errorCode}, {"timestamp", std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count()}}}};
  159. res.set_header("Content-Type", "application/nlohmann::json");
  160. res.status = 403;
  161. res.body = error.dump();
  162. }
  163. void AuthMiddleware::addPublicPath(const std::string& path) {
  164. m_config.publicPaths.push_back(path);
  165. }
  166. void AuthMiddleware::addAdminPath(const std::string& path) {
  167. m_config.adminPaths.push_back(path);
  168. }
  169. void AuthMiddleware::addUserPath(const std::string& path) {
  170. m_config.userPaths.push_back(path);
  171. }
  172. AuthContext AuthMiddleware::authenticateApiKey(const httplib::Request& req) {
  173. AuthContext context;
  174. context.authenticated = false;
  175. if (!m_userManager) {
  176. context.errorMessage = "User manager not available";
  177. context.errorCode = "USER_MANAGER_UNAVAILABLE";
  178. return context;
  179. }
  180. // Extract API key from header
  181. std::string apiKey = extractToken(req, "X-API-Key");
  182. if (apiKey.empty()) {
  183. context.errorMessage = "Missing API key";
  184. context.errorCode = "MISSING_API_KEY";
  185. return context;
  186. }
  187. // Validate API key
  188. auto result = m_userManager->authenticateApiKey(apiKey);
  189. if (!result.success) {
  190. context.errorMessage = result.errorMessage;
  191. context.errorCode = result.errorCode;
  192. return context;
  193. }
  194. // API key is valid
  195. context.authenticated = true;
  196. context.userId = result.userId;
  197. context.username = result.username;
  198. context.role = result.role;
  199. context.permissions = result.permissions;
  200. context.authMethod = "API_KEY";
  201. return context;
  202. }
  203. AuthContext AuthMiddleware::authenticateUnix(const httplib::Request& req) {
  204. AuthContext context;
  205. context.authenticated = false;
  206. if (!m_userManager) {
  207. context.errorMessage = "User manager not available";
  208. context.errorCode = "USER_MANAGER_UNAVAILABLE";
  209. return context;
  210. }
  211. // Check if Unix authentication is the configured method
  212. if (m_config.authMethod != AuthMethod::UNIX) {
  213. context.errorMessage = "Unix authentication not available";
  214. context.errorCode = "UNIX_AUTH_UNAVAILABLE";
  215. return context;
  216. }
  217. // For Unix auth, we need to get username and password from request
  218. std::string username;
  219. std::string password;
  220. // Try to extract from JSON body (for login API)
  221. std::string contentType = req.get_header_value("Content-Type");
  222. if (contentType.find("application/nlohmann::json") != std::string::npos) {
  223. try {
  224. nlohmann::json body = nlohmann::json::parse(req.body);
  225. username = body.value("username", "");
  226. password = body.value("password", "");
  227. } catch (const nlohmann::json::exception& e) {
  228. // Invalid JSON, continue with other methods
  229. }
  230. }
  231. // If no credentials in body, check headers
  232. if (username.empty()) {
  233. username = req.get_header_value("X-Unix-User");
  234. // Also check Authorization header for Bearer token (for UI requests after login)
  235. if (username.empty()) {
  236. std::string authHeader = req.get_header_value("Authorization");
  237. if (!authHeader.empty() && authHeader.find("Bearer ") == 0) {
  238. std::string token = authHeader.substr(7); // Remove "Bearer "
  239. // Check if this is a Unix token
  240. if (token.find("unix_token_") == 0) {
  241. // Extract username from token
  242. size_t lastUnderscore = token.find_last_of('_');
  243. if (lastUnderscore != std::string::npos) {
  244. username = token.substr(lastUnderscore + 1);
  245. }
  246. }
  247. }
  248. }
  249. }
  250. if (username.empty()) {
  251. // Check if this is a request for the login page or API endpoints
  252. // For UI requests, we'll let the UI handler show the login page
  253. // For API requests, we need to return an error
  254. std::string path = req.path;
  255. if (path.find("/ui/") == 0 || path == "/ui") {
  256. // This is a UI request, let it proceed to show the login page
  257. context = createGuestContext();
  258. context.authenticated = false; // Ensure it's false to trigger login page
  259. return context;
  260. } else {
  261. // This is an API request, return error
  262. context.errorMessage = "Missing Unix username";
  263. context.errorCode = "MISSING_UNIX_USER";
  264. return context;
  265. }
  266. }
  267. // Authenticate Unix user (with or without password depending on PAM availability)
  268. auto result = m_userManager->authenticateUnix(username, password);
  269. if (!result.success) {
  270. context.errorMessage = result.errorMessage;
  271. context.errorCode = result.errorCode;
  272. return context;
  273. }
  274. // Unix authentication successful
  275. context.authenticated = true;
  276. context.userId = result.userId;
  277. context.username = result.username;
  278. context.role = result.role;
  279. context.permissions = result.permissions;
  280. context.authMethod = "UNIX";
  281. return context;
  282. }
  283. AuthContext AuthMiddleware::authenticatePam(const httplib::Request& req) {
  284. AuthContext context;
  285. context.authenticated = false;
  286. if (!m_userManager) {
  287. context.errorMessage = "User manager not available";
  288. context.errorCode = "USER_MANAGER_UNAVAILABLE";
  289. return context;
  290. }
  291. // Check if PAM authentication is the configured method
  292. if (m_config.authMethod != AuthMethod::PAM) {
  293. context.errorMessage = "PAM authentication not available";
  294. context.errorCode = "PAM_AUTH_UNAVAILABLE";
  295. return context;
  296. }
  297. // For PAM auth, we need to get username and password from request
  298. // This could be from a JSON body for login requests
  299. std::string username;
  300. std::string password;
  301. // Try to extract from JSON body (for login API)
  302. std::string contentType = req.get_header_value("Content-Type");
  303. if (contentType.find("application/nlohmann::json") != std::string::npos) {
  304. try {
  305. nlohmann::json body = nlohmann::json::parse(req.body);
  306. username = body.value("username", "");
  307. password = body.value("password", "");
  308. } catch (const nlohmann::json::exception& e) {
  309. // Invalid JSON
  310. }
  311. }
  312. // If no credentials in body, check Authorization header for basic auth
  313. if (username.empty() || password.empty()) {
  314. std::string authHeader = req.get_header_value("Authorization");
  315. if (!authHeader.empty() && authHeader.find("Basic ") == 0) {
  316. // Decode basic auth
  317. std::string basicAuth = authHeader.substr(6); // Remove "Basic "
  318. // Note: In a real implementation, you'd decode base64 here
  319. // For now, we'll expect the credentials to be in the JSON body
  320. }
  321. }
  322. if (username.empty() || password.empty()) {
  323. // Check if this is a request for the login page or API endpoints
  324. // For UI requests, we'll let the UI handler show the login page
  325. // For API requests, we need to return an error
  326. std::string path = req.path;
  327. if (path.find("/ui/") == 0 || path == "/ui") {
  328. // This is a UI request, let it proceed to show the login page
  329. context = createGuestContext();
  330. context.authenticated = false; // Ensure it's false to trigger login page
  331. return context;
  332. } else {
  333. // This is an API request, return error
  334. context.errorMessage = "Missing PAM credentials";
  335. context.errorCode = "MISSING_PAM_CREDENTIALS";
  336. return context;
  337. }
  338. }
  339. // Authenticate PAM user
  340. auto result = m_userManager->authenticatePam(username, password);
  341. if (!result.success) {
  342. context.errorMessage = result.errorMessage;
  343. context.errorCode = result.errorCode;
  344. return context;
  345. }
  346. // PAM authentication successful
  347. context.authenticated = true;
  348. context.userId = result.userId;
  349. context.username = result.username;
  350. context.role = result.role;
  351. context.permissions = result.permissions;
  352. context.authMethod = "PAM";
  353. return context;
  354. }
  355. std::string AuthMiddleware::extractToken(const httplib::Request& req, const std::string& headerName) const {
  356. std::string authHeader = req.get_header_value(headerName);
  357. if (headerName == "Authorization") {
  358. return JWTAuth::extractTokenFromHeader(authHeader);
  359. }
  360. return authHeader;
  361. }
  362. AuthContext AuthMiddleware::createGuestContext() const {
  363. AuthContext context;
  364. context.authenticated = false;
  365. context.userId = "guest";
  366. context.username = "guest";
  367. context.role = "guest";
  368. context.permissions = UserManager::getDefaultPermissions(UserManager::UserRole::GUEST);
  369. context.authMethod = "none";
  370. return context;
  371. }
  372. void AuthMiddleware::logAuthAttempt(const httplib::Request& req,
  373. const AuthContext& context,
  374. bool success) const {
  375. auto now = std::chrono::system_clock::now();
  376. auto time_t_now = std::chrono::system_clock::to_time_t(now);
  377. std::stringstream timestamp_ss;
  378. timestamp_ss << std::put_time(std::gmtime(&time_t_now), "%Y-%m-%dT%H:%M:%S");
  379. auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()) % 1000;
  380. timestamp_ss << "." << std::setfill('0') << std::setw(3) << ms.count() << "Z";
  381. std::string clientIp = getClientIp(req);
  382. std::string userAgent = getUserAgent(req);
  383. std::string username = context.authenticated ? context.username : "unknown";
  384. std::string status = success ? "success" : "failure";
  385. std::string message = "Authentication " + status + " - " +
  386. "timestamp=" + timestamp_ss.str() + ", " +
  387. "ip=" + (clientIp.empty() ? "unknown" : clientIp) + ", " +
  388. "username=" + (username.empty() ? "unknown" : username) + ", " +
  389. "path=" + req.path + ", " +
  390. "user-agent=" + (userAgent.empty() ? "unknown" : userAgent);
  391. if (success) {
  392. Logger::getInstance().info(message);
  393. } else {
  394. Logger::getInstance().warning(message);
  395. }
  396. }
  397. std::string AuthMiddleware::getClientIp(const httplib::Request& req) {
  398. // Check various headers for client IP
  399. std::string ip = req.get_header_value("X-Forwarded-For");
  400. if (ip.empty()) {
  401. ip = req.get_header_value("X-Real-IP");
  402. }
  403. if (ip.empty()) {
  404. ip = req.get_header_value("X-Client-IP");
  405. }
  406. if (ip.empty()) {
  407. ip = req.remote_addr;
  408. }
  409. return ip;
  410. }
  411. std::string AuthMiddleware::getUserAgent(const httplib::Request& req) {
  412. return req.get_header_value("User-Agent");
  413. }
  414. bool AuthMiddleware::validateConfig(AuthConfig config) {
  415. // Validate JWT configuration
  416. if (config.authMethod == AuthMethod::JWT) {
  417. if (config.jwtSecret.empty()) {
  418. // Will be auto-generated
  419. }
  420. if (config.jwtExpirationMinutes <= 0 || config.jwtExpirationMinutes > 1440) {
  421. return false; // Max 24 hours
  422. }
  423. }
  424. // Validate realm
  425. if (config.authRealm.empty()) {
  426. return false;
  427. }
  428. return true;
  429. }
  430. void AuthMiddleware::initializeDefaultPaths() {
  431. // Parse custom public paths if provided
  432. if (!m_config.customPublicPaths.empty()) {
  433. // Split comma-separated paths
  434. std::stringstream ss(m_config.customPublicPaths);
  435. std::string path;
  436. while (std::getline(ss, path, ',')) {
  437. // Trim whitespace
  438. path.erase(0, path.find_first_not_of(" \t"));
  439. path.erase(path.find_last_not_of(" \t") + 1);
  440. if (!path.empty()) {
  441. // Ensure path starts with /
  442. if (path[0] != '/') {
  443. path = "/" + path;
  444. }
  445. m_config.publicPaths.push_back(path);
  446. }
  447. }
  448. }
  449. // Add default public paths - only truly public endpoints when auth is enabled
  450. if (m_config.publicPaths.empty()) {
  451. m_config.publicPaths = {
  452. "/api/health",
  453. "/api/status"
  454. // Note: Model discovery endpoints removed from public paths
  455. // These now require authentication when auth is enabled
  456. };
  457. }
  458. // Add default admin paths
  459. if (m_config.adminPaths.empty()) {
  460. m_config.adminPaths = {
  461. "/api/users",
  462. "/api/auth/users",
  463. "/api/system/restart"};
  464. }
  465. // Add default user paths
  466. if (m_config.userPaths.empty()) {
  467. m_config.userPaths = {
  468. "/api/generate",
  469. "/api/queue",
  470. "/api/models/load",
  471. "/api/models/unload",
  472. "/api/auth/profile",
  473. "/api/auth/api-keys",
  474. // Model discovery endpoints moved to user paths
  475. "/api/models",
  476. "/api/models/types",
  477. "/api/models/directories",
  478. "/api/samplers",
  479. "/api/schedulers",
  480. "/api/parameters"};
  481. }
  482. }
  483. bool AuthMiddleware::isAuthenticationDisabled() const {
  484. return m_config.authMethod == AuthMethod::NONE;
  485. }
  486. AuthContext AuthMiddleware::authenticateJwt(const httplib::Request& req) {
  487. AuthContext context;
  488. context.authenticated = false;
  489. if (!m_jwtAuth) {
  490. context.errorMessage = "JWT authentication not available";
  491. context.errorCode = "JWT_AUTH_UNAVAILABLE";
  492. return context;
  493. }
  494. // Extract JWT token from Authorization header
  495. std::string token = extractToken(req, "Authorization");
  496. if (token.empty()) {
  497. context.errorMessage = "Missing JWT token";
  498. context.errorCode = "MISSING_JWT_TOKEN";
  499. return context;
  500. }
  501. // Validate JWT token
  502. auto result = m_jwtAuth->validateToken(token);
  503. if (!result.success) {
  504. context.errorMessage = result.errorMessage;
  505. context.errorCode = "INVALID_JWT_TOKEN";
  506. return context;
  507. }
  508. // JWT is valid
  509. context.authenticated = true;
  510. context.userId = result.userId;
  511. context.username = result.username;
  512. context.role = result.role;
  513. context.permissions = result.permissions;
  514. context.authMethod = "JWT";
  515. return context;
  516. }
  517. std::vector<std::string> AuthMiddleware::getRequiredPermissions(const std::string& path) const {
  518. std::vector<std::string> permissions;
  519. if (requiresAdminAccess(path)) {
  520. permissions.push_back("admin");
  521. } else if (requiresUserAccess(path)) {
  522. permissions.push_back("user");
  523. }
  524. return permissions;
  525. }
  526. bool AuthMiddleware::pathMatchesPattern(const std::string& path, const std::vector<std::string>& patterns) {
  527. for (const auto& pattern : patterns) {
  528. if (pattern == path) {
  529. return true;
  530. }
  531. // Check if pattern is a prefix
  532. if (pattern.back() == '/' && path.find(pattern) == 0) {
  533. return true;
  534. }
  535. // Simple wildcard matching
  536. if (pattern.find('*') != std::string::npos) {
  537. std::regex regexPattern(pattern, std::regex_constants::icase);
  538. if (std::regex_match(path, regexPattern)) {
  539. return true;
  540. }
  541. }
  542. }
  543. return false;
  544. }
  545. AuthConfig AuthMiddleware::getConfig() const {
  546. return m_config;
  547. }
  548. void AuthMiddleware::updateConfig(AuthConfig config) {
  549. m_config = std::move(config);
  550. }
  551. // Factory functions
  552. namespace AuthMiddlewareFactory {
  553. std::unique_ptr<AuthMiddleware> createDefault(std::shared_ptr<UserManager> userManager,
  554. const std::string& /*dataDir*/) {
  555. AuthConfig config;
  556. config.authMethod = AuthMethod::NONE;
  557. config.enableGuestAccess = true;
  558. config.jwtSecret = "";
  559. config.jwtExpirationMinutes = 60;
  560. config.authRealm = "stable-diffusion-rest";
  561. return std::make_unique<AuthMiddleware>(config, userManager);
  562. }
  563. std::unique_ptr<AuthMiddleware> createMultiMethod(std::shared_ptr<UserManager> userManager,
  564. AuthConfig config) {
  565. return std::make_unique<AuthMiddleware>(std::move(config), userManager);
  566. }
  567. std::unique_ptr<AuthMiddleware> createJwtOnly(std::shared_ptr<UserManager> userManager,
  568. const std::string& jwtSecret,
  569. int jwtExpirationMinutes) {
  570. AuthConfig config;
  571. config.authMethod = AuthMethod::JWT;
  572. config.enableGuestAccess = false;
  573. config.jwtSecret = jwtSecret;
  574. config.jwtExpirationMinutes = jwtExpirationMinutes;
  575. config.authRealm = "stable-diffusion-rest";
  576. return std::make_unique<AuthMiddleware>(config, userManager);
  577. }
  578. std::unique_ptr<AuthMiddleware> createApiKeyOnly(std::shared_ptr<UserManager> userManager) {
  579. AuthConfig config;
  580. config.authMethod = AuthMethod::API_KEY;
  581. config.enableGuestAccess = false;
  582. config.authRealm = "stable-diffusion-rest";
  583. return std::make_unique<AuthMiddleware>(config, userManager);
  584. }
  585. } // namespace AuthMiddlewareFactory