Help us shape the future of Go! Take the 2026 Go Developer Survey

clientapi

package
v0.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package clientapi implements the RustDesk client wire API mounted at /api (login, logout, currentUser, address books, accessible users/peers, heartbeat, sysinfo, audit, device provisioning). See docs/research/client-api.md (normative) and SPEC section 3. The OIDC endpoints (/api/login-options, /api/oidc/*) live elsewhere (stream B) and reuse TokenIssuer and NewUserPayload from this package.

Files: account.go (login/logout/currentUser), ab.go + ab_legacy.go (address books), accessible.go (users/peers/device groups), device.go (heartbeat/sysinfo), audit.go, provisioning.go (deploy/cli/switch-grant/ record), kick.go (KickQueue), respond.go (wire helpers).

Index

Constants

View Source
const (
	// SettingABLegacyOnly (bool, default false): answer /api/ab/personal with
	// 404 so clients use the legacy single-blob address book.
	SettingABLegacyOnly = "clientapi.ab_legacy_only"
	// SettingABMaxPeers (int, default 0 = unlimited): max_peer_one_ab, also
	// enforced on peer/add.
	SettingABMaxPeers = "clientapi.ab_max_peers"
	// SettingDeployEnabled (bool, default true): POST /api/devices/deploy.
	SettingDeployEnabled = "clientapi.deploy_enabled"
)

Settings keys (JSON values in the settings table) read by the client API.

View Source
const DefaultKickTTL = 2 * time.Minute

DefaultKickTTL bounds how long a kick waits for the device's heartbeat.

Variables

View Source
var DefaultDeviceRate = RateConfig{Rate: 50, Burst: 300}

DefaultDeviceRate limits the unauthenticated device endpoints per client IP. It is generous because many devices may share one NAT address (each device sends a heartbeat every 3-15 s).

Functions

func Register

func Register(r chi.Router, d Deps)

Register mounts the client API on r (already scoped to /api). The /api/v1/* sub-trees are mounted separately by the app and must not be claimed here.

Types

type AuthBody

type AuthBody struct {
	Type        string       `json:"type"`
	AccessToken string       `json:"access_token,omitempty"`
	TfaType     string       `json:"tfa_type,omitempty"`
	Secret      string       `json:"secret,omitempty"`
	User        *UserPayload `json:"user,omitempty"`
}

AuthBody is the login response: {"type":"access_token","access_token",...} or a second-factor challenge {"type":"email_check","tfa_type":"tfa_check","secret"}.

type Deps

type Deps struct {
	Log     *slog.Logger
	Config  *config.Config
	Store   *store.Store
	AEAD    *crypto.AEAD
	JWT     *auth.ClientJWT
	Hasher  *auth.Hasher
	Limiter auth.Limiter
	Metrics prometheus.Registerer
	// Telemetry counts logins, issued tokens and audit records (nil: none).
	Telemetry *metrics.Panel

	// LDAP authenticates corporate users when password authentication fails or user is unprovisioned.
	LDAP LDAPAuthenticator
	// TOTP verifies the second factor (default NoTOTP).
	TOTP TOTPVerifier
	// Email is the e-mail second factor (default NoEmailFactor).
	Email EmailFactor
	// Kicks delivers admin "disconnect" requests through heartbeats (default:
	// a private MemKickQueue; share one with the admin API).
	Kicks KickQueue
	// WebSessions resolves web client session ids ("web-…") to panel users
	// for connection rules (nil: web clients count as unknown controllers).
	WebSessions WebSessionResolver
	// Strategies supplies heartbeat strategies (optional).
	Strategies StrategyProvider
	// DeviceRate limits unauthenticated device calls per IP (zero value:
	// DefaultDeviceRate; Rate < 0 disables).
	DeviceRate RateConfig
	// Now is the clock (default time.Now).
	Now func() time.Time
}

Deps are the collaborators the client API needs.

type EmailFactor

type EmailFactor interface {
	// LoginFactorRequired reports whether u must enter an e-mailed code
	// (checked only for users without TOTP).
	LoginFactorRequired(ctx context.Context, u *store.User) (bool, error)
	// SendLoginCode e-mails a sign-in code to u. Errors implementing
	// RetryAfter() time.Duration are rate limits.
	SendLoginCode(ctx context.Context, u *store.User, ip string) error
	// VerifyLoginCode checks and consumes a code (limited attempts per code).
	VerifyLoginCode(ctx context.Context, userID int64, code string) (bool, error)
}

EmailFactor is the e-mail second factor of POST /api/login (implemented by mailauth.Service). The default (NoEmailFactor) never requires it.

type KickQueue

type KickQueue interface {
	// Kick asks device rustdeskID to close connections connIDs (the client's
	// conn ids, as reported in heartbeat "conns" and audit "conn_id"); with
	// no ids every live connection is closed. Requests expire if the device
	// does not heartbeat in time.
	Kick(rustdeskID string, connIDs ...int32)
	// Take removes and returns the connection ids to close, given the live
	// connection ids the device reported in its heartbeat.
	Take(rustdeskID string, live []int32) []int32
}

KickQueue holds pending "disconnect" requests for devices. The admin API (stream B) calls Kick; the heartbeat handler calls Take and answers with {"disconnect": [...]}, which makes the client close those connections ("Closed manually by web console").

type LDAPAuthenticator added in v0.7.0

type LDAPAuthenticator interface {
	Authenticate(ctx context.Context, username, password string) (*store.User, error)
}

LDAPAuthenticator verifies corporate credentials and provisions/refreshes users.

type LoginContext

type LoginContext struct {
	// Method is store.LoginMethodPassword / LoginMethodOIDC / LoginMethodWebAuth.
	Method string
	// DeviceID and DeviceUUID are the client's RustDesk id and base64 uuid.
	DeviceID   string
	DeviceUUID string
	// Platform is deviceInfo.os; ClientKind is deviceInfo.type ("client", "browser").
	Platform   string
	ClientKind string
	IP         string
	UserAgent  string
}

LoginContext describes where a client login came from.

func LoginContextFromRequest

func LoginContextFromRequest(r *http.Request, method string) LoginContext

LoginContextFromRequest fills IP and UserAgent from r.

type MemKickQueue

type MemKickQueue struct {
	// contains filtered or unexported fields
}

MemKickQueue is an in-memory KickQueue (kicks do not survive a restart, which is fine: connection ids are only meaningful while connected).

func NewMemKickQueue

func NewMemKickQueue(ttl time.Duration, now func() time.Time) *MemKickQueue

NewMemKickQueue returns an empty queue (ttl <= 0: DefaultKickTTL; now nil: time.Now).

func (*MemKickQueue) Kick

func (q *MemKickQueue) Kick(rustdeskID string, connIDs ...int32)

Kick implements KickQueue.

func (*MemKickQueue) Len

func (q *MemKickQueue) Len() int

Len returns the number of devices with pending kicks.

func (*MemKickQueue) Take

func (q *MemKickQueue) Take(rustdeskID string, live []int32) []int32

Take implements KickQueue. A kick-all with no live connections stays pending (until it expires); specific ids that are not live are dropped.

type NoEmailFactor

type NoEmailFactor struct{}

NoEmailFactor is the EmailFactor used when e-mail is not wired.

func (NoEmailFactor) LoginFactorRequired

func (NoEmailFactor) LoginFactorRequired(context.Context, *store.User) (bool, error)

LoginFactorRequired implements EmailFactor.

func (NoEmailFactor) SendLoginCode

func (NoEmailFactor) SendLoginCode(context.Context, *store.User, string) error

SendLoginCode implements EmailFactor.

func (NoEmailFactor) VerifyLoginCode

func (NoEmailFactor) VerifyLoginCode(context.Context, int64, string) (bool, error)

VerifyLoginCode implements EmailFactor.

type NoTOTP

type NoTOTP struct{}

NoTOTP is the TOTPVerifier used when 2FA is not wired: nobody is enrolled.

func (NoTOTP) Enrolled

func (NoTOTP) Enrolled(context.Context, int64) (bool, error)

Enrolled implements TOTPVerifier.

func (NoTOTP) Verify

func (NoTOTP) Verify(context.Context, int64, string) (bool, error)

Verify implements TOTPVerifier.

type RateConfig

type RateConfig struct {
	Rate  float64
	Burst int
}

RateConfig is a token-bucket rate limit: Rate requests per second with bursts of Burst. A zero Rate disables limiting.

type Strategy

type Strategy struct {
	// ModifiedAt identifies the strategy version (the client echoes it back).
	ModifiedAt    int64
	ConfigOptions map[string]string
	Extra         map[string]string
}

Strategy is a device strategy pushed in heartbeat responses: config_options are merged into the client's options ("" removes a key).

type StrategyProvider

type StrategyProvider interface {
	StrategyFor(ctx context.Context, d *store.Device) (*Strategy, error)
}

StrategyProvider returns the strategy that applies to a device, or nil. The app wires internal/clientopts (server-wide client options such as the WebRTC ICE servers); the heartbeat sends no strategy when unset.

type TOTPVerifier

type TOTPVerifier interface {
	// Enrolled reports whether userID has a confirmed second factor.
	Enrolled(ctx context.Context, userID int64) (bool, error)
	// Verify checks a TOTP or recovery code for userID, enforcing replay
	// protection, and reports whether it was accepted.
	Verify(ctx context.Context, userID int64, code string) (bool, error)
}

TOTPVerifier is the second factor of POST /api/login. Stream B implements it over totp_secrets/recovery_codes; the default (NoTOTP) reports that no user is enrolled, so logins are single-factor.

type TokenIssuer

type TokenIssuer struct {
	// contains filtered or unexported fields
}

TokenIssuer mints client tokens: an HS256 JWT {"user_id","exp"} (what hbbs verifies) plus an auth_tokens row holding only sha256(token) with a sliding expiry. It also records the login event, binds the device to the user and stamps last_login_at. Stream B's OIDC auth-query uses it too.

func NewTokenIssuer

func NewTokenIssuer(d Deps) *TokenIssuer

NewTokenIssuer builds a TokenIssuer from d (Store, JWT, Config, Log, Now).

func (*TokenIssuer) Issue

func (ti *TokenIssuer) Issue(ctx context.Context, u *store.User, lc LoginContext) (string, error)

Issue creates a client token for u and returns it (shown to the client once).

type UserInfo

type UserInfo struct {
	EmailVerification      bool `json:"email_verification"`
	EmailAlarmNotification bool `json:"email_alarm_notification"`
}

UserInfo is UserPayload.info.

type UserPayload

type UserPayload struct {
	Name        string   `json:"name"`
	DisplayName string   `json:"display_name"`
	Avatar      string   `json:"avatar"`
	Email       string   `json:"email"`
	Note        string   `json:"note"`
	Status      int      `json:"status"`
	IsAdmin     bool     `json:"is_admin"`
	Info        UserInfo `json:"info"`
}

UserPayload is the user object of login, currentUser and OIDC auth-query responses. status: 1 normal, 0 disabled (-1 unverified is unused). info is always an object: the Rust client (OIDC auth-query) requires it.

func NewUserPayload

func NewUserPayload(u *store.User) UserPayload

NewUserPayload maps a user to the client payload.

type WebSessionResolver added in v0.11.0

type WebSessionResolver interface {
	User(id string) (int64, bool)
}

WebSessionResolver maps a web client session id to its panel user.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL