# How the S-UI API Interface Works: Session and Token Authentication Explained

> Understand the S-UI API interface by learning about its session and token authentication. This guide explains how the dual-layer HTTP system handles browser and programmatic access.

- Repository: [Alireza Ahmadi/s-ui](https://github.com/alireza0/s-ui)
- Tags: api-reference
- Published: 2026-05-22

---

**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`](https://github.com/alireza0/s-ui/blob/main/main.go) initializes the router and registers both handlers under the `/api` base path:

```go
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`](https://github.com/alireza0/s-ui/blob/main/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:

```go
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`](https://github.com/alireza0/s-ui/blob/main/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`](https://github.com/alireza0/s-ui/blob/main/api/apiService.go). This layer aggregates domain-specific services through struct embedding:

```go
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 via `UserService.Login` and creates a session cookie using `SetLoginUser`
- **`Save`** – Persists configuration changes through `SettingService.Save` and returns updated partial data via `LoadPartialData`
- **`LoadData`** / **`LoadPartialData`** – Retrieves full or filtered configuration by calling service methods like `InboundService.GetAll` and `ClientService.GetAll`
- **`GetTokens`** / **`AddToken`** / **`DeleteToken`** – Manage v2 API tokens through `UserService` methods
- **`GetSingboxConfig`** – 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`](https://github.com/alireza0/s-ui/blob/main/api/session.go) and support the v1 authentication flow. These utilities manage signed cookies containing the username and expiration timestamp:

- **`SetLoginUser`** – Creates the session cookie
- **`GetLoginUser`** – Retrieves the authenticated user from the cookie
- **`ClearSession`** – 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:

```bash
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:

```bash
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:

```bash
TOKEN=eyJhbGciOi...
curl -H "Token: ${TOKEN}" http://localhost:8080/api/load

```

### Update Client Data (v2)

Modify client settings using token authentication:

```bash
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 `checkLogin` middleware in [`api/apiHandler.go`](https://github.com/alireza0/s-ui/blob/main/api/apiHandler.go), while v2 uses header-based tokens verified by `checkToken` in [`api/apiV2Handler.go`](https://github.com/alireza0/s-ui/blob/main/api/apiV2Handler.go)
- **Business logic is centralized** in the `ApiService` struct ([`api/apiService.go`](https://github.com/alireza0/s-ui/blob/main/api/apiService.go)), which embeds domain services like `UserService` and `ConfigService`
- **Session state** for v1 is managed through helpers in [`api/session.go`](https://github.com/alireza0/s-ui/blob/main/api/session.go) (`SetLoginUser`, `GetLoginUser`, `ClearSession`), whereas v2 maintains an in-memory token list loaded via `ReloadTokens`
- **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 the `service` package

## 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`](https://github.com/alireza0/s-ui/blob/main/api/apiHandler.go) within the middleware closure (lines 24-29) that calls `checkLogin`, which is defined in [`api/session.go`](https://github.com/alireza0/s-ui/blob/main/api/session.go). The v2 token verification lives in [`api/apiV2Handler.go`](https://github.com/alireza0/s-ui/blob/main/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`](https://github.com/alireza0/s-ui/blob/main/apiHandler.go) (v1) or [`apiV2Handler.go`](https://github.com/alireza0/s-ui/blob/main/apiV2Handler.go) (v2), implement the business logic as a method on `ApiService` in [`api/apiService.go`](https://github.com/alireza0/s-ui/blob/main/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.