Authentication#
At a high level, 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
Both can be completed through login(). The callback
flow is generally the better default because it also works with SSO.
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 |
Fastest “happy path” and easiest onboarding. |
|
Headless hosts (containers, CI workers, cron jobs, orchestrators) |
|
Avoids dependence on a local browser session. |
One-off/manual testing without callback wiring |
|
Useful for quick experiments when interactive callback flow is not available. |
Interactive Authentication#
The most direct local workflow is
default_authenticate(). It launches or prints a
LabArchives login URL, listens on a temporary local callback server, and then
signs the user in immediately after the redirect completes.
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 deeper integrations with other systems, or for use in servers, use
generate_auth_url(). It generates a LabArchives
authentication URL that eventually redirects to the callback URL you pass in,
letting your application handle credential capture on its own server.
For service environments, this is the recommended 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 capture + login() for user context, or External App authentication where operationally appropriate.
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 + local callback listener.
Instead, use one-hour codes for job execution and use 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 regular rotation.
Build your own refresh/re-auth step in orchestration when codes expire.
Use least-privilege LabArchives users for automated jobs.