Description
IntegrationService / ConfiguredIntegrationService (packages/plus/integrations) currently model at most one connected account per provider per domain. There is no per-connection identity and no way to have two accounts for the same provider (e.g. two GitHub.com accounts) connected at the same time, with one designated as the "primary" and used by default.
This blocks a consumer (Kepler, GitKraken's desktop dev tool) from migrating its provider-connection management off the gk CLI onto @gitkraken/core-gitlens, because the CLI/backend already support genuine multi-account connections and Kepler's UI/data model depends on that.
Current behavior (verified against 177e3bb2 on main)
-
ConfiguredIntegrationDescriptor has no connection/session identifier and no primary flag:
// packages/plus/integrations/src/authentication/models.ts:82-88
export interface ConfiguredIntegrationDescriptor {
readonly cloud: boolean;
readonly integrationId: IntegrationIds;
readonly scopes: string;
readonly domain?: string;
readonly expiresAt?: string | Date;
}
-
ConfiguredIntegrationService dedupes/overwrites by (integrationId, domain, cloud) — a second account for the same provider+domain silently replaces the first rather than coexisting:
addOrUpdateConfigured (configuredIntegrationService.ts:123-154) finds an "existing" descriptor by domain === descriptor.domain && integrationId === descriptor.integrationId && cloud === descriptor.cloud, and replaces it.
removeConfigured (configuredIntegrationService.ts:156-184) removes by the same (domain, cloud) key, so there's no way to target one of several connections.
-
Session storage mirrors this: the secret key is integration.auth[.cloud]:{id}|{domain} and ProviderAuthenticationSession.id is literally set to descriptor.domain (configuredIntegrationService.ts:366-368, used at integrationAuthenticationProvider.ts:266) — i.e. the "id" is not a real per-connection identifier, just the domain.
-
IntegrationAuthenticationProvider.deleteSession/getSession (integrationAuthenticationProvider.ts:31-40, impl at :64 and :91) operate on a {domain, scopes} descriptor, so they can only ever address "the" session for that domain — there's nothing to disambiguate between two accounts on the same domain.
-
IntegrationService.getConfigured() (integrationService.ts:443-448) just forwards to the above, so it inherits the same one-session-per-domain ceiling.
-
There is no primary/default-connection setter anywhere in packages/plus/integrations today.
I confirmed with a repo-wide grep that there's no existing tracking issue or TODO for this ("multi-account", "multiple connections", etc. all return zero hits), so filing fresh.
Why this matters
GitKraken's account/token backend and the gk CLI already support connecting multiple accounts to the same provider simultaneously, with a real per-connection identity and an explicit primary/default selection that can be switched or removed independently per connection (removing the primary promotes a secondary). This is a shipped, paid-tier capability today — @gitkraken/core-gitlens is the odd one out in not modeling it.
Concretely, Kepler's provider DTO already expects this shape end-to-end (ProviderConnection[] with tokenId/primary per connection, sourced from the CLI), and cannot adopt @gitkraken/core-gitlens as its provider backend for connection management until this exists — see internal tracking: gitkraken/kepler#1325 (part of gitkraken/kepler#1322).
Proposed API surface
ConfiguredIntegrationDescriptor gains a stable per-connection identifier (e.g. id/tokenId) and a primary: boolean field, and ConfiguredIntegrationService stops deduping by (integrationId, domain, cloud) alone — it should support an array of connections per (integrationId, domain).
IntegrationService/IntegrationAuthenticationService gain connection-identity-aware operations, roughly:
getConfigured(id?) returns all connections for a provider, each with its identifier and primary flag (superset of today's behavior when there's only one).
connectSecondary(id, ...) / an "add without replacing the existing primary" connect flow.
setPrimaryConnection(id, connectionId) to switch which connection is used by default.
deleteConnection(id, connectionId) to remove one specific connection (as opposed to deleteAllSessions(), which already exists and clears everything for a provider).
- Whatever backs this should be able to represent "no secondary support" gracefully for any provider/plan where GitKraken's backend doesn't offer it, since this is presumably gated by entitlement server-side already.
I'm intentionally not prescribing the exact wire format here since the team maintaining the account/token backend already knows the existing contract the CLI talks to — happy to pair on this if useful.
Acceptance criteria
- Two accounts can be connected to the same provider (e.g. GitHub) at the same time without one silently overwriting the other's stored session/config.
- Each connection is addressable by a stable identifier distinct from the domain.
- There is a way to read which connection is currently primary/default, and to switch it.
- Removing the primary connection when a secondary exists promotes the secondary (or at least leaves the provider in a well-defined connected state), rather than leaving the provider fully disconnected.
deleteConnection/equivalent only removes the targeted connection, not every session for that provider.
Additional context
- Internal motivating issue: gitkraken/kepler#1325 (part of epic gitkraken/kepler#1322).
- Checked against
@gitkraken/core-gitlens@0.4.0 / vscode-gitlens@177e3bb2.
Description
IntegrationService/ConfiguredIntegrationService(packages/plus/integrations) currently model at most one connected account per provider per domain. There is no per-connection identity and no way to have two accounts for the same provider (e.g. two GitHub.com accounts) connected at the same time, with one designated as the "primary" and used by default.This blocks a consumer (Kepler, GitKraken's desktop dev tool) from migrating its provider-connection management off the
gkCLI onto@gitkraken/core-gitlens, because the CLI/backend already support genuine multi-account connections and Kepler's UI/data model depends on that.Current behavior (verified against
177e3bb2onmain)ConfiguredIntegrationDescriptorhas no connection/session identifier and noprimaryflag:ConfiguredIntegrationServicededupes/overwrites by(integrationId, domain, cloud)— a second account for the same provider+domain silently replaces the first rather than coexisting:addOrUpdateConfigured(configuredIntegrationService.ts:123-154) finds an "existing" descriptor bydomain === descriptor.domain && integrationId === descriptor.integrationId && cloud === descriptor.cloud, and replaces it.removeConfigured(configuredIntegrationService.ts:156-184) removes by the same(domain, cloud)key, so there's no way to target one of several connections.Session storage mirrors this: the secret key is
integration.auth[.cloud]:{id}|{domain}andProviderAuthenticationSession.idis literally set todescriptor.domain(configuredIntegrationService.ts:366-368, used atintegrationAuthenticationProvider.ts:266) — i.e. the "id" is not a real per-connection identifier, just the domain.IntegrationAuthenticationProvider.deleteSession/getSession(integrationAuthenticationProvider.ts:31-40, impl at:64and:91) operate on a{domain, scopes}descriptor, so they can only ever address "the" session for that domain — there's nothing to disambiguate between two accounts on the same domain.IntegrationService.getConfigured()(integrationService.ts:443-448) just forwards to the above, so it inherits the same one-session-per-domain ceiling.There is no primary/default-connection setter anywhere in
packages/plus/integrationstoday.I confirmed with a repo-wide grep that there's no existing tracking issue or TODO for this ("multi-account", "multiple connections", etc. all return zero hits), so filing fresh.
Why this matters
GitKraken's account/token backend and the
gkCLI already support connecting multiple accounts to the same provider simultaneously, with a real per-connection identity and an explicit primary/default selection that can be switched or removed independently per connection (removing the primary promotes a secondary). This is a shipped, paid-tier capability today —@gitkraken/core-gitlensis the odd one out in not modeling it.Concretely, Kepler's provider DTO already expects this shape end-to-end (
ProviderConnection[]withtokenId/primaryper connection, sourced from the CLI), and cannot adopt@gitkraken/core-gitlensas its provider backend for connection management until this exists — see internal tracking: gitkraken/kepler#1325 (part of gitkraken/kepler#1322).Proposed API surface
ConfiguredIntegrationDescriptorgains a stable per-connection identifier (e.g.id/tokenId) and aprimary: booleanfield, andConfiguredIntegrationServicestops deduping by(integrationId, domain, cloud)alone — it should support an array of connections per(integrationId, domain).IntegrationService/IntegrationAuthenticationServicegain connection-identity-aware operations, roughly:getConfigured(id?)returns all connections for a provider, each with its identifier andprimaryflag (superset of today's behavior when there's only one).connectSecondary(id, ...)/ an "add without replacing the existing primary" connect flow.setPrimaryConnection(id, connectionId)to switch which connection is used by default.deleteConnection(id, connectionId)to remove one specific connection (as opposed todeleteAllSessions(), which already exists and clears everything for a provider).I'm intentionally not prescribing the exact wire format here since the team maintaining the account/token backend already knows the existing contract the CLI talks to — happy to pair on this if useful.
Acceptance criteria
deleteConnection/equivalent only removes the targeted connection, not every session for that provider.Additional context
@gitkraken/core-gitlens@0.4.0/vscode-gitlens@177e3bb2.