mirror of
https://github.com/openai/codex.git
synced 2026-11-10 21:00:47 +00:00
Add experimental ChatGPT workspace switching to app-server (#57514)
## What changed - Add `accountSession/list` and `accountSession/switch` for the active managed ChatGPT login, with optional workspace membership refresh. - Validate membership and workspace policy, pause new turns, and unload running threads before switching credentials. Notify clients with `thread/closed` and `account/updated`; selecting the current workspace leaves threads running. - Support browser sign-in for SSO through `allowBrowserLogin`, binding the callback to the requesting login and workspace. Require a separate sign-in for government workspaces. - Preserve the selected workspace across token refresh and conditionally restore the previous workspace if routing validation fails. Guard credential writes against concurrent login or logout changes. ## Testing Add app-server and auth tests covering workspace switching, credential preservation, policy and identity rejection, conditional rollback, stale sessions, and SSO callback validation. GitOrigin-RevId: 1234abc33b71e69b66318da3e201fccc08867906
This commit is contained in:
committed by
copyberry
parent
27ace641fe
commit
d257beb16a
+97
@@ -6919,6 +6919,103 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"AccountSession": {
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"email": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"imageUrl": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"isActive": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"lastUsedAt": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"selectedWorkspaceAccountId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"workspaces": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/v2/AccountSessionWorkspace"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isActive",
|
||||
"lastUsedAt",
|
||||
"sessionId",
|
||||
"workspaces"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AccountSessionWorkspace": {
|
||||
"properties": {
|
||||
"accountId": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageUrl": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"kind": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/AccountSessionWorkspaceKind"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"accountId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AccountSessionWorkspaceKind": {
|
||||
"enum": [
|
||||
"personal",
|
||||
"workspace"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"AccountTokenUsageDailyBucket": {
|
||||
"properties": {
|
||||
"startDate": {
|
||||
|
||||
+97
@@ -154,6 +154,103 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"AccountSession": {
|
||||
"properties": {
|
||||
"displayName": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"email": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"imageUrl": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"isActive": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"lastUsedAt": {
|
||||
"format": "int64",
|
||||
"type": "integer"
|
||||
},
|
||||
"selectedWorkspaceAccountId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"sessionId": {
|
||||
"type": "string"
|
||||
},
|
||||
"userId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"workspaces": {
|
||||
"items": {
|
||||
"$ref": "#/definitions/AccountSessionWorkspace"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isActive",
|
||||
"lastUsedAt",
|
||||
"sessionId",
|
||||
"workspaces"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AccountSessionWorkspace": {
|
||||
"properties": {
|
||||
"accountId": {
|
||||
"type": "string"
|
||||
},
|
||||
"imageUrl": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"kind": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/AccountSessionWorkspaceKind"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"accountId"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AccountSessionWorkspaceKind": {
|
||||
"enum": [
|
||||
"personal",
|
||||
"workspace"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"AccountTokenUsageDailyBucket": {
|
||||
"properties": {
|
||||
"startDate": {
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AccountSessionWorkspace } from "./AccountSessionWorkspace";
|
||||
|
||||
export type AccountSession = { sessionId: string, email: string | null, userId: string | null, displayName: string | null, imageUrl: string | null, lastUsedAt: bigint, isActive: boolean, selectedWorkspaceAccountId: string | null, workspaces: Array<AccountSessionWorkspace>, };
|
||||
@@ -0,0 +1,6 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { AccountSessionWorkspaceKind } from "./AccountSessionWorkspaceKind";
|
||||
|
||||
export type AccountSessionWorkspace = { accountId: string, name: string | null, imageUrl: string | null, kind: AccountSessionWorkspaceKind | null, };
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type AccountSessionWorkspaceKind = "personal" | "workspace";
|
||||
@@ -4,6 +4,9 @@ export type { Account } from "./Account";
|
||||
export type { AccountLoginCompletedNotification } from "./AccountLoginCompletedNotification";
|
||||
export type { AccountRateLimitsUpdatedNotification } from "./AccountRateLimitsUpdatedNotification";
|
||||
export type { AccountRoutingOverride } from "./AccountRoutingOverride";
|
||||
export type { AccountSession } from "./AccountSession";
|
||||
export type { AccountSessionWorkspace } from "./AccountSessionWorkspace";
|
||||
export type { AccountSessionWorkspaceKind } from "./AccountSessionWorkspaceKind";
|
||||
export type { AccountTokenUsageDailyBucket } from "./AccountTokenUsageDailyBucket";
|
||||
export type { AccountTokenUsageSummary } from "./AccountTokenUsageSummary";
|
||||
export type { AccountUpdatedNotification } from "./AccountUpdatedNotification";
|
||||
|
||||
@@ -1358,6 +1358,22 @@ client_request_definitions! {
|
||||
response: v2::CancelLoginAccountResponse,
|
||||
},
|
||||
|
||||
#[experimental("accountSession/list")]
|
||||
AccountSessionsList => "accountSession/list" {
|
||||
params: v2::AccountSessionsListParams,
|
||||
serialization: global("account-auth"),
|
||||
manual_payload_conversion: manual,
|
||||
response: v2::AccountSessionsResponse,
|
||||
},
|
||||
|
||||
#[experimental("accountSession/switch")]
|
||||
AccountSessionsSwitch => "accountSession/switch" {
|
||||
params: v2::AccountSessionsSwitchParams,
|
||||
serialization: global("account-auth"),
|
||||
manual_payload_conversion: manual,
|
||||
response: v2::AccountSessionsSwitchResponse,
|
||||
},
|
||||
|
||||
LogoutAccount => "account/logout" {
|
||||
params: #[ts(type = "undefined")] #[serde(skip_serializing_if = "Option::is_none")] Option<()>,
|
||||
serialization: global("account-auth"),
|
||||
|
||||
@@ -227,8 +227,26 @@ pub struct AccountSessionsLogoutParams {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export_to = "v2/")]
|
||||
pub struct AccountSessionsSwitchParams {
|
||||
/// Opaque active-login identifier returned by accountSession/list.
|
||||
pub session_id: String,
|
||||
/// Workspace to select within that login; this is not a user identifier.
|
||||
pub account_id: String,
|
||||
/// Allow Core to start browser sign-in if this session needs SSO for the workspace.
|
||||
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
||||
#[ts(optional, as = "Option<bool>")]
|
||||
pub allow_browser_login: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
#[serde(untagged, rename_all_fields = "camelCase")]
|
||||
#[ts(untagged, rename_all_fields = "camelCase", export_to = "v2/")]
|
||||
pub enum AccountSessionsSwitchResponse {
|
||||
Switched {
|
||||
#[serde(flatten)]
|
||||
sessions: AccountSessionsResponse,
|
||||
},
|
||||
/// Open the URL and await account/login/completed for this login ID.
|
||||
BrowserLogin { login_id: String, auth_url: String },
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
||||
|
||||
Reference in New Issue
Block a user