Authentication#
Authentication is handled by LabArchives. labapi works with two input patterns:
callback-based authentication, where LabArchives redirects back with
emailandauth_codemanually copied External App credentials from the LabArchives UI
Use login() for either pattern. Prefer
callback-based authentication for user sign-ins, especially when SSO is
required.
Choosing an Auth Pattern#
Use the flow that matches where your code runs:
Scenario |
Recommended approach |
Why |
|---|---|---|
Local scripts, notebooks, ad-hoc analysis on a workstation |
No callback wiring or manual credential copying; handles the browser round-trip automatically. |
|
Headless hosts (containers, CI workers, cron jobs, orchestrators) |
|
Avoids dependence on a local browser session. |
One-off/manual testing without callback wiring |
|
Credentials are copied from the LabArchives UI; no browser or callback listener needed. |
Interactive Authentication#
For local interactive use, call
default_authenticate(). It opens or prints a
LabArchives login URL, starts a temporary local callback server, and logs in
after LabArchives redirects back.
Note
When the Optional Extras are installed, this method can open a
compatible local browser automatically. Without builtin-auth, it still
works in terminal/manual mode by printing the authentication URL.
from labapi import Client
with Client() as client:
user = client.default_authenticate()
Server-Based Authentication#
For web apps and service integrations, call
generate_auth_url() with your callback URL.
LabArchives redirects to that callback with email and auth_code, and
your server passes those values to login().
The flow:
Your app exposes a callback URL (for example
https://my-service.example.org/labarchives/callback).Your app sends users to
client.generate_auth_url(callback_url).LabArchives redirects back to your callback URL with
emailandauth_codequery parameters.Your callback handler exchanges those values via
login().Your service stores only what it needs for subsequent calls, following your organization’s secret-management policy.
Note
labapi currently does not provide a separate client-credentials style service principal flow.
Service integrations should use callback-based login for user context. Use External App authentication only when the job can manage a short-lived External App auth code.
Example Flask App#
import flask
from labapi import Client
app = flask.Flask(__name__)
@app.route("/login")
def login():
with Client() as client:
callback_url = flask.url_for("callback", _external=True)
auth_url = client.generate_auth_url(callback_url)
return flask.redirect(auth_url)
@app.route("/callback")
def callback():
email = flask.request.args.get("email")
auth_code = flask.request.args.get("auth_code")
if not email or not auth_code:
return "Authentication failed.", 400
with Client() as client:
user = client.login(email, auth_code)
notebook_names = list(user.notebooks)
return f"Logged in as {user.id}. Notebooks: {notebook_names}"
if __name__ == "__main__":
app.run(port=8080)
Advanced Local Callback Control#
If you want to keep browser handling separate from callback capture, use
generate_auth_url() and
collect_auth_response() directly:
from labapi import Client
with Client() as client:
callback_path = "/auth/local-demo/"
auth_url = client.generate_auth_url(
f"http://127.0.0.1:8089{callback_path}"
)
with client.collect_auth_response(
port=8089,
callback_path=callback_path,
) as auth_response_collector:
print("Open authentication URL in your browser:")
print(auth_url)
user = auth_response_collector.wait()
Headless and CI Workflows#
In non-interactive environments (CI, scheduled jobs, or batch workers), avoid default_authenticate() because it expects a browser and a local callback listener.
For scheduled jobs, pass a short-lived auth_code to login() directly:
export API_URL="https://api.labarchives.com"
export ACCESS_KEYID="your_access_key"
export ACCESS_PWD="your_access_password"
export AUTH_EMAIL="service.user@example.org"
export AUTH_KEY="short_lived_auth_code"
import os
from labapi import Client
client = Client()
user = client.login(
os.environ["AUTH_EMAIL"],
os.environ["AUTH_KEY"],
)
# continue your automated task...
# notebook = user.notebooks["Automation Notebook"]
Note
AUTH_EMAIL and AUTH_KEY here are application-level environment
variable names chosen by this example. Unlike API_URL,
ACCESS_KEYID, and ACCESS_PWD, they are not auto-loaded by
Client.
Operational guidance for automation:
Treat
auth_codevalues as secrets; keep them in your CI secret store rather than source control.Prefer short-lived credentials, and define how the job obtains a new External App auth code before the current one expires.
Use least-privilege LabArchives users for automated jobs.