Documentation
¶
Overview ¶
Package webauth holds the browser sign-in capture core shared by openobserve-cli and the o3 desktop app: cookie shaping, the login-success heuristic, the injected capture script, and the policy deciding when a captured state counts as a completed login.
It is pure and dependency-light — it imports only the standard library and pkg/auth — so both a CLI driving a browser over the DevTools Protocol and a desktop app driving a native WebView can share it. Transport-specific code lives with its client (see pkg/webauth/cdp for the CLI's).
Index ¶
- func AssembleSession(cookies []Cookie, host, authorization, email string) pkgauth.Session
- func EarliestExpiry(cs []Cookie) time.Time
- func HostMatches(cookieDomain, host string) bool
- func LoginSucceeded(currentURL, host string, cookies []Cookie) bool
- func ProbeJS(deliver, host string) string
- func Replayable(s pkgauth.Session) bool
- func SerializeCookies(cs []Cookie) string
- type Cookie
- type Driver
- type Tracker
- type VerifyFunc
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AssembleSession ¶
AssembleSession builds the storable/replayable session from captured cookies. Only cookies scoped to host are kept; the Cookie header is serialized stably, and the soonest cookie expiry (if any) drives the connection UI. Authorization and email ride along as the header fallback and display metadata.
func EarliestExpiry ¶
EarliestExpiry returns the soonest non-zero expiry among cookies (drives the "expires in N days" UI). Zero when every cookie is a session cookie.
func HostMatches ¶
HostMatches reports whether a cookie's Domain applies to host. An empty domain is a host-only cookie and matches (the caller already scoped it). A leading dot is ignored; host equal to, or a subdomain of, the domain matches.
func LoginSucceeded ¶
LoginSucceeded is the capture heuristic, kept pure so it can be unit-tested in isolation. Login is considered complete when either:
- a known post-login OpenObserve auth cookie for the host is present, or
- the WebView has navigated off the login page AND at least one host cookie exists (OpenObserve lands on /web/ or /web/logs after login).
func ProbeJS ¶
ProbeJS returns the capture script injected into the sign-in page. The script calls the global function named deliver with an object carrying an {authorization} or {email} key as it observes them.
host scopes the script to the OpenObserve instance. This is a security boundary, not a nicety: the injection mechanisms both callers use (the CDP Page.addScriptToEvaluateOnNewDocument, and o3's WKUserScript) evaluate the script in EVERY document, including a full-page or iframed identity provider on a completely different origin. An IdP that sends its own Authorization header over XHR or fetch would otherwise have that header captured, written to the user's keychain, and replayed on every request to the OpenObserve instance — a third party's bearer token handed to a different server. So the script does nothing at all unless location.hostname is host, or a subdomain of it (the same relation HostMatches applies to cookie domains). Any port in host is stripped first, since location.hostname carries none. An empty host disables capture entirely rather than capturing everywhere.
It hooks BOTH window.fetch and XMLHttpRequest.prototype.setRequestHeader. Both are required: OpenObserve's web app issues its API requests through axios, which uses XHR, so a fetch-only hook captures nothing on a real instance and leaves only the short-lived session cookie. The Authorization header the SPA sends is the durable credential — typically Basic base64(email:token) — and it outlives the session cookie, so it is the more valuable of the two captures.
The script is defensive to the point of paranoia — every step is wrapped in try/catch and the original function is always called — because it runs inside a page we do not control and must never break the user's ability to log in.
func Replayable ¶
Replayable reports whether a captured state carries anything o3 could replay, and so is worth handing to the verifier.
Cookies are the usual authenticator, but they are NOT universal: an instance using native (email + password) login authenticates its own SPA with an Authorization header the browser builds locally and sets no cookies at all. Gating capture on cookies alone therefore never verified such a login — the window stayed open on the instance's home page after the user had signed in.
func SerializeCookies ¶
SerializeCookies renders cookies into a Cookie header value ("k1=v1; k2=v2"), sorted by name (then value) for a stable, testable result.
Types ¶
type Cookie ¶
type Cookie struct {
Name string
Value string
Domain string
Path string
Expires time.Time // zero for session cookies
Secure bool
HTTPOnly bool
}
Cookie is a single captured cookie, mirroring the fields the native WKHTTPCookieStore hands back.
func FilterForHost ¶
FilterForHost keeps only cookies whose domain applies to host.
type Driver ¶
type Driver interface {
Capture(loginURL, host string, verify VerifyFunc) (pkgauth.Session, error)
}
Driver opens a browser at loginURL and captures the session established there. host scopes which cookies are kept. Implementations block until login is verified, the user gives up, or a timeout elapses.
Two implementations exist: pkg/webauth/cdp drives a Chromium-family browser over the DevTools Protocol (used by the CLI, and by o3 off macOS), and o3's own darwin shell drives a native WKWebView.
type Tracker ¶
type Tracker struct {
// contains filtered or unexported fields
}
Tracker decides when an observed browser state counts as a completed login. It is shared by every transport so the CLI and o3 cannot drift on the question of when sign-in has actually finished.
The policy, in order:
- Assemble the observed state into a session and drop it unless something is replayable. An instance using native login sets no cookies at all, so "has cookies" is not the test — "has cookies OR an Authorization header" is.
- An authenticated API probe is the SOLE success signal. A benign cookie set on the login page, or an in-progress redirect to an external identity provider, must never be mistaken for a completed login, and only a real authenticated request can tell the difference.
- Verify each distinct state at most once. Without this a static page is re-probed on every tick, sending an authenticated request per second.
A nil verifier disables the probe and falls back to the pure cookie/URL heuristic in LoginSucceeded. That path exists for unit tests and for any caller with no API client to hand; production callers always pass a verifier.
func NewTracker ¶
func NewTracker(host string, verify VerifyFunc) *Tracker
NewTracker returns a Tracker scoping cookies to host and confirming captures with verify. A nil verify selects the heuristic fallback.
func (*Tracker) Observe ¶
func (t *Tracker) Observe(cookies []Cookie, currentURL, authz, email string) (pkgauth.Session, bool)
Observe feeds one sampled browser state to the policy. It returns the captured session and true exactly when sign-in is complete.
Observe blocks for the duration of the verification request, so callers should not hold a UI lock across it.
type VerifyFunc ¶
VerifyFunc confirms that a captured session actually authenticates against the instance's API. When a verifier is supplied, Capture reports success only once it returns true, so a login that merely set a benign preference/language cookie — or an in-progress SSO redirect that briefly leaves the login path — is never mistaken for a completed sign-in. A nil verifier disables the probe and falls back to the pure cookie/URL heuristic (used off-darwin and in unit tests).
func PingVerifier ¶
func PingVerifier(baseURL, org string, timeout time.Duration, maxRetries int) VerifyFunc
PingVerifier returns a VerifyFunc that confirms a session by making a real authenticated request to the instance. This is the sole success signal for capture: only an authenticated response proves the captured state works.
It takes plain values rather than a config.Defaults so this package stays free of pkg/config (and so of yaml.v3). Callers pass d.Timeout, d.MaxRetries.