Skip to main content

Authentication Client for Questra to use in all other Questra Client Libraries

Project description

Questra Authentication

A Python OAuth2 authentication client for the Questra API with support for both service account and interactive user authentication flows.

Features

  • Multiple Authentication Modes
    • Service Account authentication (username/password)
    • Interactive User authentication (OAuth2 Device Code Flow)
  • Automatic Token Management
    • Automatic token refresh when expired
    • Configurable minimum token lifetime
  • OIDC Discovery
    • Automatic discovery of OAuth2 endpoints
    • Support for multiple discovery paths
    • Fallback to .well-known/openid-configuration
  • Type-Safe Configuration
    • Dataclass-based configuration objects
    • Comprehensive exception hierarchy
  • Flexible Architecture
    • Strategy pattern for credential handling
    • Clean separation of concerns

Installation

Using pip

pip install seven2one-questra-authentication

Using poetry

poetry add seven2one-questra-authentication

Requirements

  • Python >= 3.10
  • requests-oauthlib >= 2.0.0

Quick Start

Service Account Authentication

from questra_authentication import QuestraAuthentication

# Initialize client with service account credentials
client = QuestraAuthentication(
    url="https://authentik.dev.example.com",
    username="ServiceUser",
    password="secret_password",
    oidc_discovery_paths=['/application/o/questra']
)

# Get access token (automatically refreshed when expired)
access_token = client.get_access_token()

# Use token for API requests
headers = {"Authorization": f"Bearer {access_token}"}

Interactive User Authentication

from questra_authentication import QuestraAuthentication

# Initialize client for interactive authentication
client = QuestraAuthentication(
    url="https://authentik.dev.example.com",
    interactive=True,
    oidc_discovery_paths=['/application/o/questra']
)

# User will be prompted to authorize via browser
# Token is automatically managed after authorization
access_token = client.get_access_token()

Advanced Usage

Multiple Discovery Paths

client = QuestraAuthentication(
    url="https://authentik.dev.example.com",
    username="ServiceUser",
    password="secret_password",
    oidc_discovery_paths=[
        '/application/o/techstack',
        '/application/o/questra'
    ]
)

Multiple Base URLs

client = QuestraAuthentication(
    url=[
        "https://auth1.example.com",
        "https://auth2.example.com"
    ],
    username="ServiceUser",
    password="secret_password",
    oidc_discovery_paths=['/application/o/questra']
)

Custom Scopes

client = QuestraAuthentication(
    url="https://authentik.dev.example.com",
    username="ServiceUser",
    password="secret_password",
    scope="openid profile email"
)

Using Lower-Level API

from questra_authentication import (
    OAuth2Authentication,
    OAuth2ServiceCredential,
    OidcDiscoveryClient
)

# Discover OIDC configuration
discovery_client = OidcDiscoveryClient(
    "https://authentik.dev.example.com/application/o/questra"
)
oidc_config = discovery_client.discover()

# Create credentials
credentials = OAuth2ServiceCredential(
    username="ServiceUser",
    password="secret_password"
)

# Initialize OAuth2 client
oauth_client = OAuth2Authentication(
    client_id="Questra",
    credentials=credentials,
    oidc_config=oidc_config
)

# Authenticate
oauth_client.authenticate()

# Get access token
token = oauth_client.get_access_token()

API Reference

QuestraAuthentication

Main client class for simplified Questra API access.

QuestraAuthentication Constructor Parameters

  • url (str | List[str]): Base URL(s) of the identity provider
  • client_id (str, optional): OAuth2 client ID (default: "Questra")
  • username (str, optional): Username for service account authentication
  • password (str, optional): Password for service account authentication
  • interactive (bool, optional): Enable interactive device code flow (default: False)
  • scope (str, optional): OAuth2 scopes
  • oidc_discovery_paths (List[str], optional): Discovery paths (default: ['/application/o/questra'])

QuestraAuthentication Methods

  • get_access_token() -> str: Returns a valid access token, automatically refreshing if needed
  • get_oidc_config() -> OidcConfig: Returns the OIDC configuration
  • is_authenticated() -> bool: Check if client is authenticated
  • reauthenticate() -> None: Force re-authentication

OAuth2Authentication

Low-level OAuth2 authentication client.

OAuth2Authentication Constructor Parameters

  • client_id (str): OAuth2 client ID
  • credentials (OAuth2Credential): Credential strategy
  • oidcConfig (OidcConfig): OIDC configuration
  • scope (str, optional): OAuth2 scopes
  • minimum_token_lifetime_seconds (int, optional): Minimum lifetime before refresh (default: 60)

OAuth2Authentication Methods

  • authenticate() -> None: Perform initial authentication
  • get_access_token() -> str: Get valid access token

Credential Types

OAuth2ServiceCredential

Service account credentials using username/password.

credentials = OAuth2ServiceCredential(
    username="ServiceUser",
    password="secret_password"
)

OAuth2InteractiveUserCredential

Interactive user credentials using device code flow.

credentials = OAuth2InteractiveUserCredential()

OidcConfig

Immutable dataclass containing OIDC endpoints.

OidcConfig Attributes

  • issuer: Issuer URL
  • authorization_endpoint: Authorization endpoint URL
  • token_endpoint: Token endpoint URL
  • userinfo_endpoint: User info endpoint URL
  • end_session_endpoint: End session endpoint URL
  • device_authorization_endpoint: Device authorization endpoint URL

OidcConfig Methods

  • to_dict() -> Dict[str, str]: Convert to dictionary
  • to_json() -> str: Convert to JSON string

Exception Hierarchy

QuestraAuthenticationError
\x00\x00 AuthenticationError
   \x00\x00 NotAuthenticatedError
   \x00\x00 SessionNotInitializedError
   \x00\x00 InvalidCredentialsError
   \x00\x00 TokenExpiredError
\x00\x00 OidcDiscoveryError

Exception Descriptions

  • QuestraAuthenticationError: Base exception for all client errors
  • AuthenticationError: General authentication failure
  • NotAuthenticatedError: Operation requires authentication but client is not authenticated
  • SessionNotInitializedError: OAuth2 session not initialized
  • InvalidCredentialsError: Invalid or wrong credential type provided
  • TokenExpiredError: Token expired and cannot be refreshed
  • OidcDiscoveryError: OIDC discovery failed (includes URLs tried and original error)

Error Handling

from questra_authentication import (
    QuestraAuthentication,
    AuthenticationError,
    OidcDiscoveryError
)

try:
    client = QuestraAuthentication(
        url="https://authentik.dev.example.com",
        username="ServiceUser",
        password="wrong_password"
    )
except OidcDiscoveryError as e:
    print(f"OIDC discovery failed: {e}")
    print(f"Tried URLs: {e.urls}")
except AuthenticationError as e:
    print(f"Authentication failed: {e}")

# Get token with error handling
try:
    token = client.get_access_token()
except NotAuthenticatedError:
    print("Not authenticated. Please authenticate first.")
    client.reauthenticate()

Development

Setup Development Environment

# Clone repository
git clone <repository-url>
cd S2O.Questra.Python.Authentication

# Install dependencies
poetry install

# Install development and test dependencies
poetry install --with dev,test

# Install pre-commit hooks
poetry run pre-commit install

Code Quality Tools

This project uses multiple tools to ensure code quality, consistency, and type safety:

Ruff - Fast Linter & Formatter

Ruff combines linting and formatting in a single, extremely fast tool.

# Run linter with auto-fixes
poetry run ruff check . --fix

# Check code formatting
poetry run ruff format --check .

# Format code
poetry run ruff format .

mypy - Static Type Checker

mypy validates type hints and catches type-related bugs before runtime.

# Run mypy on source code
poetry run mypy src/

# Run with verbose output
poetry run mypy src/ --verbose

Pyright - Advanced Type Checker

Pyright provides additional type checking with strict mode enabled.

# Run Pyright on source code
poetry run pyright src/

# Check specific file
poetry run pyright src/questra_authentication/authentication.py

Pre-commit Hooks

All quality checks run automatically on git commit:

# Run all pre-commit hooks manually
poetry run pre-commit run --all-files

# Update hook versions
poetry run pre-commit autoupdate

VS Code Integration

The project includes VS Code settings (.vscode/settings.json) that:

  • Automatically format on save with Ruff
  • Run linters on file changes
  • Display type hints inline
  • Organize imports automatically

Recommended extensions are listed in .vscode/extensions.json.

Running Tests

# Run all tests
poetry run pytest

# Run with coverage
poetry run pytest --cov=questra_authentication

# Run specific test file
poetry run pytest tests/test_authentication.py

CI/CD Pipeline

The project uses Azure DevOps for continuous integration. The pipeline (azure-pipelines.yml) automatically runs:

  1. Code Quality Checks

    • Ruff linting
    • Ruff formatting validation
  2. Type Checking

    • mypy validation
    • Pyright validation
  3. Tests

    • Pytest execution

All checks must pass before code can be merged.

Project Structure

S2O.Questra.Python.Authentication/
📁 src/
  📁 questra_authentication/
    - __init__.py
    - authentication.py       # OAuth2 authentication logic
    - device_client.py        # Device code flow implementation
    - exceptions.py           # Custom exception classes
    - questra_authentication.py  # Main client interface
📁 tests/
  - conftest.py
  - test_authentication.py
  - test_oidc_discovery.py
  - test_questra_authentication.py
- pyproject.toml
- README.md

License

[Add your license information here]

Contributing

[Add contributing guidelines here]

Authors

Changelog

Version 0.1.4

  • Current release
  • OAuth2 authentication with service account and interactive flows
  • Automatic token refresh
  • OIDC discovery with fallback support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

seven2one_questra_authentication-0.2.2.tar.gz (3.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file seven2one_questra_authentication-0.2.2.tar.gz.

File metadata

  • Download URL: seven2one_questra_authentication-0.2.2.tar.gz
  • Upload date:
  • Size: 3.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.11 {"installer":{"name":"uv","version":"0.9.11"},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for seven2one_questra_authentication-0.2.2.tar.gz
Algorithm Hash digest
SHA256 ead6575d4b9c8b5394e344902b5e3c6d5a0e7215735effa7a05bb00e48e4c43e
MD5 841bb82fdb267219f84eecbaddbcf88f
BLAKE2b-256 762a921328ac75604db7191c2a4a488a66bb08aac42e0d178b82ccff8a94055b

See more details on using hashes here.

File details

Details for the file seven2one_questra_authentication-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: seven2one_questra_authentication-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 10.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.11 {"installer":{"name":"uv","version":"0.9.11"},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for seven2one_questra_authentication-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 97282386994b327af68be55ee77ad024af2f1e52edcbf365073a76a0916c34d8
MD5 994451d9c3a101a5233163d847156a44
BLAKE2b-256 bf26911afe4b0370beee24a999593cee08ea0e28f3b931d624e70516969062e9

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page