Server and Webapp Usage#
This guide covers how to connect a Bfabric client in server-side applications and webapps that integrate with
B-Fabric, where there is no config file to log in from interactively.
Overview#
Three situations, three methods:
Situation |
Method |
|---|---|
A background job or server acting as itself, with no user |
|
A webapp launched from B-Fabric, acting for the current user |
|
A webapp receiving a B-Fabric webapp token |
Service Accounts#
connect_oauth() authenticates as a registered OAuth client rather than as a person, using the client credentials
grant. This is what background jobs, feeders and servers should use:
from bfabric import Bfabric
client = Bfabric.connect_oauth(
client_id="my-service",
client_secret="...",
base_url="https://fgcz-bfabric.uzh.ch/bfabric",
scope="api:read api:write",
)
Tokens are fetched and refreshed automatically. Pass token_cache_path to keep them across restarts.
Important
A client credentials token carries no user identity. Anything authorized by the caller’s container membership — file downloads, for instance — needs a token from a user flow instead. See OAuth Usage & Troubleshooting.
Apps Launched From B-Fabric#
An app that B-Fabric opens with a jwt URL parameter can exchange that short-lived launch token for a client that holds
both identities at once:
from bfabric.oauth import WebappClient
webapp = WebappClient.create(
"https://fgcz-bfabric.uzh.ch/bfabric",
launch_token,
client_id="my-webapp",
client_secret="...",
scope="api:read api:write",
)
webapp.user.read(endpoint="sample", obj={}) # as the logged-in user
webapp.service.save(endpoint="workunit", obj={}) # as the service account
print(webapp.context.entity_class_name, webapp.context.entity_id) # what was clicked on
webapp.context also carries application_id, job_id, the user’s email, name and groups, and the token’s
expiry.
Token-Based Authentication Methods#
connect_token() / connect_token_async() - Token Authentication#
These methods create a client from a B-Fabric webapp token. They are functionally equivalent:
connect_token()- Synchronous wrapper that runs an event loop internally. Use in synchronous code or outside async contexts.connect_token_async()- Native async version. Use in async functions or with async web frameworks.
from bfabric import Bfabric
from bfabric.experimental.webapp_integration_settings import TokenValidationSettings
# Configure which B-Fabric instances are allowed
settings = TokenValidationSettings(
validation_bfabric_instance="https://fgcz-bfabric.uzh.ch/bfabric",
supported_bfabric_instances=[
"https://fgcz-bfabric.uzh.ch/bfabric",
"https://fgcz-bfabric-test.uzh.ch/bfabric",
],
)
# Synchronous usage
token = "your_token_here"
client, token_data = Bfabric.connect_token(token=token, settings=settings)
# Asynchronous usage
# client, token_data = await Bfabric.connect_token_async(token=token, settings=settings)
print(f"Authenticated as: {token_data.user}")
print(f"Token expires: {token_data.token_expires}")
print(f"Token caller: {token_data.caller}")
from_token_data() - Create Client from Validated Token#
Warning
This is a niche use case. Use connect_token() or connect_token_async() unless you have specific validation requirements.
If you need custom token validation logic, you can validate a token yourself and create a client from the TokenData:
from bfabric import Bfabric
from bfabric.rest.token_data import get_token_data
# Validate token first
base_url = "https://fgcz-bfabric.uzh.ch/bfabric"
token = "your_token_here"
token_data = get_token_data(base_url=base_url, token=token)
# Check if the token is from an allowed instance
allowed_instances = ["https://fgcz-bfabric.uzh.ch/bfabric"]
if token_data.caller not in allowed_instances:
raise ValueError(f"Token from {token_data.caller} is not allowed")
# Create client
client = Bfabric.from_token_data(token_data)
Configuration#
TokenValidationSettings#
The TokenValidationSettings class configures which B-Fabric instances are allowed to issue tokens for your application.
from bfabric.experimental.webapp_integration_settings import TokenValidationSettings
settings = TokenValidationSettings(
validation_bfabric_instance="https://fgcz-bfabric.uzh.ch/bfabric",
supported_bfabric_instances=[
"https://fgcz-bfabric.uzh.ch/bfabric",
"https://fgcz-bfabric-test.uzh.ch/bfabric",
],
)
Parameters:
validation_bfabric_instance(str): The B-Fabric instance to use for token validation. Must be one of the supported instances.supported_bfabric_instances(list[str]): List of B-Fabric instance URLs that are allowed to issue tokens.
Important
The validation_bfabric_instance must be included in supported_bfabric_instances. This is enforced at initialization.
WebappIntegrationSettings#
The WebappIntegrationSettings class extends TokenValidationSettings with feeder_user_credentials. Use this when your webapp needs to create or update entities on behalf of users using a privileged service account.
from bfabric.experimental.webapp_integration_settings import WebappIntegrationSettings
from bfabric.config import BfabricAuth
settings = WebappIntegrationSettings(
validation_bfabric_instance="https://fgcz-bfabric.uzh.ch/bfabric",
supported_bfabric_instances=["https://fgcz-bfabric.uzh.ch/bfabric"],
feeder_user_credentials={
"https://fgcz-bfabric.uzh.ch/bfabric": BfabricAuth(
login="feeder_user", password="feeder_user_password"
),
},
)
Parameters:
Inherits all parameters from
TokenValidationSettingsfeeder_user_credentials(dict[str, BfabricAuth]): Mapping of B-Fabric instance URLs to credentials with permission to create/update entities. All instance keys must be insupported_bfabric_instances.
Note
Feeder user credentials are typically used when the authenticated user lacks permission to perform certain operations, but a privileged service account does.
Security Best Practices#
Restrict Token Instances#
Always restrict supported_bfabric_instances to only the instances you trust:
settings = TokenValidationSettings(
validation_bfabric_instance="https://fgcz-bfabric.uzh.ch/bfabric",
supported_bfabric_instances=[
"https://fgcz-bfabric.uzh.ch/bfabric", # Only allow production
# "https://fgcz-bfabric-test.uzh.ch/bfabric", # Commented out to prevent test tokens
],
)
Check Token Expiration#
Verify tokens haven’t expired in your application logic:
from datetime import datetime, timezone
client, token_data = Bfabric.connect_token(token=token, settings=settings)
if token_data.token_expires < datetime.now(timezone.utc):
raise ValueError("Token has expired")
Verify Web Service Permissions#
Check if the authenticated user has web service permissions:
if not token_data.web_service_user:
raise PermissionError("User does not have web service permissions")
Use Secrets for Tokens#
For additional security, use pydantic.SecretStr when handling tokens:
from pydantic import SecretStr
secret_token = SecretStr(request_token)
client, token_data = Bfabric.connect_token(token=secret_token, settings=settings)
Working with Token Data#
When you create a client using token-based methods, you receive both a Bfabric client and TokenData object:
client, token_data = Bfabric.connect_token(token=token, settings=settings)
# Token data contains useful information
print(f"User: {token_data.user}")
print(f"Application ID: {token_data.application_id}")
print(f"Entity: {token_data.entity_class}#{token_data.entity_id}")
print(f"Job ID: {token_data.job_id}")
print(f"Expires: {token_data.token_expires}")
print(f"Web service user: {token_data.web_service_user}")
print(f"Caller instance: {token_data.caller}")
# Load the entity associated with the token
entity = token_data.load_entity(client=client)