feat: zero-config auth β interactive PAT setup, auto-rotation, all CLIs (#81, #83)#82
Merged
Merged
Conversation
Mints a new 2-hour PAT every 90 minutes, persists to env var, ~/.databrickscfg, and optionally to a Databricks app secret, then revokes the old PAT. Includes 19 tests covering rotation logic, persistence, lifecycle, and logging.
β¦entials (#81) Owner resolution no longer depends on PAT. Uses the auto-provisioned SP to call w.apps.get().creator and matches against X-Forwarded-Email. Falls back to PAT-based resolution for backward compat.
β¦ create (#81) - Rotation interval: 10 min (less API churn overnight) - Token lifetime: 15 min (5-min overlap buffer) - ensure_fresh() called on session creation β if token age > 8 min, rotate immediately so user never starts with a stale token
- Remove ensure_fresh() (unnecessary with overlap buffer) - Rotation only fires when active sessions exist - No sessions = no API churn (no pointless token minting overnight) - 10-min rotation interval, 15-min token lifetime (5-min overlap) - Pass session_count_fn to PATRotator for decoupled session awareness
β¦KEN from app.yaml (#83)
β¦) after PAT setup (#83)
The index route was treating "pending" (setup not started yet, waiting for PAT) the same as "running" (setup actively in progress), causing the loading/snake page to appear immediately instead of index.html with the PAT prompt. Now only shows loading.html when setup is actively running.
With deferred setup (runs after PAT, not at boot), the wait happens inside the terminal via polling. The loading.html snake game page was unreachable dead code.
When the user pastes a PAT, immediately rotate it into a short-lived token we own (with a known token ID). This ensures the first background rotation can revoke the old token instead of logging "no old token to revoke." The user-pasted PAT becomes unused after the initial mint.
Add _last_rotation_time to PATRotator, set on every successful mint. pat-status now checks is_token_expired first β if the token lifetime has elapsed (no rotation while sessions were idle), immediately returns valid: false to show the PAT prompt. Avoids a wasted API call to validate a known-dead token.
Adds app_state.py β a shared JSON file at ~/.coda/app_state.json that holds app_owner (set at boot) and last_rotation_time/last_token_id (set on every rotation). Admins can inspect this file for diagnostics. The rotator loads last_rotation_time on init so is_token_expired works across app restarts.
Clean up app_state wiring: - Move import app_state to top-level in app.py - pat_rotator writes to app_state.json on every rotation (not just initial mint), keeping on-disk state current for admin inspection - Add /api/app-state endpoint for admin diagnostics - Remove duplicate write from configure_pat (rotator handles it) - Add 8 tests for app_state round-trip, merge, permissions, corruption
Pin mlflow to 3.10.1 and all other packages to their current resolved versions for reproducible deploys.
- pyasn1 0.6.3 fixes GHSA-jr27-m4p2-rc6r (DoS via recursive decoding) - pyjwt 2.12.1 fixes GHSA-752w-5fwx-jx9f (crit header bypass) - cryptography 46.0.6, requests 2.33.0, pygments fix β not released yet, ignoring in audit until available
requests 2.33.0 fixes the predictable temp file extraction vuln but hasn't landed on PyPI yet. Pin to the v2.33.0 tag commit SHA from GitHub. Remove the audit ignore for this CVE.
We only use mlflow.claude_code.hooks (in mlflow-tracing). The [genai] extra pulled in litellm β tokenizers β huggingface-hub β typer β rich β pygments (GHSA-5239-wwwm-4pmq, no fix). Switching to mlflow-tracing drops ~40 transitive deps including pygments, scikit-learn, scipy.
Constrained cryptography>=46.0.6 which resolved by downgrading google-auth to 2.47.0 (no cryptography dependency). Removes the last --ignore-vuln from the audit workflow. All 5 CVEs now resolved.
mcp and cryptography conflict: mcp>=1.23 needs google-auth which needs cryptography, but 46.0.6 isn't on PyPI yet. Prioritize mcp fix (DNS rebinding) over cryptography (low-impact X.509 name constraints). Weekly audit will catch when 46.0.6 lands.
Install cryptography 46.0.6 from GitHub tag (not on PyPI proxy yet). Zero --ignore-vuln flags remaining β all 6 CVEs resolved.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The app deploys with zero secrets and zero pre-configuration. On first terminal session, the user pastes a short-lived token. The app validates it, configures all CLIs (Claude, Codex, OpenCode, Gemini, Databricks), runs setup, and starts auto-rotation. Old tokens are revoked every 10 minutes. No secret scopes, no SP credential leakage, no pre-deployment setup.
Fixes #81, fixes #83
What Changed
current_user.me()app.creator(stripped after)X-Forwarded-Emailvsapp.creatorWorkspaceClient(SP fallback bug)User Flow
Key Fixes in This PR
Fix: SP credential fallback bug
The Databricks SDK silently falls back to SP credentials (
DATABRICKS_CLIENT_ID/SECRET) when PAT validation fails. This caused/api/pat-statusto returnvalid: truefor dead PATs. Fixed by:Fix: Setup crashes without token
setup_claude.pycrashed withKeyErroronos.environ["DATABRICKS_TOKEN"]when no token was set. Fixed by: splitting CLI install (no token needed) from auth config (needs token), and deferring setup to after PAT is provided.Fix: Session-aware rotation
Rotation only runs while active sessions exist. No wasted API calls minting tokens overnight when nobody's using the app.
Fix: Snake game loading page removed
The loading page with snake game was dead code after deferring setup to after PAT. Setup progress now displays inline in the terminal.
Files Changed
pat_rotator.pyapp.pyapp.yamlDATABRICKS_TOKEN, removed secret scope env vars, removedresourcessectionsetup_claude.pysetup_codex.pysetup_opencode.pysetup_gemini.pysetup_databricks.pystatic/index.htmlstatic/loading.htmltests/test_pat_rotator.pytests/test_pat_rotation_integration.pytests/test_heartbeat.pyREADME.mddocs/deployment.mdTest Plan
Automated (102 tests, all passing)
Manual Testing on Databricks Apps (test-coda on 9cefok)
1. Verify zero-config deploy
DATABRICKS_TOKENsecretOwner resolved from app.creator: sathish.gangichetty@databricks.comPAT rotation: no token configured β rotation disabledSP credentials stripped β PAT-only auth from this point(confirmed 2026-03-27T23:59:25Z and 2026-03-28T00:05:19Z)2. Verify interactive PAT setup
Token:prompt with cursor*), press EnterValidating token...Token configured for sathish.gangichetty@databricks.comAuto-rotation started. This token will be rotated out in 10 minutes.Setup triggered after PAT configurationat 00:07:00Z)/api/setup-status200 from 00:07:00-00:07:15)POST /api/sessionat 00:07:15)3. Verify all CLIs configured
Claude CLI auth configured(00:06:22)Databricks CLI auth configured(00:06:22)CLI config updated: setup_codex.py(00:06:26)CLI config updated: setup_opencode.py(00:06:35)CLI config updated: setup_gemini.py(00:07:00)claude --versionβ prints versionclaudeβ starts Claude Codedatabricks current-user meβ returns your identitydatabricks clusters listβ works4. Verify PAT rotation in logs
PAT rotation starting β minting new short-lived token...(confirmed multiple cycles)PAT persisted: env var + ~/.databrickscfg updatedPAT rotation complete β new token... Old token ELIMINATED5. Verify session awareness
PAT rotation: no active sessions β skipping rotation(23:21:45Z)6. Verify restart recovery
PAT rotation: no token configured β rotation disabled7. Verify stale token handling (SP fallback fix)
create failed (403): Invalid access token(23:31-23:51)8. Second tab (setup already done)
This pull request was AI-assisted by Claude Code.