Documentation
¶
Overview ¶
Package auth implements core.Auth: it maps the X-Plex-Token a Plex app presents to an Ether user, and keeps the users table with per-user settings (stream limit, content rating cap, languages, sections).
A token is recognised, in order, as
- the server's own token (the owner),
- a server access token synced from plex.tv for the owner or a friend,
- any other plex.tv token, validated against plex.tv (GET /api/v2/user) and checked for access to this server (owner id, synced friend, or the server in that token's resources list).
Results of (3) are cached (positive and negative) so plex.tv is asked at most once per token per TTL; during a plex.tv outage a previously valid token keeps working for StaleTTL.
Index ¶
- Constants
- func HashToken(token string) string
- func IsOwnerSession(token string) bool
- func StoredIdentity(ctx context.Context, db *sql.DB, id int64) (*core.User, error)
- type Options
- type PlexTVValidator
- type Service
- func (s *Service) ActiveUser(ctx context.Context, id int64) (*core.User, error)
- func (s *Service) AllUsers(ctx context.Context) ([]UserInfo, error)
- func (s *Service) Authenticate(ctx context.Context, token string) (*core.User, error)
- func (s *Service) BootstrapOwner(ctx context.Context, p config.Plex) error
- func (s *Service) Close()
- func (s *Service) FlushSeen(ctx context.Context) error
- func (s *Service) Invalidate(token string)
- func (s *Service) IssueDelegation(ctx context.Context, userID int64) (string, error)
- func (s *Service) IssueOwnerSession(ctx context.Context, credential string) (string, time.Time, error)
- func (s *Service) Owner(ctx context.Context) (*core.User, error)
- func (s *Service) OwnerSession(ctx context.Context, token string) (*core.User, error)
- func (s *Service) RevokeOwnerSession(ctx context.Context, token string) error
- func (s *Service) SetBus(bus clusterbus.Bus)
- func (s *Service) SetOwner(ctx context.Context, a plextv.Account, serverToken string) error
- func (s *Service) SyncFriends(ctx context.Context, friends []plextv.Friend) error
- func (s *Service) UpdateUser(ctx context.Context, u core.User) error
- func (s *Service) User(ctx context.Context, id int64) (*core.User, error)
- func (s *Service) Users(ctx context.Context) ([]core.User, error)
- type UserInfo
- type Validator
Constants ¶
const ( // OwnerSessionTTL is how long an unused session stays valid. Use // extends it, at most once a day, up to OwnerSessionMaxAge. OwnerSessionTTL = 30 * 24 * time.Hour // OwnerSessionMaxAge bounds a session regardless of use. OwnerSessionMaxAge = 90 * 24 * time.Hour )
Owner console sessions. Signing in to the admin console used to hand the browser the owner's plex.tv account token, which then sat in localStorage where any script on the page could read it and take over the Plex account. The console now exchanges that credential for a server-local session: it only opens the admin API, is never accepted as a Plex or playback credential, and can be revoked. Only hashes persist, and every check reads the shared database, so a session issued on one cluster node works on all of them and dies everywhere the moment it is revoked or ownership changes.
Variables ¶
This section is empty.
Functions ¶
func IsOwnerSession ¶ added in v0.2.48
IsOwnerSession reports whether token has the shape of an owner console session. It says nothing about validity.
Types ¶
type Options ¶
type Options struct {
// Validator checks unknown tokens; nil means only server and synced
// tokens are accepted.
Validator Validator
// CacheTTL is how long a plex.tv-validated token is trusted (15m).
CacheTTL time.Duration
// NegativeTTL is how long a rejected token is remembered (2m).
NegativeTTL time.Duration
// StaleTTL lets a validated token keep working this long past CacheTTL
// while plex.tv is unreachable (24h).
StaleTTL time.Duration
// MaxCacheEntries bounds the token caches (10000).
MaxCacheEntries int
// MaxConcurrentValidations bounds distinct unknown tokens being checked
// with plex.tv (16). Excess requests fail fast; cached tokens still work.
MaxConcurrentValidations int
Logger *slog.Logger
Now func() time.Time
}
Options configures the Service.
type PlexTVValidator ¶
PlexTVValidator is the production Validator.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service implements core.Auth and plextv.UserSync.
func (*Service) ActiveUser ¶
ActiveUser returns a current access snapshot, including for stream grants.
func (*Service) Authenticate ¶
Authenticate implements core.Auth.
func (*Service) BootstrapOwner ¶
BootstrapOwner creates the owner from config without network access (on startup, before plex.tv is reachable) and registers the server token.
func (*Service) FlushSeen ¶
FlushSeen persists last-seen times (call periodically and at shutdown). Every node flushes its own observations; the update only ever moves last_seen_at forward, so nodes flushing in any order keep the latest.
func (*Service) Invalidate ¶
Invalidate forgets cached validations of token.
func (*Service) IssueDelegation ¶ added in v0.2.7
IssueDelegation creates a server-local credential, never a plex.tv account token. Only hashes persist; expiry and membership are checked on every use, including when another cluster node receives the credential.
func (*Service) IssueOwnerSession ¶ added in v0.2.48
func (s *Service) IssueOwnerSession(ctx context.Context, credential string) (string, time.Time, error)
IssueOwnerSession exchanges an owner credential (a plex.tv token Ether accepts as the owner's) for a console session. Sessions and delegated credentials cannot be exchanged, so a stolen session cannot extend itself past its absolute lifetime.
func (*Service) OwnerSession ¶ added in v0.2.48
OwnerSession returns the owner a console session belongs to. It fails with core.ErrUnauthorized once the session expired, was revoked, or its user is no longer the active owner.
func (*Service) RevokeOwnerSession ¶ added in v0.2.48
RevokeOwnerSession ends a console session. Unknown tokens are not an error: signing out twice is harmless.
func (*Service) SetBus ¶
func (s *Service) SetBus(bus clusterbus.Bus)
SetBus connects the cluster bus (nil disconnects): after every write this service tells the other nodes to reload users and tokens, and it reloads when they write. A standalone server never calls it.
func (*Service) SetOwner ¶
SetOwner implements plextv.UserSync: it makes a the owner and registers serverToken as the owner's token. A unique index admits one owner row, so when another node changes the owner concurrently the losing transaction is retried against the winner's committed state.
func (*Service) SyncFriends ¶
SyncFriends implements plextv.UserSync: it replaces the synced access tokens, upserts friends' profiles (never their Ether settings) and deactivates every non-owner absent from the authoritative membership list, including viewers first discovered through personal-token validation.
func (*Service) UpdateUser ¶
UpdateUser implements core.Auth. It changes only per-user settings: stream limit, content rating cap, languages and allowed sections. Identity fields come from plex.tv and are ignored.
type UserInfo ¶
type UserInfo struct {
core.User
Active bool `json:"active"`
Origin string `json:"origin"`
Home bool `json:"home"`
Restricted bool `json:"restricted"`
PlexSections []int `json:"plexSections"`
LastSeen time.Time `json:"lastSeen,omitempty"`
}
UserInfo is a user plus bookkeeping for the admin UI.
type Validator ¶
type Validator interface {
// Account returns the account behind token. An error matching
// core.ErrUnauthorized means the token is invalid; other errors mean
// plex.tv could not answer.
Account(ctx context.Context, token string) (*plextv.Account, error)
// HasServer reports whether token's resources list includes this
// server (i.e. the account was granted access).
HasServer(ctx context.Context, token string) (bool, error)
}
Validator checks tokens Ether does not already know against plex.tv.