Skip to content

Commit 8c57c76

Browse files
committed
fix(config): stop auth persistence wiping the offline license
Every SetSpeakeasyAuthInfo call overwrote offline_license_token with whatever the context carried, so any authentication that produced no persistable token (platform not issuing tokens, free tier, workspace mismatch) silently erased a stored or manually configured offline license — including from non-auth commands via UseExistingAPIKeyIfAvailable. Replace the stored token only when authentication produced one; keep it when the workspace is unchanged; clear it on workspace change so the previous workspace's license is not carried over. Logout still clears it.
1 parent 3d6f8fa commit 8c57c76

2 files changed

Lines changed: 55 additions & 2 deletions

File tree

internal/config/config.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,14 @@ func SetSpeakeasyAuthInfo(ctx context.Context, info core.SpeakeasyAuthInfo) erro
110110
defaultWorkspaceID := vCfg.GetString("speakeasy_workspace_id")
111111
keepingSelfDefault := defaultWorkspaceID == "self" && info.WorkspaceID != "self"
112112
if !keepingSelfDefault {
113-
token, _ := core.GetLicenseTokenFromContext(ctx)
114-
vCfg.Set("offline_license_token", string(token))
113+
// A stored offline license may be manually configured or come from a
114+
// platform that does not issue tokens; only replace it when this
115+
// authentication produced one, and drop it when the workspace changes.
116+
if token, ok := core.GetLicenseTokenFromContext(ctx); ok {
117+
vCfg.Set("offline_license_token", string(token))
118+
} else if defaultWorkspaceID != info.WorkspaceID {
119+
vCfg.Set("offline_license_token", "")
120+
}
115121
vCfg.Set("speakeasy_api_key", info.APIKey)
116122
vCfg.Set("speakeasy_workspace_id", info.WorkspaceID)
117123
vCfg.Set("speakeasy_customer_id", info.CustomerID)

internal/config/config_test.go

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package config
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
core "github.com/speakeasy-api/speakeasy-core/auth"
8+
"github.com/spf13/viper"
9+
)
10+
11+
func TestSetSpeakeasyAuthInfoOfflineLicensePersistence(t *testing.T) { //nolint:paralleltest
12+
originalCfg, originalDir := vCfg, cfgDir
13+
t.Cleanup(func() { vCfg, cfgDir = originalCfg, originalDir })
14+
cfgDir = t.TempDir()
15+
vCfg = viper.New()
16+
vCfg.SetConfigName("config")
17+
vCfg.SetConfigType("yaml")
18+
vCfg.AddConfigPath(cfgDir)
19+
20+
vCfg.Set("speakeasy_workspace_id", "workspace-a")
21+
vCfg.Set("offline_license_token", "stored-token")
22+
23+
info := core.SpeakeasyAuthInfo{APIKey: "api-key", WorkspaceID: "workspace-a"}
24+
25+
if err := SetSpeakeasyAuthInfo(context.Background(), info); err != nil {
26+
t.Fatalf("set auth info without token: %v", err)
27+
}
28+
if got := GetOfflineLicenseToken(); got != "stored-token" {
29+
t.Fatalf("token-less authentication replaced the stored offline license: %q", got)
30+
}
31+
32+
freshCtx := context.WithValue(context.Background(), core.LicenseTokenKey, []byte("fresh-token"))
33+
if err := SetSpeakeasyAuthInfo(freshCtx, info); err != nil {
34+
t.Fatalf("set auth info with fresh token: %v", err)
35+
}
36+
if got := GetOfflineLicenseToken(); got != "fresh-token" {
37+
t.Fatalf("fresh license token was not persisted: %q", got)
38+
}
39+
40+
other := core.SpeakeasyAuthInfo{APIKey: "api-key", WorkspaceID: "workspace-b"}
41+
if err := SetSpeakeasyAuthInfo(context.Background(), other); err != nil {
42+
t.Fatalf("set auth info for other workspace: %v", err)
43+
}
44+
if got := GetOfflineLicenseToken(); got != "" {
45+
t.Fatalf("workspace change kept the previous workspace's offline license: %q", got)
46+
}
47+
}

0 commit comments

Comments
 (0)