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
- Variables
- func Register(r chi.Router, d Deps)
- type AuthBody
- type Deps
- type EmailFactor
- type KickQueue
- type LDAPAuthenticator
- type LoginContext
- type MemKickQueue
- type NoEmailFactor
- type NoTOTP
- type RateConfig
- type Strategy
- type StrategyProvider
- type TOTPVerifier
- type TokenIssuer
- type UserInfo
- type UserPayload
- type WebSessionResolver
Constants ¶
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.
const DefaultKickTTL = 2 * time.Minute
DefaultKickTTL bounds how long a kick waits for the device's heartbeat.
Variables ¶
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 ¶
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.
type NoEmailFactor ¶
type NoEmailFactor struct{}
NoEmailFactor is the EmailFactor used when e-mail is not wired.
func (NoEmailFactor) LoginFactorRequired ¶
LoginFactorRequired implements EmailFactor.
func (NoEmailFactor) SendLoginCode ¶
SendLoginCode implements EmailFactor.
func (NoEmailFactor) VerifyLoginCode ¶
VerifyLoginCode implements EmailFactor.
type NoTOTP ¶
type NoTOTP struct{}
NoTOTP is the TOTPVerifier used when 2FA is not wired: nobody is enrolled.
type RateConfig ¶
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
WebSessionResolver maps a web client session id to its panel user.