HTTP API Reference¶
luci-sso exposes a CGI script at https://<router>/cgi-bin/luci-sso/. This document describes every endpoint, the cookies it reads and sets, the headers present on every response, the error response format, and the constraints applied to all requests.
Base URL¶
https://<router>/cgi-bin/luci-sso
All paths below are relative to this base. Only HTTPS is accepted — the router's uhttpd configuration should not serve this path over HTTP.
Endpoints¶
GET / — Probe or initiate login¶
Without parameters: Starts the OIDC authorization code flow. Generates a PKCE pair, nonce, and state token; saves them to a handshake file; and redirects the browser to the IdP's authorization endpoint.
| Rate-limited | Yes |
| Requires configuration | Yes |
| Success response | 302 with Location: <IdP authorize URL> |
| Sets cookie | __Host-luci_sso_state (see Cookies) |
With ?action=enabled: Returns whether SSO is configured and enabled. Does not touch the OIDC flow. Safe to poll from scripts.
| Rate-limited | No |
| Requires configuration | No |
| Success response | 200 application/json — {"enabled": true} or {"enabled": false} |
GET /callback — Handle IdP redirect¶
Called automatically by the browser after the user authenticates at the IdP. The IdP appends code and state to the URL.
| Query parameter | Description |
|---|---|
code |
Authorization code issued by the IdP. Single-use, short-lived. |
state |
Must match the value stored in the __Host-luci_sso_state cookie. |
| Rate-limited | Yes |
| Requires configuration | Yes |
| Success response | 302 with Location: /cgi-bin/luci/ |
| Sets cookies | sysauth_https, sysauth (see Cookies) |
| Clears cookie | __Host-luci_sso_state (Max-Age=0) |
On failure, returns an error page (see Error responses).
GET /logout — End the session¶
Destroys the active LuCI session and redirects the browser. If the IdP advertises an end_session_endpoint in its discovery document, the browser is sent there (RP-Initiated Logout). Otherwise, the browser is sent to /.
| Query parameter | Description |
|---|---|
stoken |
CSRF token. Must match the token field of the current UBUS session. LuCI includes this automatically in logout links. |
| Rate-limited | Yes |
| Requires configuration | Yes — discovery runs to find end_session_endpoint |
| Success response | 302 with Location: <end_session_endpoint or /> |
| Clears cookies | sysauth_https, sysauth (Max-Age=0) |
Error on missing/invalid stoken |
403 — CSRF check failure |
If no active session is found (cookie absent or session already expired), the endpoint returns 302 / without error.
Cookies¶
__Host-luci_sso_state¶
Carries the handshake state token during the OIDC flow. The __Host- prefix enforces that the cookie is only sent over HTTPS and is scoped to the root path.
| Attribute | Value |
|---|---|
| Name | __Host-luci_sso_state |
HttpOnly |
Yes |
Secure |
Yes |
SameSite |
Lax |
Path |
/ |
Max-Age |
300 (5 minutes) — matches the handshake lifetime |
sysauth_https¶
The LuCI session cookie for HTTPS connections.
| Attribute | Value |
|---|---|
| Name | sysauth_https |
HttpOnly |
Yes |
Secure |
Yes |
SameSite |
Strict |
Path |
/ |
Max-Age |
Not set — session cookie (expires when browser closes) |
sysauth¶
A compatibility alias for sysauth_https. LuCI reads whichever is present.
| Attribute | Value |
|---|---|
| Name | sysauth |
HttpOnly |
Yes |
Secure |
Yes |
SameSite |
Strict |
Path |
/ |
Security headers¶
Every response — success, redirect, and error — includes these headers:
| Header | Value |
|---|---|
Content-Security-Policy |
default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self'; frame-ancestors 'none'; |
X-Content-Type-Options |
nosniff |
X-Frame-Options |
DENY |
Cache-Control |
no-store |
Referrer-Policy |
no-referrer |
These are set unconditionally in web.uc:render() and cannot be suppressed.
Error responses¶
When a request fails, the CGI returns a plain-text body with a user-facing message. Internal error codes are not included in the response body — they appear only in the system log (logread -e luci-sso).
HTTP/1.1 400 Bad Request
Content-Type: text/plain
Error: Your session has expired or is invalid. You MUST try logging in again.
| HTTP status | When it occurs |
|---|---|
400 Bad Request |
Malformed callback parameters |
401 Unauthorized |
Authentication flow failed |
403 Forbidden |
CSRF token missing or invalid on logout |
404 Not Found |
Path does not match any endpoint |
429 Too Many Requests |
Rate limit exceeded |
431 Request Header Fields Too Large |
Input exceeded 16 KB |
503 Service Unavailable |
SSO is not configured or not enabled |
500 Internal Server Error |
Unexpected crash or system failure |
For the mapping from internal error codes to HTTP statuses, see Log Messages.
Request limits¶
These constraints apply to all rate-limited endpoints:
| Limit | Value |
|---|---|
| Maximum query string length | 16 384 bytes |
| Maximum cookie header length | 16 384 bytes |
| Maximum number of query parameters | 100 |
| Maximum number of cookies | 100 |
| Rate limit | 50 requests per 60-second window |
The rate limit is global — it counts all requests across all source addresses. It does not apply to ?action=enabled.
Requests that exceed the size limits return 431. Requests that exceed the rate limit return 429.
Back-channel limits¶
These constraints apply to the router's outbound requests to the IdP (discovery, JWKS, token endpoint, UserInfo).
| Limit | Value |
|---|---|
| Maximum IdP response body (discovery, JWKS, token, UserInfo) | 256 KB |
| Maximum ID Token size | 16 KB |
Responses that exceed the response size limit are rejected before being parsed — the router logs OIDC_DISCOVERY_FAILED, JWKS_FETCH_FAILED, TOKEN_ENDPOINT_NETWORK_ERROR, or USERINFO_NETWORK_ERROR depending on which back-channel call triggered it. ID Tokens that exceed the token size limit cause ID_TOKEN_VERIFICATION_FAILED.