---
title: "POST /auth/connect."
method: POST
path: "/auth/connect"
tags: ["system"]
---

# POST /auth/connect.

`POST /auth/connect`

Validates the bearer token in the Authorization header, registers the client
PID, and returns a JWT session token wrapped in the canonical envelope.

Rate limiting: This endpoint has no session token yet, so rate limiting
is applied by client IP address (from ConnectInfo). This is trustworthy
because it comes from the TCP stack, not caller-controlled input.

NOTE on ConnectInfo: ConnectInfo<SocketAddr> reads the socket address from
the TCP connection metadata, NOT from HTTP headers. In test contexts (using
tower::ServiceExt::oneshot without a real TCP listener), the socket address
will be a default value (typically 127.0.0.1:0 or ::1:0). The unit tests
for SlidingWindowRateLimiter cover the rate limiting logic independently.
IP-based rate limiting for /auth/connect works correctly in production.

IMPORTANT: ConnectInfo<SocketAddr> works ONLY when the server uses
`into_make_service_with_connect_info::<SocketAddr>()`.
In test contexts using tower::ServiceExt::oneshot, ConnectInfo may be absent.
The handler uses Option<ConnectInfo<SocketAddr>> so tests work correctly.
IP-based rate limiting is skipped when ConnectInfo is unavailable (test-only code path).

## Request body

- ConnectRequest — Request body for `POST /auth/connect`.
  - `clientType` string, required — Client type: `"gui"`, `"cli"`, or `"other"`.
  - `pid` integer, required — Client process ID. Used for PID whitelist verification in JWT middleware.

## Response `200`

JWT session token issued

- SessionTokenEnvelope — Canonical success envelope: `{ "data": T, "ts": <unix millis i64> }`. `ts` is `chrono::Utc::now().timestamp_millis()`, set in the webserver handler via [`ApiEnvelope::now`] (the contract carries only the type + the clock helper, not a hard dependency on when the handler reads the clock). `rename_all = "camelCase"` is a no-op for the single-word fields here but is declared for forward-compat. IMPORTANT (utoipa v4): every concrete `ApiEnvelope<X>` that needs a named OpenAPI component is declared in the `#[aliases(...)]` block below. Add a new alias line whenever a new payload type needs enveloping. NEVER register the bare `ApiEnvelope` in `components(schemas(...))` — utoipa errors on a bare generic, and an un-aliased generic inlines an anonymous schema.
  - `data` SessionTokenResponse, required — Response body for `POST /auth/connect`. Mirrors the current flat shape returned by the webserver `ConnectResponse` and decoded by the native client's local `ConnectResponse`: `{ "sessionToken", "expiresInSecs", "refreshAtSecs" }`.
    - `expiresInSecs` integer, required — Token time-to-live in seconds.
    - `refreshAtSecs` integer, required — Recommended refresh time in seconds.
    - `sessionToken` string, required — HS256-signed JWT session token.
  - `ts` integer, required — Server time when the response was built (unix epoch milliseconds).

## Other responses

- `400` — Malformed connect request
- `401` — Missing or invalid bearer token
- `429` — Too many requests from this client IP
- `500` — Failed to sign the session token

---

[API](https://skmtc.net/uniclipboard/apis/uniclipboard-daemon-api.md) · [All operations](https://skmtc.net/uniclipboard/apis/uniclipboard-daemon-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/uniclipboard/uniclipboard-daemon-api/revisions/cdaebaa4d46a/schema)
