Skip to content

Commit 033cfda

Browse files
authored
Add user (Basic auth) to Connect-RPC API reference endpoints (#193) (#194)
The `user` parameter is sent as an Authorization: Basic header but isn't part of the protobuf message, so it was missing from the generated API reference. Add a SandboxUserAuth security scheme and inject it into the 8 Connect-RPC endpoints that support it.
1 parent 41fbf82 commit 033cfda

2 files changed

Lines changed: 75 additions & 8 deletions

File tree

openapi-public.yml

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1586,7 +1586,8 @@ paths:
15861586
$ref: '#/components/schemas/filesystem.CreateWatcherResponse'
15871587
'502': *id003
15881588
security:
1589-
- *id005
1589+
- SandboxAccessTokenAuth: []
1590+
SandboxUserAuth: []
15901591
servers:
15911592
- *id004
15921593
/filesystem.Filesystem/GetWatcherEvents:
@@ -1654,7 +1655,8 @@ paths:
16541655
$ref: '#/components/schemas/filesystem.ListDirResponse'
16551656
'502': *id003
16561657
security:
1657-
- *id005
1658+
- SandboxAccessTokenAuth: []
1659+
SandboxUserAuth: []
16581660
servers:
16591661
- *id004
16601662
/filesystem.Filesystem/MakeDir:
@@ -1688,7 +1690,8 @@ paths:
16881690
$ref: '#/components/schemas/filesystem.MakeDirResponse'
16891691
'502': *id003
16901692
security:
1691-
- *id005
1693+
- SandboxAccessTokenAuth: []
1694+
SandboxUserAuth: []
16921695
servers:
16931696
- *id004
16941697
/filesystem.Filesystem/Move:
@@ -1722,7 +1725,8 @@ paths:
17221725
$ref: '#/components/schemas/filesystem.MoveResponse'
17231726
'502': *id003
17241727
security:
1725-
- *id005
1728+
- SandboxAccessTokenAuth: []
1729+
SandboxUserAuth: []
17261730
servers:
17271731
- *id004
17281732
/filesystem.Filesystem/Remove:
@@ -1756,7 +1760,8 @@ paths:
17561760
$ref: '#/components/schemas/filesystem.RemoveResponse'
17571761
'502': *id003
17581762
security:
1759-
- *id005
1763+
- SandboxAccessTokenAuth: []
1764+
SandboxUserAuth: []
17601765
servers:
17611766
- *id004
17621767
/filesystem.Filesystem/RemoveWatcher:
@@ -1824,7 +1829,8 @@ paths:
18241829
$ref: '#/components/schemas/filesystem.StatResponse'
18251830
'502': *id003
18261831
security:
1827-
- *id005
1832+
- SandboxAccessTokenAuth: []
1833+
SandboxUserAuth: []
18281834
servers:
18291835
- *id004
18301836
/filesystem.Filesystem/WatchDir:
@@ -1849,7 +1855,8 @@ paths:
18491855
$ref: '#/components/schemas/filesystem.WatchDirResponse'
18501856
'502': *id003
18511857
security:
1852-
- *id005
1858+
- SandboxAccessTokenAuth: []
1859+
SandboxUserAuth: []
18531860
parameters:
18541861
- &id006
18551862
name: Connect-Protocol-Version
@@ -2052,7 +2059,8 @@ paths:
20522059
$ref: '#/components/schemas/process.StartResponse'
20532060
'502': *id003
20542061
security:
2055-
- *id005
2062+
- SandboxAccessTokenAuth: []
2063+
SandboxUserAuth: []
20562064
parameters:
20572065
- *id006
20582066
- *id007
@@ -2264,6 +2272,12 @@ components:
22642272
(on connect), [POST /sandboxes/{sandboxID}/resume](/docs/api-reference/sandboxes/resume-a-sandbox)
22652273
(on resume), and [GET /sandboxes/{sandboxID}](/docs/api-reference/sandboxes/get-a-sandbox)
22662274
(for running or paused sandboxes).'
2275+
SandboxUserAuth:
2276+
type: http
2277+
scheme: basic
2278+
description: Optional system user for the operation. Sets file ownership and
2279+
resolves relative paths. Pass the desired username with no password. Defaults
2280+
to the sandbox's default user when omitted.
22672281
parameters:
22682282
FilePath:
22692283
name: path

scripts/generate_openapi_reference.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090

9191
# Security scheme name for envd endpoints (must not collide with platform's AccessTokenAuth)
9292
SANDBOX_AUTH_SCHEME = "SandboxAccessTokenAuth"
93+
SANDBOX_USER_SCHEME = "SandboxUserAuth"
9394

9495
# ---------------------------------------------------------------------------
9596
# Proto parsing — auto-detect streaming RPCs
@@ -575,6 +576,17 @@ def setup_sandbox_auth_scheme(spec: dict[str, Any]) -> None:
575576
"and [GET /sandboxes/{sandboxID}](/docs/api-reference/sandboxes/get-a-sandbox) (for running or paused sandboxes)."
576577
),
577578
}
579+
# Optional Basic auth for setting the system user on sandbox operations.
580+
# The SDK sends: Authorization: Basic <base64("username:")>
581+
schemes[SANDBOX_USER_SCHEME] = {
582+
"type": "http",
583+
"scheme": "basic",
584+
"description": (
585+
"Optional system user for the operation. Sets file ownership and resolves "
586+
"relative paths. Pass the desired username with no password. "
587+
"Defaults to the sandbox's default user when omitted."
588+
),
589+
}
578590

579591

580592
# Mapping of (path, method) to desired operationId for the public docs.
@@ -616,6 +628,20 @@ def add_operation_ids(spec: dict[str, Any]) -> None:
616628
"/process.Process/StreamInput",
617629
}
618630

631+
# Connect-RPC endpoints that accept an optional user via Authorization header.
632+
# The SDK sends: Authorization: Basic <base64("username:")>
633+
# This is not part of the protobuf message — it must be added as an OpenAPI parameter.
634+
USER_HEADER_ENDPOINTS = {
635+
"/process.Process/Start",
636+
"/filesystem.Filesystem/ListDir",
637+
"/filesystem.Filesystem/MakeDir",
638+
"/filesystem.Filesystem/Move",
639+
"/filesystem.Filesystem/Remove",
640+
"/filesystem.Filesystem/Stat",
641+
"/filesystem.Filesystem/WatchDir",
642+
"/filesystem.Filesystem/CreateWatcher",
643+
}
644+
619645

620646
def fix_spec_issues(spec: dict[str, Any]) -> None:
621647
"""Fix known discrepancies between the source spec and the live API.
@@ -1113,6 +1139,32 @@ def _singularize(word: str) -> str:
11131139
print(f" {f}")
11141140

11151141

1142+
def add_user_auth_security(spec: dict[str, Any]) -> None:
1143+
"""Add optional Basic auth (user) security to Connect-RPC endpoints that support it.
1144+
1145+
The sandbox resolves user from an Authorization: Basic header where the
1146+
username encodes the desired OS user. This is a transport-level concern
1147+
not captured in the proto definitions, so we inject it during post-processing.
1148+
1149+
Endpoints that support user get two security options (OR):
1150+
- SandboxAccessTokenAuth only (uses default user)
1151+
- SandboxAccessTokenAuth + SandboxUserAuth (custom user)
1152+
"""
1153+
paths = spec.get("paths", {})
1154+
count = 0
1155+
for ep_path in USER_HEADER_ENDPOINTS:
1156+
path_item = paths.get(ep_path, {})
1157+
op = path_item.get("post")
1158+
if not op:
1159+
continue
1160+
op["security"] = [
1161+
{SANDBOX_AUTH_SCHEME: [], SANDBOX_USER_SCHEME: []},
1162+
]
1163+
count += 1
1164+
if count:
1165+
print(f"==> Added optional user auth (Basic) to {count} Connect-RPC endpoints")
1166+
1167+
11161168
def _strip_supabase_security(path_item: dict[str, Any]) -> None:
11171169
"""Remove Supabase security entries from all operations in a path item.
11181170
@@ -1455,6 +1507,7 @@ def main() -> None:
14551507
setup_sandbox_auth_scheme(merged)
14561508
add_operation_ids(merged)
14571509
fix_spec_issues(merged)
1510+
add_user_auth_security(merged)
14581511

14591512
# Remove internal/unwanted paths
14601513
filter_paths(merged)

0 commit comments

Comments
 (0)