How the S-UI API Interface Works: Session and Token Authentication Explained
The S-UI API interface is a dual-layer HTTP system built on the Gin framework, offering session-based authentication for browser interactions and token-based authentication for programmatic access, with both layers delegating business logic to a central ApiService struct.
The alireza0/s-ui repository implements a clean, modular API architecture that separates routing, authentication, and business logic. This design allows administrators to manage Sing-box configurations through both interactive web sessions and automated scripts. Understanding how the S-UI API interface handles request routing and security is essential for extending functionality or building custom integrations.
Architecture Overview
S-UI’s HTTP API is structured around two distinct authentication handlers that share a common service layer. The architecture relies on the Gin web framework for routing and middleware management.
At bootstrap, main.go initializes the router and registers both handlers under the /api base path:
router := gin.Default()
apiGroup := router.Group("/api")
api.NewAPIHandler(apiGroup, api.NewAPIv2Handler(apiGroup))
This setup creates a clear separation between the v1 session-based handler (APIHandler) for UI interactions and the v2 token-based handler (APIv2Handler) for external automation.
API Version 1: Session-Based Authentication
The v1 handler, defined in api/apiHandler.go, manages traditional browser-based authentication using HTTP cookies. It creates an APIHandler struct and installs middleware that validates login sessions for all routes except authentication endpoints.
Middleware Implementation
The session validation middleware (lines 24-29) checks the request path and validates the session cookie:
g.Use(func(c *gin.Context) {
path := c.Request.URL.Path
if !strings.HasSuffix(path, "login") && !strings.HasSuffix(path, "logout") {
checkLogin(c) // validates the session cookie
}
})
Routing Logic
APIHandler registers two generic catch-all routes that extract the action name from the URL:
- POST
/:postAction→postHandler - GET
/:getAction→getHandler
Inside these handlers, a switch statement matches the action parameter against supported operations (lines 34-63 for POST, lines 66-106 for GET) and forwards requests to the corresponding ApiService methods. Unknown actions return a JSON error via common.NewError.
API Version 2: Token-Based Authentication
The v2 handler in api/apiV2Handler.go provides stateless authentication suitable for scripts and third-party integrations. Instead of cookies, it relies on a Token header and maintains an in-memory list of valid tokens.
Token Management
When initialized, NewAPIv2Handler loads existing tokens from the database via ReloadTokens (lines 22-34) and stores them in memory. The checkToken middleware (lines 12-20) intercepts every request to verify the token header against this list, aborting the request if the token is missing or expired.
Endpoint Structure
Like v1, v2 uses generic POST and GET handlers, but exposes a smaller subset of actions focused on programmatic configuration management. This restricted surface area reduces security exposure for automated access.
Core Service Layer (ApiService)
All API actions converge on the ApiService struct defined in api/apiService.go. This layer aggregates domain-specific services through struct embedding:
type ApiService struct {
service.SettingService
service.UserService
service.ConfigService
service.ClientService
// …
}
Because services are embedded, ApiService can invoke methods directly (e.g., a.UserService.Login, a.ConfigService.GetConfig). Key methods include:
Login– Authenticates credentials viaUserService.Loginand creates a session cookie usingSetLoginUserSave– Persists configuration changes throughSettingService.Saveand returns updated partial data viaLoadPartialDataLoadData/LoadPartialData– Retrieves full or filtered configuration by calling service methods likeInboundService.GetAllandClientService.GetAllGetTokens/AddToken/DeleteToken– Manage v2 API tokens throughUserServicemethodsGetSingboxConfig– Streams the current Sing-box JSON configuration as a downloadable file
This delegation pattern keeps HTTP handlers thin and business logic reusable across CLI, UI, and API contexts.
Session Management Utilities
Session handling helpers reside in api/session.go and support the v1 authentication flow. These utilities manage signed cookies containing the username and expiration timestamp:
SetLoginUser– Creates the session cookieGetLoginUser– Retrieves the authenticated user from the cookieClearSession– Invalidates the session
The v2 handler does not use these helpers, relying entirely on the in-memory token list for stateless authentication.
Practical API Examples
The following curl commands demonstrate interaction patterns with the S-UI API interface.
Session-Based Login (v1)
Authenticate and establish a session cookie:
curl -X POST http://localhost:8080/api/login \
-d "user=admin&pass=secret"
# → sets cookie named "sui_session"
Token Generation (v2)
First obtain the session cookie, then request a token:
COOKIE=$(curl -s -c - http://localhost:8080/api/login -d "user=admin&pass=secret" | grep s_ui_session | awk '{print $7}')
curl -X POST http://localhost:8080/api/addToken \
-b "sui_session=${COOKIE}" \
-d "expiry=86400&desc=automation"
# → JSON: {"token":"eyJhbGciOi..."}
Fetch Configuration with Token (v2)
Use the token to retrieve configuration data:
TOKEN=eyJhbGciOi...
curl -H "Token: ${TOKEN}" http://localhost:8080/api/load
Update Client Data (v2)
Modify client settings using token authentication:
curl -X POST http://localhost:8080/api/save \
-H "Token: ${TOKEN}" \
-d "object=clients&action=update&data={\"id\":\"client1\",\"address\":\"10.0.0.2\"}"
Summary
- S-UI uses a dual-version API built on Gin, with v1 for browser sessions and v2 for programmatic access
- Authentication differs by version: v1 relies on signed cookies via
checkLoginmiddleware inapi/apiHandler.go, while v2 uses header-based tokens verified bycheckTokeninapi/apiV2Handler.go - Business logic is centralized in the
ApiServicestruct (api/apiService.go), which embeds domain services likeUserServiceandConfigService - Session state for v1 is managed through helpers in
api/session.go(SetLoginUser,GetLoginUser,ClearSession), whereas v2 maintains an in-memory token list loaded viaReloadTokens - Extension follows a simple pattern: add cases to the switch statements in the appropriate handler, implement logic in
ApiService, and optionally create new service interfaces in theservicepackage
Frequently Asked Questions
How do I choose between v1 and v2 of the S-UI API?
Use v1 for browser-based interactions that require persistent login sessions, as it handles cookies and session management automatically. Use v2 for automation scripts that need stateless authentication via tokens, allowing external tools to interact with the API without maintaining cookie sessions. The v2 interface exposes a smaller set of actions specifically designed for programmatic configuration management.
Where is the authentication logic implemented in the source code?
The v1 session validation resides in api/apiHandler.go within the middleware closure (lines 24-29) that calls checkLogin, which is defined in api/session.go. The v2 token verification lives in api/apiV2Handler.go within the checkToken middleware (lines 12-20). Both handlers delegate authorization checks to these middleware layers before reaching the routing logic.
Can I extend the API with custom endpoints?
Yes, extending the API requires three steps: add a new case to the postHandler or getHandler switch statement in either apiHandler.go (v1) or apiV2Handler.go (v2), implement the business logic as a method on ApiService in api/apiService.go, and if the functionality represents a new domain, create a corresponding service interface in the service package. This architecture keeps routing code thin while concentrating business logic in reusable service methods.
How does token expiration work in the v2 API?
When creating a token via AddToken, you specify an expiry duration in seconds. The token is stored in the database and loaded into memory via ReloadTokens when the handler initializes. The checkToken middleware validates each request against this in-memory list, rejecting tokens that are missing or have exceeded their expiration timestamp. Tokens persist across application restarts because they are stored in the database, but the in-memory cache requires a restart or explicit reload to recognize database changes.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →