tunnelctl
Concepts

Authentication & tokens

How the CLI authenticates with SSO, and the two kinds of tokens involved.

tunnelctl uses your organization's identity provider (OIDC, e.g. Keycloak) for authentication. There is no separate tunnelctl password — you sign in with SSO.

User authentication (OIDC)

When you run tunnelctl login, the CLI performs an OIDC Authorization Code flow with PKCE:

The CLI opens the identity provider in your browser (or uses the device-code flow with --no-browser).
After you sign in, the provider redirects back with an authorization code, which the CLI exchanges for an access token and a refresh token.
Tokens are stored at ~/.config/tunnelctl/oidc.json with mode 0600.
The access token is short-lived (~5 minutes); the CLI refreshes it automatically before each API call.

The access token is sent as Authorization: Bearer <token> to the control-plane API, which validates its signature, issuer, audience, and expiry, and derives your permissions from provider-specific claims.

Connection tokens

Authenticating you to the API is separate from authorizing an FRP client to serve a specific slug. For that, the server issues a per-tunnel connection token (a signed JWT) when you bring a tunnel up. The CLI stores it alongside the tunnel's metadata in ~/.config/tunnelctl/tunnels/<slug>.json.

The tunnel edge validates each connection by checking three things, so a leaked token can't be used to hijack a slug:

  1. Signature — the token is signed by the server.
  2. Serial — the token's serial must match the tunnel's current serial; rotating the token bumps the serial and invalidates older ones.
  3. Slug agreement — the slug in the signed token, the database row, and the client's requested proxy name must all match.

Rotation, not expiry

Connection tokens don't rely on short expiry — they're revoked by rotating the serial. Issuing a fresh token (e.g. via the server's rotate operation) immediately invalidates the previous one.

API keys (automation)

For non-interactive use, the server can issue scoped API keys (signed JWTs) to a caller that is already authenticated via OIDC. An API key can only carry a subset of the issuing user's permissions, so it can't be used to escalate privileges.

On this page