Skip to content

feat(cli): create organization access tokens through the dashboard - #15409

Merged
pieterbeulque merged 8 commits into
mainfrom
pieter/cli-oat
Oct 14, 2026
Merged

pieterbeulque merged 8 commits into
mainfrom
pieter/cli-oat

Conversation

@pieterbeulque

@pieterbeulque pieterbeulque commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Summary

polar auth token creates an organization access token (OAT) for an app and prints it on stdout, for the caller to store wherever it belongs.

What

  • CLI — polar auth token [--scope …]
    • Takes the organization and environment from the current session (auth whoami), including --local.
    • Opens /dashboard/<slug>/cli/token, waits on the existing loopback listener (127.0.0.1:3333), and prints the token, nothing else, on stdout.
    • The browser prompt goes to stderr, as does a hint for sandbox and local tokens on how to point the SDK at that environment. So echo "POLAR_ACCESS_TOKEN=$(polar auth token)" >> .env.local and polar auth token | gh secret set POLAR_ACCESS_TOKEN capture only the token. --json is refused.
  • Web — /dashboard/[organization]/cli/token: full-screen page with the existing token form, prefilled with the CLI's name and scopes. Cancel notifies the CLI. Opened without a CLI key, it shows the token once to copy.
  • Refactors
    • utils/loopback.ts: the callback server is shared by auth login and auth token.
    • dashboardOrigin in services/api.ts: OAuth authorizationUrl and the token page resolve the dashboard the same way, including POLAR_DASHBOARD_URL and the guard against the hosted dashboard when POLAR_API_URL is set.
    • useSubmitOrganizationAccessToken: create-token logic, including the fresh-session retry, is shared by the settings modal and the new page.

Why

Creating an OAT requires a fresh web session. That stays: an OAuth session must not be able to mint an OAT, because the OAT can outlive it. But developers and agents setting up Polar in an app need a token from the CLI. The token prints on stdout, so an agent should redirect or pipe it rather than read it, to keep it out of its history.

How

The CLI generates a one-time RSA-OAEP key pair and a state, and passes the public key to the page. After the user creates the token in their web session, the page encrypts it with WebCrypto and redirects to the fixed loopback URL. The CLI checks state and decrypts.

  • The page only sends to 127.0.0.1:3333, so a crafted link cannot redirect the token.
  • The token travels encrypted, so browser history only holds ciphertext.
  • Delivery is a top-level navigation, like the OAuth login, so no CORS or CSP changes are needed.

Checklist

  • This PR addresses a single concern (one bug fix, one feature, one refactor)
  • The diff is reasonably sized and easy to review
  • New functionality is covered by tests
  • Linting and type checking pass
  • No unrelated changes or drive-by fixes are included

Verification

  • Run end to end in Chrome against a local stack: dev seed, bun src/cli.ts auth login --local, then bun src/cli.ts auth token.
  • The local stack is HTTP throughout, so the redirect from an HTTPS dashboard to http://127.0.0.1:3333 can only be checked on sandbox after deploy. auth login already makes the same jump in production (polar.sh → http://127.0.0.1:3333/oauth/callback); this page navigates with window.location rather than a server redirect, which browsers also treat as a top-level navigation.
  • If the CLI has stopped listening when the page redirects, the token is created but never delivered and has to be revoked in settings.

🤖 Generated with Claude Code

View guided diff Turn on auto-fix

@mintlify

mintlify Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated
Polar 🟢 Ready View Preview Oct 14, 2026, 8:11 AM

💡 Tip: Enable Automations to automatically generate PRs for you.

@vercel

vercel Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
orbit Ready Ready Preview Oct 14, 2026 9:24am UTC
polar-test Ready Ready Preview Oct 14, 2026 9:24am UTC

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 23 files

Confidence score: 3/5

  • In clients/packages/cli/README.md, the login and dashboard commands fail if run in the same shell as the preceding local-API snippet because the required environment variables conflict or are missing. Update the example to set the required variables or show the commands in a separate shell.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="clients/packages/cli/README.md">

<violation number="1" location="clients/packages/cli/README.md:65">
P2: These commands fail after following the preceding local-API snippet in the same shell: `auth login` rejects `POLAR_ACCESS_TOKEN`, and the dashboard resolver requires `POLAR_DASHBOARD_URL` when `POLAR_API_URL` is set. Unset the token override and set the local dashboard URL before either command.</violation>
</file>

This PR changes authentication, authorization, or input validation. Ultrareviews find 2.4x more serious bugs than standard reviews. Comment @cubic-dev-ai ultrareview to run one.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread clients/packages/cli/README.md Resolved

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 9 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.

View guided diff | Turn on auto-fix | Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

View guided diff | Turn on auto-fix | Re-trigger cubic

@pieterbeulque
pieterbeulque marked this pull request as ready for review October 13, 2026 14:58
pieterbeulque and others added 5 commits October 14, 2026 09:11
`polar auth token` opens /dashboard/:slug/cli/token for the active
organization. The page creates the token with the web session, so the
fresh-session requirement on token creation stays and an OAuth session
still can't mint a token that outlives it.

The CLI sends a one-time RSA public key and a state with the page link.
The page encrypts the token in the browser and redirects to the CLI's
loopback listener on 127.0.0.1:3333, which only ever receives
ciphertext. The CLI decrypts it and writes it to .env (or --env-file)
without printing it, so the token stays out of terminal and agent
history. Opened without a key, the page shows the token once to copy.

Along the way:
- OAuth login and the token handoff share the loopback callback server
  (utils/loopback.ts) and the dashboard origin (dashboardOrigin in
  services/api.ts), so --local and POLAR_DASHBOARD_URL apply to both.
- The create-token logic, including the fresh-session retry, moves out
  of CreateAccessTokenModal into useSubmitOrganizationAccessToken.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sandbox tokens also write POLAR_ENVIRONMENT=sandbox and local tokens
POLAR_API_URL, and a variable left from another environment is flagged
rather than removed. --stdout prints KEY=value lines to pipe into a
hosting provider, with status messages on stderr. Drop --env-var and
--name: the token is always POLAR_ACCESS_TOKEN and is named after the
current directory, editable on the dashboard page.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Ignore loopback callbacks whose state doesn't match instead of ending
  the wait, so another page can't abort a login or token handoff.
- Create new env files with mode 600 and remove later duplicate
  assignments, so the new token is the one that takes effect.
- Unset the token-override variables before the local login steps in
  the README.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Without --env-file, write to .env.local when it exists and .env
otherwise, so projects that commit .env keep secrets in .env.local.
After writing, warn when git doesn't ignore the file. Writing env files
moves into services/env-file.ts, which runs git check-ignore.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 3 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.

View guided diff | Turn on auto-fix | Re-trigger cubic

…nv file

Drop --stdout and --env-file. auth token now prints KEY=value lines on
stdout and leaves storing them to the caller; prompts and hints go to
stderr so the output can be piped or redirected.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 8 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

Comment thread docs/integrate/cli/reference/auth.mdx Resolved
Drop the KEY=value lines so the output can go straight into a secret
store or a variable. The environment becomes an SDK hint on stderr.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 4 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

View guided diff | Turn on auto-fix | Re-trigger cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 23 files

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Adds polar auth token CLI command and a dashboard page that mints long-lived org access tokens via an encrypted loopback handoff. This is a new authentication/token-issuance path with a novel security design, so it needs human security review.

View guided diff | Turn on auto-fix | Re-trigger cubic

awaitCallback already rejects callbacks with the wrong state before
parsing them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 issues found across 2 files (changes from recent commits).

Confidence score: 5/5

  • Automated review surfaced no issues in the provided summaries.
  • No files require special attention.

Requires human review: Adds a CLI command and dashboard page that mint long-lived org access tokens via an encrypted loopback handoff, and refactors auth login into shared loopback code. New token-issuance/auth surface and accepted docs risks require human sign-off.

View guided diff | Turn on auto-fix | Re-trigger cubic

@pieterbeulque
pieterbeulque added this pull request to the merge queue Oct 14, 2026
Merged via the queue into main with commit 3dc214f Oct 14, 2026
29 checks passed
@pieterbeulque
pieterbeulque deleted the pieter/cli-oat branch October 14, 2026 09:35

This branch was successfully deployed

3 active (1 outdated) deployments
Preview – polar-test — a039fdc1 Deployed Oct 14, 2026 by vercel[bot]
Preview – orbit — a039fdc1 Deployed Oct 14, 2026 by vercel[bot]
staging - docs — bc6deba4 Deployed Oct 14, 2026 by mintlify[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants