flux_restful.auth package

Submodules

flux_restful.auth.base module

The authentication backend interface.

A backend answers two questions: “are these credentials valid?” and “who does this bearer token belong to?” Everything else in the server (routers, the web views, job submission) only ever sees a Principal, so backends can be swapped by setting FLUX_AUTH_BACKEND without touching the rest of the code.

class flux_restful.auth.base.AuthBackend[source]

Bases: object

Base class for authentication backends.

Subclasses set name (the value used in FLUX_AUTH_BACKEND) and override authenticate and/or resolve. Token issuing and verification default to server-signed JWTs and only need overriding when tokens come from somewhere else (for example an OIDC provider).

authenticate(db: Session, username: str, password: str) → Principal | None[source]

Validate a username and password. Return a Principal, or None.

issue_token(principal: Principal) → str[source]

Issue an access token for an authenticated principal.

issues_tokens = True
name = 'base'
resolve(db: Session, username: str) → Principal | None[source]

Resolve a username (typically a token subject) to a Principal.

Return None if the user is unknown or inactive.

supports_password = False
validate() → None[source]

Check configuration at startup. Raise AuthError if unusable.

verify_token(db: Session, token: str) → Principal | None[source]

Verify a bearer token and return its Principal, or None if invalid.

exception flux_restful.auth.base.AuthError[source]

Bases: Exception

A backend is misconfigured or cannot perform the requested operation.

class flux_restful.auth.base.Principal(user_name: str, is_superuser: bool = False, is_active: bool = True, backend: str = '')[source]

Bases: object

An authenticated identity, independent of the backend that produced it.

user_name is the login name and, in multi-user mode, the system account that jobs are submitted as.

backend : str = ''
is_active : bool = True
is_superuser : bool = False
user_name : str
flux_restful.auth.base.is_admin(username: str) → bool[source]

Users listed in FLUX_ADMIN_USERS are superusers regardless of backend.

flux_restful.auth.base.is_system_user(username: str) → bool[source]

Whether a name is a system account that a backend may map to.

Accounts below FLUX_MIN_UID (root, daemons) are never acceptable, so a bad claim mapping or a misconfigured PAM stack cannot yield them.

flux_restful.auth.database module

class flux_restful.auth.database.DatabaseBackend[source]

Bases: AuthBackend

Users and bcrypt password hashes stored in the server database.

Accounts are managed with flux-restful (init, add-user).

authenticate(db: Session, username: str, password: str) → Principal | None[source]

Validate a username and password. Return a Principal, or None.

name = 'database'
resolve(db: Session, username: str) → Principal | None[source]

Resolve a username (typically a token subject) to a Principal.

Return None if the user is unknown or inactive.

supports_password = True
class flux_restful.auth.database.SharedSecretBackend[source]

Bases: DatabaseBackend

Database users plus the shared-secret token handshake.

This is what FLUX_REQUIRE_AUTH=true historically meant, and what the Python client and the Flux Operator use: the client encodes its username and password with FLUX_SECRET_KEY and posts it to /v1/token to obtain an access token. The shared secret is only ever used to decode that handshake; access tokens are signed with the server-only signing key.

name = 'shared-secret'
validate() → None[source]

Check configuration at startup. Raise AuthError if unusable.

flux_restful.auth.none module

class flux_restful.auth.none.NoAuthBackend[source]

Bases: AuthBackend

No authentication: every request is anonymous.

This is the default and is only appropriate when the server is otherwise isolated (for example, only reachable inside a Flux Operator pod). Actions that need a superuser, such as stopping the service, are unavailable.

issues_tokens = False
name = 'none'
supports_password = False

flux_restful.auth.oidc module

class flux_restful.auth.oidc.OidcBackend(jwks_loader=None)[source]

Bases: AuthBackend

Accept tokens issued by an OpenID Connect / OAuth2 provider.

Clients obtain a token from the provider themselves and present it as a bearer token. The server verifies the signature against the provider’s JWKS, plus the issuer (FLUX_OIDC_ISSUER) and audience (FLUX_OIDC_AUDIENCE, normally the client id). The username is the “sub” claim by default: it is the only claim OIDC guarantees to be stable and unique for an issuer, and claims like preferred_username or email may be user-chosen. Superusers are the usernames (claim values) in FLUX_ADMIN_USERS.

FLUX_OIDC_USERNAME_CLAIM selects another claim. In multi-user mode, where the username is the system account jobs run as, it must be set explicitly and the value must be an existing account.

The server never issues tokens for this backend and password login is not available.

issue_token(principal: Principal) → str[source]

Issue an access token for an authenticated principal.

issues_tokens = False
jwks(refresh: bool = False) → dict[source]
property multi_user : bool
name = 'oidc'
supports_password = False
property username_claim : str
validate() → None[source]

Check configuration at startup. Raise AuthError if unusable.

verify_token(db: Session, token: str) → Principal | None[source]

Verify a bearer token and return its Principal, or None if invalid.

flux_restful.auth.pam module

class flux_restful.auth.pam.PamBackend(authenticator=None)[source]

Bases: AuthBackend

Authenticate against the host’s PAM stack (system accounts).

Requires the python-pam package. The PAM service is FLUX_PAM_SERVICE (default “login”). Superusers are the accounts in FLUX_ADMIN_USERS. This pairs naturally with multi-user mode, where jobs run as the system user.

authenticate(db: Session, username: str, password: str) → Principal | None[source]

Validate a username and password. Return a Principal, or None.

name = 'pam'
resolve(db: Session, username: str) → Principal | None[source]

Resolve a username (typically a token subject) to a Principal.

Return None if the user is unknown or inactive.

supports_password = True
validate() → None[source]

Check configuration at startup. Raise AuthError if unusable.

flux_restful.auth.tokens module

Server-issued access tokens.

Tokens are signed with FLUX_TOKEN_SIGNING_KEY, which only the server knows. This is deliberately a different key from FLUX_SECRET_KEY: that one is shared with clients for the token handshake, so it must never be able to mint tokens.

exception flux_restful.auth.tokens.TokenError[source]

Bases: Exception

The token is malformed, expired, or not signed by this server.

flux_restful.auth.tokens.create_access_token(subject: str, backend: str, expires_delta: timedelta | None = None) → str[source]

Create a signed access token for a subject (username).

flux_restful.auth.tokens.decode_access_token(token: str) → dict[source]

Decode and verify a token issued by this server. Raises TokenError.

Module contents

Pluggable authentication for the Flux RESTful API.

Select a backend with FLUX_AUTH_BACKEND (see flux_restful.core.config.KNOWN_AUTH_BACKENDS). Routers only depend on get_backend() and the Principal it returns.

flux_restful.auth.create_backend(name: str) → AuthBackend[source]

Instantiate and validate a backend by name.

flux_restful.auth.describe() → dict[source]

Public description of how to authenticate, safe to show to anyone.

flux_restful.auth.get_backend() → AuthBackend[source]

The configured backend (created on first use).

flux_restful.auth.handshake_enabled() → bool[source]

The shared-secret handshake on /v1/token needs a password backend and a secret.

flux_restful.auth.reset_backend() → None[source]

Forget the cached backend so it is re-created from settings (for tests).