auth_middleware.cpp 24 KB

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