# How CasaOS Implements JWT-Based Authentication and Token Management

> Discover how CasaOS uses JWT authentication and token management with Echo middleware to validate API requests via ECDSA signatures injecting user IDs for secure access.

- Repository: [IceWhale/CasaOS](https://github.com/IceWhaleTech/CasaOS)
- Tags: how-to-guide
- Published: 2026-06-26

---

**CasaOS validates every API request using JSON Web Tokens (JWT) processed through Echo middleware that verifies ECDSA signatures against a runtime public key and injects the user ID into request headers for downstream handlers.**

CasaOS is an open-source home cloud platform developed by IceWhaleTech that secures its REST APIs using stateless JWT-based authentication. The implementation leverages the Echo web framework alongside cryptographic utilities from the CasaOS-Common library to enforce token validation on all `/v1` and `/v2` endpoints.

## JWT Middleware Integration in the Echo Router

CasaOS centralizes authentication logic in dedicated router files, applying middleware during route group initialization. This approach ensures consistent protection across the entire API surface without requiring individual handler modifications.

### Token Validation Pipeline in route/v1.go and route/v2.go

The router configuration in [`route/v1.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1.go) (lines 44-56) and [`route/v2.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v2.go) (lines 71-84) applies `echo_middleware.JWTWithConfig` to the API groups. The middleware configuration defines a custom `ParseTokenFunc` that delegates cryptographic validation to `jwt.Validate` from the CasaOS-Common package.

The configuration includes three critical components:

- **Skipper function**: Returns `true` for localhost requests (`::1` or `127.0.0.1`), bypassing authentication for local development
- **ParseTokenFunc**: Validates the token signature using an ECDSA public key retrieved via `external.GetPublicKey(config.CommonInfo.RuntimePath)`
- **TokenLookupFuncs**: Extracts tokens from either the `Authorization` header (Bearer scheme) or the `token` query parameter

```go
// route/v1.go – lines 44-56
v1Group.Use(echo_middleware.JWTWithConfig(echo_middleware.JWTConfig{
    Skipper: func(c echo.Context) bool {
        return c.RealIP() == "::1" || c.RealIP() == "127.0.0.1"
    },
    ParseTokenFunc: func(token string, c echo.Context) (interface{}, error) {
        valid, claims, err := jwt.Validate(
            token,
            func() (*ecdsa.PublicKey, error) { 
                return external.GetPublicKey(config.CommonInfo.RuntimePath) 
            })
        if err != nil || !valid {
            return nil, echo.ErrUnauthorized
        }
        c.Request().Header.Set("user_id", strconv.Itoa(claims.ID))
        return claims, nil
    },
    TokenLookupFuncs: []echo_middleware.ValuesExtractor{
        func(ctx echo.Context) ([]string, error) {
            if len(ctx.Request().Header.Get(echo.HeaderAuthorization)) > 0 {
                return []string{ctx.Request().Header.Get(echo.HeaderAuthorization)}, nil
            }
            return []string{ctx.QueryParam("token")}, nil
        },
    },
}))

```

### Asymmetric Key Verification

The validation process uses **asymmetric cryptography** rather than shared secrets. The `jwt.Validate` function receives a callback returning an `*ecdsa.PublicKey` loaded from the runtime path. This design eliminates the need to distribute private signing keys to the API server, maintaining a secure separation between token issuance and verification.

## Token Extraction and Claims Processing

Once the middleware validates the signature, it processes the JWT claims to identify the requesting user and make that context available to business logic.

### Authorization Header and Query Parameter Support

The middleware supports dual token sources through the `TokenLookupFuncs` array. It first checks the `Authorization` header for a Bearer token. If the header is absent, it falls back to the `token` query parameter, enabling authentication in scenarios such as WebSocket connections or direct browser links where header manipulation is difficult.

### User ID Propagation to Downstream Handlers

After successful validation, the middleware extracts the `ID` field from the JWT claims and injects it into the request header as `user_id`. This allows handlers to access the authenticated user identifier via `c.Request().Header.Get("user_id")` without re-parsing the token. The claims object includes standard JWT fields such as `exp` (expiration) and `iat` (issued at) alongside the user-specific `ID`.

## Configuration and Public Key Management

CasaOS maintains JWT configuration in [`internal/conf/config.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/internal/conf/config.go), which includes a `JwtSecret` field defined as:

```go
JwtSecret string `json:"jwt_secret" env:"JWT_SECRET"`

```

While this field supports symmetric HMAC signing as a fallback, the current implementation exclusively uses the asymmetric approach. The system loads the ECDSA public key from `config.CommonInfo.RuntimePath` using the `external.GetPublicKey` utility provided by CasaOS-Common, reading the PEM-encoded key at runtime.

## Practical Implementation Examples

### Making an Authenticated Request

Use the Bearer token scheme in the Authorization header:

```bash
curl -H "Authorization: Bearer $TOKEN" \
     http://localhost:80/v1/sys/hardware

```

### Accessing User Context in Handlers

Retrieve the authenticated user ID from the injected header:

```go
func GetSystemHardwareInfo(c echo.Context) error {
    userID := c.Request().Header.Get("user_id")
    log.Printf("hardware request from user %s", userID)
    
    // Implement business logic here
    return c.JSON(http.StatusOK, hardwareInfo)
}

```

### Authenticating via Query Parameter

For requests where headers cannot be set:

```

http://localhost:80/v1/file?token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...

```

## Summary

- **Centralized Middleware**: CasaOS uses `echo_middleware.JWTWithConfig` in [`route/v1.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1.go) and [`route/v2.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v2.go) to protect all API endpoints under `/v1` and `/v2`
- **ECDSA Verification**: Token validation relies on asymmetric cryptography with public keys loaded from `config.CommonInfo.RuntimePath` via `external.GetPublicKey`
- **Flexible Token Sources**: The system accepts tokens from the `Authorization` header (Bearer) or the `token` query parameter
- **Context Propagation**: Validated user IDs propagate to handlers via the `user_id` request header, enabling stateless user identification
- **Hybrid Configuration**: While [`internal/conf/config.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/internal/conf/config.go) includes `JwtSecret` for symmetric signing, the active implementation uses ECDSA verification exclusively

## Frequently Asked Questions

### How does CasaOS validate JWT tokens on each request?

CasaOS validates JWT tokens through a custom `ParseTokenFunc` configured in the Echo middleware. This function calls `jwt.Validate` from the CasaOS-Common library, which verifies the token's ECDSA signature against the public key stored in the runtime directory. Validation occurs automatically for every request to `/v1` and `/v2` endpoints, rejecting unauthorized requests with a 401 status before they reach business logic.

### What signing algorithm does CasaOS use for JWT verification?

CasaOS implements **ECDSA** (Elliptic Curve Digital Signature Algorithm) for JWT verification. The middleware uses a public key loaded via `external.GetPublicKey(config.CommonInfo.RuntimePath)` to verify signatures. While the configuration struct includes a `JwtSecret` field for symmetric HMAC signing, the current implementation exclusively uses the asymmetric ECDSA approach.

### Where does CasaOS store the public key for token verification?

The public key resides in the runtime path specified by `config.CommonInfo.RuntimePath`. The `external.GetPublicKey` function in CasaOS-Common reads the PEM-encoded ECDSA public key from this location. This design allows the main CasaOS application to verify tokens without access to the private signing key, which remains secured in the authentication service or key management system.

### Can CasaOS fall back to symmetric JWT signing?

Yes, the codebase includes infrastructure for symmetric signing through the `JwtSecret` configuration field in [`internal/conf/config.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/internal/conf/config.go). However, the current middleware implementation in [`route/v1.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v1.go) and [`route/v2.go`](https://github.com/IceWhaleTech/CasaOS/blob/main/route/v2.go) does not utilize this secret, instead relying exclusively on the ECDSA public key validation via `jwt.Validate`. The symmetric option remains available for future implementation or custom builds requiring HMAC-based verification.