Auth Exceptions

Exceptions for the authentication module: OAuth2/OIDC providers, tokens, and session management.

Exception hierarchy

AuthError (base)
├── ConfigurationError        # Invalid auth configuration
├── ProviderNotFoundError     # Provider not in config
├── DiscoveryError            # OIDC discovery failed
├── TokenError
│   ├── TokenExpiredError     # Token has expired
│   ├── TokenRefreshError     # Refresh token failed
│   ├── TokenExchangeError    # Code exchange failed
│   ├── TokenValidationError  # Token validation failed
│   └── TokenStorageError     # Token storage operation failed
├── AuthorizationError        # Authorization flow failed
├── CallbackServerError       # OAuth callback server issue
└── PreflightError            # Preflight checks failed

Common failure modes

  • ProviderNotFoundError is raised when requesting a provider not defined in kstlib.conf.yml.

  • DiscoveryError surfaces when OIDC auto-discovery fails (network error, invalid issuer URL).

  • TokenExpiredError indicates the access token has expired and refresh is needed.

  • TokenRefreshError is raised when refresh token exchange fails (revoked, expired refresh token).

  • CallbackServerError occurs when the local OAuth callback server cannot start (port in use).

Usage patterns

Handling token expiry

from kstlib.auth import AuthSession
from kstlib.auth.errors import TokenExpiredError, TokenRefreshError

session = AuthSession(provider="keycloak")

try:
    token = session.get_valid_token()
except TokenExpiredError:
    try:
        token = session.refresh()
    except TokenRefreshError:
        # Refresh failed - need full re-auth
        token = session.login()

Safe provider lookup

from kstlib.auth.config import get_provider_config
from kstlib.auth.errors import ProviderNotFoundError

try:
    config = get_provider_config("my-provider")
except ProviderNotFoundError:
    logger.error("Provider not configured")
    config = get_provider_config("default")

Handling callback server issues

from kstlib.auth import AuthSession
from kstlib.auth.errors import CallbackServerError

try:
    session.login()
except CallbackServerError as e:
    logger.error(f"Callback server failed: {e}")
    print("Try closing other applications using port 8400")

API reference

Authentication module exceptions.

exception kstlib.auth.errors.AuthError(message, *, details=None)[source]

Bases: KstlibError

Base exception for all authentication errors.

__init__(self, message: 'str', *, details: 'dict[str, Any] | None' = None) 'None' -> None[source]

Initialize the auth error with a message and optional structured details.

exception kstlib.auth.errors.AuthExpiredError(message, *, token_source=None, suggested_action=None)[source]

Bases: AuthError

Raised when an authenticated request returns HTTP 401 indicating token expiration.

Surfaced by kstlib.rapi.client (and any other consumer) when a server response signals that the previously-valid access token has expired or been invalidated during the session. The user must re-authenticate via the appropriate channel (for example, sas-admin auth login for Viya, or via a dedicated OAuth client when configured in kstlib.auth).

Note

Distinct from TokenExpiredError. The two cover different lifecycle points and originate from different sub-systems :

  • AuthExpiredError (this class, inherits from AuthError) is raised by kstlib.rapi.client when the server returns HTTP 401 at runtime, signalling that a token which was valid at send time has been expired or invalidated by the identity provider during the session.

  • TokenExpiredError (inherits from TokenError) is raised by kstlib.auth when a loaded token is detected as already expired before the request is sent (client-side pre-flight check).

token_source

Optional label identifying where the token was loaded from (for example, '~/.sas/credentials.json', 'env:KSTLIB_TOKEN', 'sops:secrets/api.sops.json'). None when the source is unknown.

suggested_action

Optional human-readable hint guiding the user toward a successful re-authentication (for example, 'Run: sas-admin auth login -u <user>'). None when no contextual hint is available.

Examples

>>> err = AuthExpiredError(
...     "Access token expired (HTTP 401).",
...     token_source="~/.sas/credentials.json",
...     suggested_action="Run: sas-admin auth login -u <user>",
... )
>>> err.token_source
'~/.sas/credentials.json'
>>> isinstance(err, AuthError)
True
__init__(self, message: 'str', *, token_source: 'str | None' = None, suggested_action: 'str | None' = None) 'None' -> None[source]

Initialize AuthExpiredError.

Parameters:
  • message (str) – Human-readable description of the expiration (typically including the HTTP status and a short rationale, never the raw token or response body).

  • token_source (str | None) – Optional label for where the token came from (used by callers to surface a contextual hint without exposing the secret material itself).

  • suggested_action (str | None) – Optional hint pointing the user to the right re-authentication procedure.

exception kstlib.auth.errors.AuthorizationError(reason, *, error_code=None, error_description=None)[source]

Bases: AuthError

Raised during authorization flow failures.

__init__(self, reason: 'str', *, error_code: 'str | None' = None, error_description: 'str | None' = None) 'None' -> None[source]

Initialize with the reason for the failure plus optional OAuth error code and description.

exception kstlib.auth.errors.CallbackServerError(reason, *, port=None)[source]

Bases: AuthError

Raised when the local callback server fails to start or receive callback.

__init__(self, reason: 'str', *, port: 'int | None' = None) 'None' -> None[source]

Initialize with the reason for the callback server failure and the port that was in use.

exception kstlib.auth.errors.ConfigurationError(message, *, details=None)[source]

Bases: AuthError

Raised when auth configuration is invalid or missing.

exception kstlib.auth.errors.DiscoveryError(issuer, reason)[source]

Bases: AuthError

Raised when OIDC discovery fails.

__init__(self, issuer: 'str', reason: 'str') 'None' -> None[source]

Initialize with the failing issuer URL and the reason for the failure.

exception kstlib.auth.errors.PreflightError(step, reason)[source]

Bases: AuthError

Raised when preflight validation fails.

__init__(self, step: 'str', reason: 'str') 'None' -> None[source]

Initialize with the failing preflight step name and the reason for the failure.

exception kstlib.auth.errors.ProviderNotFoundError(provider_name)[source]

Bases: AuthError

Raised when a named provider is not configured.

__init__(self, provider_name: 'str') 'None' -> None[source]

Initialize with the name of the missing provider.

exception kstlib.auth.errors.TokenError(message, *, details=None)[source]

Bases: AuthError

Base exception for token-related errors.

exception kstlib.auth.errors.TokenExchangeError(reason, *, error_code=None)[source]

Bases: TokenError

Raised when authorization code exchange fails.

__init__(self, reason: 'str', *, error_code: 'str | None' = None) 'None' -> None[source]

Initialize with the reason for the exchange failure and an optional OAuth error code.

exception kstlib.auth.errors.TokenExpiredError(message, *, details=None)[source]

Bases: TokenError

Raised when a token has expired and cannot be refreshed.

exception kstlib.auth.errors.TokenRefreshError(reason, *, retryable=False)[source]

Bases: TokenError

Raised when token refresh fails.

__init__(self, reason: 'str', *, retryable: 'bool' = False) 'None' -> None[source]

Initialize with the reason for the refresh failure and a retryable flag.

exception kstlib.auth.errors.TokenStorageError(message, *, details=None)[source]

Bases: TokenError

Raised when token persistence fails (save/load/delete).

exception kstlib.auth.errors.TokenValidationError(reason, *, claim=None)[source]

Bases: TokenError

Raised when JWT validation fails (signature, claims, expiry).

__init__(self, reason: 'str', *, claim: 'str | None' = None) 'None' -> None[source]

Initialize with the reason for the validation failure and the offending claim name.