Skip to content

Commit 9126b1f

Browse files
feat(auth): support workspace session switching (#93)
## Summary - add local multi-workspace OAuth session support with active workspace selection - add auth workspace list/use/logout commands plus auth logout --workspace shortcut - keep service-token precedence explicit while showing cached OAuth workspaces - improve project mismatch guidance to switch workspaces instead of re-login ## Verification - pnpm --filter @prisma/cli test -- auth.test.ts auth-real-mode.test.ts token-storage.test.ts - pnpm --filter @prisma/cli exec tsc --noEmit - pnpm --recursive exec tsc --noEmit - pnpm lint - git diff --check
1 parent 577152d commit 9126b1f

25 files changed

Lines changed: 3221 additions & 58 deletions

docs/product/command-spec.md

Lines changed: 149 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,11 @@ The CLI accepts two authentication sources, in this fixed precedence:
6868
1. `PRISMA_SERVICE_TOKEN` environment variable — long-lived service token, intended for CI and other headless contexts.
6969
2. Stored OAuth session — created by `prisma-cli auth login`, kept in the OS-appropriate credentials store, refreshed automatically.
7070

71-
Stored OAuth sessions include a short-lived access token and a refresh token. Commands refresh the access token automatically when the API rejects it, coordinate refreshes across concurrent CLI processes, and tolerate short refresh-token rotation races. If the stored session cannot be refreshed, commands fail with a structured `AUTH_REQUIRED` error instead of surfacing SDK stack traces.
71+
Stored OAuth sessions include a short-lived access token and a refresh token. The local credentials store may contain OAuth grants for multiple workspaces. One local active workspace pointer selects which grant authenticated commands use. Commands refresh the selected access token automatically when the API rejects it, coordinate refreshes across concurrent CLI processes, and tolerate short refresh-token rotation races. If the selected session cannot be refreshed, commands fail with a structured `AUTH_REQUIRED` error instead of surfacing SDK stack traces or silently falling through to another workspace.
7272

7373
When `PRISMA_SERVICE_TOKEN` is set and non-empty, the token is fully sufficient for authenticated commands. If `PRISMA_SERVICE_TOKEN` is set but empty or only whitespace, commands fail with an auth configuration error instead of falling back to stored OAuth. The CLI does not read any locally stored OAuth session when a non-empty service token is present, so behavior is identical on a fresh runner and a developer machine that happens to be signed in. The active workspace is derived from the token's `sub` claim; no additional flag or environment variable is required for the common case where the token is scoped to a single workspace.
7474

75-
`auth login` and `auth logout` operate on the stored OAuth session. They do not affect the `PRISMA_SERVICE_TOKEN` environment variable.
75+
`auth login`, `auth logout`, and `auth workspace` operate on stored OAuth sessions. They do not affect the `PRISMA_SERVICE_TOKEN` environment variable. `auth login` stores the authorized workspace and makes it active. `auth logout` clears all local OAuth workspace sessions. `auth workspace logout` and `auth logout --workspace` clear one local OAuth workspace session, including while `PRISMA_SERVICE_TOKEN` is set, because they only clean local OAuth state. If that workspace was active, the CLI does not silently fall through to another cached workspace; the user must explicitly choose the next workspace with `auth workspace use`. `auth workspace use` changes only local CLI context and never mutates a remote resource. When `PRISMA_SERVICE_TOKEN` is set, workspace switching is unavailable because the token is the active auth source.
7676

7777
## Context Resolution
7878

@@ -249,6 +249,58 @@ Rules:
249249
- `credential` identifies the active credential when known, or is `null`
250250
- signed-out state is an empty auth state, not an error
251251

252+
`auth workspace list --json` returns local workspace sessions:
253+
254+
```json
255+
{
256+
"context": {
257+
"authSource": "oauth",
258+
"activeWorkspaceId": "wksp_123",
259+
"activeWorkspaceName": "Acme Inc"
260+
},
261+
"items": [
262+
{
263+
"id": "wksp_123",
264+
"name": "Acme Inc",
265+
"status": "active",
266+
"source": "oauth",
267+
"switchable": true,
268+
"credentialWorkspaceId": "cmmx...",
269+
"lastSeenAt": "2026-06-19T00:00:00.000Z"
270+
}
271+
],
272+
"count": 1
273+
}
274+
```
275+
276+
`auth workspace use --json` returns:
277+
278+
```json
279+
{
280+
"previousWorkspace": {
281+
"id": "wksp_123",
282+
"name": "Acme Inc"
283+
},
284+
"workspace": {
285+
"id": "wksp_456",
286+
"name": "Prisma Labs"
287+
}
288+
}
289+
```
290+
291+
`auth workspace logout --json` returns:
292+
293+
```json
294+
{
295+
"workspace": {
296+
"id": "wksp_123",
297+
"name": "Acme Inc"
298+
},
299+
"wasActive": true,
300+
"activeWorkspace": null
301+
}
302+
```
303+
252304
## `prisma-cli version`
253305

254306
Purpose:
@@ -346,18 +398,20 @@ prisma-cli auth login --json
346398

347399
Purpose:
348400

349-
- clear stored authentication credentials
401+
- clear all stored OAuth workspace credentials
350402

351403
Behavior:
352404

353-
- removes local session state
405+
- removes all local OAuth workspace sessions
406+
- with `--workspace <id-or-name>`, removes only the target local OAuth workspace session
354407
- succeeds even if no session exists
355-
- returns the signed-out auth state
408+
- returns the signed-out auth state unless `PRISMA_SERVICE_TOKEN` is still set
356409

357410
Examples:
358411

359412
```bash
360413
prisma-cli auth logout
414+
prisma-cli auth logout --workspace wksp_123
361415
prisma-cli auth logout --json
362416
```
363417

@@ -379,6 +433,96 @@ prisma-cli auth whoami
379433
prisma-cli auth whoami --json
380434
```
381435

436+
## `prisma-cli auth workspace list`
437+
438+
Purpose:
439+
440+
- list locally authenticated workspaces
441+
442+
Behavior:
443+
444+
- succeeds when signed out
445+
- lists local OAuth workspaces stored on this machine
446+
- human output includes a table with workspace name and workspace id
447+
- marks the active local workspace when one is selected
448+
- when `PRISMA_SERVICE_TOKEN` is set, shows the token workspace when resolvable and also shows stored local OAuth workspaces as non-switchable until the variable is unset
449+
- does not claim to list every workspace the user can access unless each workspace has been authorized locally
450+
- does not mutate local or remote state
451+
452+
Examples:
453+
454+
```bash
455+
prisma-cli auth workspace list
456+
prisma-cli auth workspace list --json
457+
```
458+
459+
## `prisma-cli auth workspace use <id-or-name>`
460+
461+
Purpose:
462+
463+
- switch the local CLI workspace
464+
465+
Behavior:
466+
467+
- requires a stored OAuth session for the target workspace
468+
- accepts the cached workspace id, credential workspace id, or cached workspace name case-insensitively
469+
- changes local CLI context only; it does not mutate a remote resource
470+
- fails with `WORKSPACE_SWITCH_UNAVAILABLE` when `PRISMA_SERVICE_TOKEN` is set
471+
- fails with `WORKSPACE_NOT_AUTHENTICATED` when no cached OAuth session matches
472+
- fails with `WORKSPACE_AMBIGUOUS` when a workspace name matches multiple cached workspaces
473+
474+
Examples:
475+
476+
```bash
477+
prisma-cli auth workspace use wksp_123
478+
prisma-cli auth workspace use "Acme Inc"
479+
```
480+
481+
## `prisma-cli auth workspace select`
482+
483+
Purpose:
484+
485+
- interactively switch the local CLI workspace
486+
487+
Behavior:
488+
489+
- lists locally authenticated OAuth workspaces in an interactive picker
490+
- shows the workspace name and workspace id for each choice
491+
- changes local CLI context only; it does not mutate a remote resource
492+
- fails with `WORKSPACE_SWITCH_UNAVAILABLE` when `PRISMA_SERVICE_TOKEN` is set
493+
- when exactly one local OAuth workspace is available, selects it without prompting
494+
- fails in non-interactive mode when more than one local OAuth workspace is available
495+
496+
Examples:
497+
498+
```bash
499+
prisma-cli auth workspace select
500+
```
501+
502+
## `prisma-cli auth workspace logout <id-or-name>`
503+
504+
Purpose:
505+
506+
- remove one local OAuth workspace session
507+
508+
Behavior:
509+
510+
- requires a stored OAuth session for the target workspace
511+
- accepts the cached workspace id, credential workspace id, or cached workspace name case-insensitively
512+
- removes only the target workspace's local OAuth grant
513+
- works while `PRISMA_SERVICE_TOKEN` is set; the service token remains the active auth source
514+
- if the removed workspace was active, leaves no active local OAuth workspace selected
515+
- does not mutate a remote resource and does not revoke remote access
516+
- fails with `WORKSPACE_NOT_AUTHENTICATED` when no cached OAuth session matches
517+
- fails with `WORKSPACE_AMBIGUOUS` when a workspace name matches multiple cached workspaces
518+
519+
Examples:
520+
521+
```bash
522+
prisma-cli auth workspace logout wksp_123
523+
prisma-cli auth workspace logout "Acme Inc"
524+
```
525+
382526
## `prisma-cli project list`
383527

384528
Purpose:

docs/product/error-conventions.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ These codes are the minimum stable set for the MVP:
160160

161161
- `USAGE_ERROR`
162162
- `AUTH_REQUIRED`
163+
- `AUTH_CONFIG_INVALID`
164+
- `WORKSPACE_SWITCH_UNAVAILABLE`
165+
- `WORKSPACE_NOT_AUTHENTICATED`
166+
- `WORKSPACE_AMBIGUOUS`
163167
- `PROJECT_SETUP_REQUIRED`
164168
- `PROJECT_LINK_TARGET_REQUIRED`
165169
- `PROJECT_CREATE_FAILED`
@@ -217,13 +221,17 @@ Recommended meanings:
217221

218222
- `USAGE_ERROR`: invalid arguments or invalid command combination
219223
- `AUTH_REQUIRED`: command needs an authenticated session
224+
- `AUTH_CONFIG_INVALID`: environment auth configuration is present but unusable, such as an empty `PRISMA_SERVICE_TOKEN`
225+
- `WORKSPACE_SWITCH_UNAVAILABLE`: `PRISMA_SERVICE_TOKEN` is the active auth source, so local OAuth workspace switching cannot apply
226+
- `WORKSPACE_NOT_AUTHENTICATED`: requested workspace is not present in the local OAuth credentials store for a switch/logout operation; callers should run `auth login` for that workspace
227+
- `WORKSPACE_AMBIGUOUS`: requested workspace name matches more than one local OAuth workspace; callers should switch by workspace id
220228
- `PROJECT_SETUP_REQUIRED`: command needs explicit or durable Project context before it can continue
221229
- `PROJECT_LINK_TARGET_REQUIRED`: `project link` needs the user to choose an existing Project or create a new one
222230
- `PROJECT_CREATE_FAILED`: Project creation failed before deployment or linking could continue
223231
- `PROJECT_NOT_FOUND`: requested project does not exist or is not accessible
224232
- `PROJECT_AMBIGUOUS`: multiple safe project candidates matched
225233
- `APP_AMBIGUOUS`: multiple apps matched the inferred or explicit app target
226-
- `LOCAL_PROJECT_WORKSPACE_MISMATCH`: local Project pin points at a different workspace than the active authenticated workspace; callers should sign in to the linked workspace or relink the directory
234+
- `LOCAL_PROJECT_WORKSPACE_MISMATCH`: local Project pin points at a different workspace than the active authenticated workspace; callers should switch to the linked workspace or relink the directory
227235
- `LOCAL_STATE_WRITE_FAILED`: the CLI could not save local Project binding state such as `.prisma/local.json` or the matching `.gitignore` entry; callers should fix directory permissions or filesystem state before retrying
228236
- `LOCAL_STATE_STALE`: local Project pin no longer matches platform data and continuing would be ambiguous
229237
- `BRANCH_NOT_DEPLOYABLE`: command tried to deploy to a non-deployable branch context

docs/product/output-conventions.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ Current MVP commands map to patterns like this:
9999
| `auth login` | `mutate` |
100100
| `auth logout` | `mutate` |
101101
| `auth whoami` | `show` |
102+
| `auth workspace list` | `list` |
103+
| `auth workspace use` | `mutate` |
104+
| `auth workspace logout` | `mutate` |
102105
| `project list` | `list` |
103106
| `project show` | `show` |
104107
| `git connect` | `mutate` |

0 commit comments

Comments
 (0)