ui/server/src/auth-1
Cockpit server, v0.9. Source: n/a.
Classes#
AuthConfigError#
Defined in: ui/server/src/auth.mjs:55
Thrown for a configuration the server must refuse to start with, as
opposed to a request-time failure. index.mjs prints .message and exits
1 rather than dumping a stack trace at whoever ran npm start.
Extends#
Error
Constructors#
Constructor
new AuthConfigError(
message):AuthConfigError
Defined in: ui/server/src/auth.mjs:56
Parameters
message
any
Returns
Overrides
Error.constructor
Properties#
name
name:
string
Defined in: ui/server/src/auth.mjs:58
Inherited from
Error.name
Variables#
DEFAULT_SESSION_TTL_HOURS#
constDEFAULT_SESSION_TTL_HOURS:8=8
Defined in: ui/server/src/auth.mjs:32
SESSION_COOKIE#
constSESSION_COOKIE:"construct_session"='construct_session'
Defined in: ui/server/src/auth.mjs:30
STATE_COOKIE#
constSTATE_COOKIE:"construct_oauth_state"='construct_oauth_state'
Defined in: ui/server/src/auth.mjs:31
Functions#
createAuth()#
createAuth(
config,deps?):object
Defined in: ui/server/src/auth.mjs:358
Build the auth surface from a resolved config.
deps exists so the tests can drive the OAuth round trip without a
network: fetchImpl stands in for GitHub, now for the clock and
randomToken for the CSRF state. onLogout runs when a session signs
out (with that session's login, or null when there is none; #569), so work the session started (the target app's dev server) stops with it.
Parameters#
config
any
deps?
Returns#
allows
allows: (
headers) =>boolean
May this request proceed? True when auth is not required at all, or when it carries a valid session. The WebSocket upgrade uses exactly this, so the socket can never be more permissive than the REST API.
Parameters
headers
any
Returns
boolean
config
config:
any
describeStartup
describeStartup: () =>
object[]
One-line startup summary + the warnings that matter.
Returns
object[]
handleCallback
handleCallback: (
req,res) =>Promise<any>
Parameters
req
any
res
any
Returns
Promise<any>
handleLogin
handleLogin: (
req,res) =>any
Parameters
req
any
res
any
Returns
any
handleLogout
handleLogout: (
req,res) =>void
Parameters
req
any
res
any
Returns
void
handleSession
handleSession: (
req,res) =>void
Parameters
req
any
res
any
Returns
void
handleTestLogin
handleTestLogin: (
req,res) =>any
The e2e escape hatch — and it is a login, not a bypass.
It does not make requests authenticate themselves and it does not
disable requireSession. It mints a normal signed session cookie for
one configured login, so Playwright proves the real gate by actually
logging in and holding a real cookie. There is no code path anywhere
in this module where "an env var is set" substitutes for "this request
carries a valid signed session".
Four independent things have to be true for it to do anything:
CONSTRUCT_AUTH_TEST_USERis set — otherwise this route 404s and does not exist.NODE_ENV !== 'production'— otherwise the process refused to start (resolveAuthConfig).- The server is bound to loopback — otherwise, likewise.
- The request carries the Cockpit's own Origin — so a page in another tab cannot mint one in a developer's browser. Fail-closed: a request with no Origin at all (plain curl) is refused too.
- The configured login passes the same allowlist the OAuth callback applies, when an allowlist exists. Also enforced at startup; kept here so the two login paths cannot drift apart later.
Parameters
req
any
res
any
Returns
any
isAllowedLogin
isAllowedLogin: (
login) =>any
Parameters
login
any
Returns
any
mountRoutes
mountRoutes: (
app) =>any
Register the /auth/* surface. These routes are deliberately outside
/api, and therefore outside the gate — you cannot log in through a
door that requires you to already be logged in.
Parameters
app
any
Returns
any
required
required:
any=config.required
requireSession
requireSession: (
req,res,next) =>any
The gate. index.mjs mounts this on /api above every route it
protects — not per route, so nothing can be added later that forgets
to be gated.
Parameters
req
any
res
any
next
any
Returns
any
sessionFromHeaders
sessionFromHeaders: (
headers) =>any
The verified session on a request, or null. Used by the middleware,
by the WebSocket upgrade check, and by /auth/session.
Parameters
headers
any
Returns
any
testLoginEnabled
testLoginEnabled:
boolean
isLoopbackHost()#
isLoopbackHost(
host):boolean
Defined in: ui/server/src/auth.mjs:48
A host string this server can bind to that is not reachable from another machine. Anything else counts as exposed, and exposure without authentication is refused at startup.
Parameters#
host
any
Returns#
boolean
parseAllowedLogins()#
parseAllowedLogins(
raw):string[]
Defined in: ui/server/src/auth.mjs:159
CONSTRUCT_ALLOWED_LOGINS — comma (or whitespace) separated GitHub
logins. Compared case-insensitively, because GitHub logins are.
Parameters#
raw
any
Returns#
string[]
parseCookies()#
parseCookies(
header):any
Defined in: ui/server/src/auth.mjs:70
Parse a Cookie: header into a null-prototype object. First occurrence
of a name wins (a second construct_session= appended by an attacker
cannot override the real one), and the result has no prototype so a
cookie literally named __proto__ is inert.
Parameters#
header
any
Returns#
any
resolveAuthConfig()#
resolveAuthConfig(
env?,__namedParameters?):object
Defined in: ui/server/src/auth.mjs:189
Resolve the whole auth posture from the environment, or throw AuthConfigError for a configuration the server must not run with.
The rules, in one place:
- OAuth credentials present -> login required, always, loopback or not.
- Bound to a non-loopback host without a way to log in -> refuse to start. This is what makes #277's loopback default meaningful: you cannot expose this server and still have it be open.
- Loopback with nothing configured -> unauthenticated, as today, with a
startup warning. Requiring every developer (and the e2e suite) to
register an OAuth app to run a local cockpit would break dev for a
threat model ("an attacker already on your machine, as you") that a
cookie does not address anyway.
CONSTRUCT_AUTH=requiredforces the gate on regardless. CONSTRUCT_AUTH=offis honoured only on loopback.
The test-login hatch (CONSTRUCT_AUTH_TEST_USER) is guarded here rather
than at request time on purpose: a box configured with it in production,
or exposed on a real interface, fails loudly at startup instead of
silently running with a back door.
Parameters#
env?
any = process.env
__namedParameters?
clientOrigin?
string = 'http://localhost:3000'
host?
string = '127.0.0.1'
port?
number = 4000
Returns#
object
allowedLogins
allowedLogins:
string[]
callbackUrl
callbackUrl:
string
clientId
clientId:
string
clientOrigin
clientOrigin:
string
clientSecret
clientSecret:
string
ephemeralSecret
ephemeralSecret:
boolean=!sessionSecretEnv
host
host:
string
loopback
loopback:
boolean
oauthConfigured
oauthConfigured:
boolean
required
required:
boolean
secureCookies
secureCookies:
boolean=!loopback
sessionSecret
sessionSecret:
any
sessionTtlMs
sessionTtlMs:
number
testUser
testUser:
string
serializeCookie()#
serializeCookie(
name,value,__namedParameters?):string
Defined in: ui/server/src/auth.mjs:91
Serialize one Set-Cookie value. httpOnly and sameSite=Lax are the
defaults rather than options a caller might forget.
Parameters#
name
any
value
any
__namedParameters?
httpOnly?
boolean = true
path?
string = '/'
sameSite?
string = 'Lax'
secure?
boolean = false
Returns#
string
signValue()#
signValue(
payload,secret):string
Defined in: ui/server/src/auth.mjs:113
base64url(JSON) + "." + base64url(HMAC-SHA256). base64url contains no
., so the separator is unambiguous.
Parameters#
payload
any
secret
any
Returns#
string
verifySession()#
verifySession(
token,secret,now?):any
Defined in: ui/server/src/auth.mjs:143
A session is a verified value that additionally names a GitHub login.
Parameters#
token
any
secret
any
now?
number = ...
Returns#
any
verifyValue()#
verifyValue(
token,secret,now?):any
Defined in: ui/server/src/auth.mjs:121
Verify signature and expiry. Returns the payload, or null — never throws, and never distinguishes why it failed to the caller, so a probing client learns nothing from the difference.
Parameters#
token
any
secret
any
now?
number = ...
Returns#
any