From c242c3876682550715c966836515a27e28152669 Mon Sep 17 00:00:00 2001 From: Nimish Date: Wed, 28 Oct 2026 15:05:23 +0530 Subject: [PATCH 1/2] feat(ai): accept key-bearing v2 Agent tokens The Console now mints Agent tokens as pss_agent:v2:{token}:{kx_pub}:{share_a}:{wrap_key}, the same layout as Service Account tokens. Parse that form and reject the bearer-only v1 form, which was never released. Only the bearer part is sent. --- src/internal/agentapi/client_test.go | 33 +++++++++---------- src/internal/agentsdk/client_test.go | 12 +++---- src/internal/agentsdk/types.go | 8 ++--- .../aiproxy/remote_integration_test.go | 2 +- src/internal/airuntime/runtime_test.go | 4 +-- src/pkg/phase/agent_scope_test.go | 4 +-- 6 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/internal/agentapi/client_test.go b/src/internal/agentapi/client_test.go index c41273d1..f81753d0 100644 --- a/src/internal/agentapi/client_test.go +++ b/src/internal/agentapi/client_test.go @@ -24,9 +24,6 @@ func testToken(prefix string, version int, lookup string) string { if lookup == "" { lookup = strings.Repeat("a", 64) } - if prefix == "pss_agent" { - return fmt.Sprintf("%s:v%d:%s", prefix, version, lookup) - } return fmt.Sprintf("%s:v%d:%s:%s:%s:%s", prefix, version, lookup, strings.Repeat("b", 64), strings.Repeat("c", 64), strings.Repeat("d", 64)) } @@ -40,7 +37,7 @@ func TestParseDurableTokenCompatibility(t *testing.T) { {testToken("pss_user", 2, ""), TokenUser}, {testToken("pss_service", 1, ""), TokenService}, {testToken("pss_service", 2, ""), TokenServiceAccount}, - {testToken("pss_agent", 1, ""), TokenAgent}, + {testToken("pss_agent", 2, ""), TokenAgent}, } for _, test := range tests { got, lookup, err := ParseDurableToken(test.token) @@ -54,7 +51,7 @@ func TestParseDurableTokenCompatibility(t *testing.T) { for _, invalid := range []string{ "", "pss_agent:v1:" + strings.Repeat("a:", 4) + "a", - "pss_agent:v2:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64), + "pss_agent:v1:" + strings.Repeat("a", 64), "pss_user:v1:not-hex", } { if _, _, err := ParseDurableToken(invalid); err == nil { @@ -66,7 +63,7 @@ func TestParseDurableTokenCompatibility(t *testing.T) { func TestClientBypassesDataPlaneProxyInsideManagedChild(t *testing.T) { t.Setenv("PHASE_AI_ENABLED", "1") t.Setenv("HTTPS_PROXY", "http://127.0.0.1:1") - client, err := NewClient("https://console.phase.dev", testToken("pss_agent", 1, ""), nil) + client, err := NewClient("https://console.phase.dev", testToken("pss_agent", 2, ""), nil) if err != nil { t.Fatal(err) } @@ -92,7 +89,7 @@ func TestClientSendsCapturedSharedUserAgentOnEveryAgentRoute(t *testing.T) { })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -172,7 +169,7 @@ func TestRequestRoutesUseSharedSDKWithoutChangingRelayContract(t *testing.T) { })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -208,7 +205,7 @@ func TestRequestRoutesUseSharedSDKWithoutChangingRelayContract(t *testing.T) { } func TestSharedSDKRequestErrorIsBoundedAndMapped(t *testing.T) { - secret := testToken("pss_agent", 1, "") + secret := testToken("pss_agent", 2, "") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if strings.HasSuffix(r.URL.Path, "/sessions/") { @@ -278,7 +275,7 @@ func TestClientNeverFollowsSameOrCrossOriginRedirects(t *testing.T) { originalRedirect := func(*http.Request, []*http.Request) error { return nil } supplied := &http.Client{Transport: source.Client().Transport, CheckRedirect: originalRedirect} - client, err := NewClient(source.URL, testToken("pss_agent", 1, ""), supplied) + client, err := NewClient(source.URL, testToken("pss_agent", 2, ""), supplied) if err != nil { t.Fatal(err) } @@ -308,7 +305,7 @@ func TestAgentAPIBasePathNormalization(t *testing.T) { {"https://example.test/phase/service/public/v1/agents", "/phase/service/public/v1/agents/sessions/"}, } for _, test := range tests { - client, err := NewClient(test.host, testToken("pss_agent", 1, ""), http.DefaultClient) + client, err := NewClient(test.host, testToken("pss_agent", 2, ""), http.DefaultClient) if err != nil { t.Fatalf("NewClient(%q): %v", test.host, err) } @@ -325,7 +322,7 @@ func TestAgentAPIBasePathNormalization(t *testing.T) { "https://example.test?tenant=one", "https://example.test#fragment", } { - if _, err := NewClient(invalid, testToken("pss_agent", 1, ""), http.DefaultClient); err == nil { + if _, err := NewClient(invalid, testToken("pss_agent", 2, ""), http.DefaultClient); err == nil { t.Fatalf("expected unsafe host rejection for %q", invalid) } } @@ -348,7 +345,7 @@ func TestAgentAPIRequiresHTTPSOutsideExplicitLoopback(t *testing.T) { } for _, test := range tests { t.Run(test.host, func(t *testing.T) { - _, err := NewClient(test.host, testToken("pss_agent", 1, ""), http.DefaultClient) + _, err := NewClient(test.host, testToken("pss_agent", 2, ""), http.DefaultClient) if (err == nil) != test.allowed { t.Fatalf("NewClient(%q) error=%v, allowed=%t", test.host, err, test.allowed) } @@ -681,7 +678,7 @@ func TestAgentIdentityUsesAgentOnlyEndpoint(t *testing.T) { })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -708,7 +705,7 @@ func TestAgentIdentityRejectsNonAgentTokensAndIncompleteResponses(t *testing.T) _, _ = w.Write([]byte(`{"schemaVersion":1,"principalType":"agent","agent":{"id":"agent-123","name":"Claude"}}`)) })) defer server.Close() - agentClient, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) + agentClient, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -728,7 +725,7 @@ func TestRuntimeRoutesCarrySessionCredential(t *testing.T) { _, _ = w.Write([]byte(`{}`)) })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -759,7 +756,7 @@ func TestRuntimeRoutesCarrySessionCredential(t *testing.T) { } func TestAPIErrorDropsDetailsAndRedactsCredentialShapedMessage(t *testing.T) { - secret := testToken("pss_agent", 1, "") + secret := testToken("pss_agent", 2, "") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) _, _ = fmt.Fprintf(w, `{"error":{"code":"denied","message":"bad %s","retryable":false,"details":{"raw":"live-secret"}}}`, secret) @@ -799,7 +796,7 @@ func TestAPIErrorCarriesRetryMetadata(t *testing.T) { })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) if err != nil { t.Fatal(err) } diff --git a/src/internal/agentsdk/client_test.go b/src/internal/agentsdk/client_test.go index 7b3c3a17..1c50cfe6 100644 --- a/src/internal/agentsdk/client_test.go +++ b/src/internal/agentsdk/client_test.go @@ -25,7 +25,7 @@ func TestClientRoutesStatusesAndDualAuthentication(t *testing.T) { const durableToken = "durable-token" const mintedSecret = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - const mintedFullToken = "pss_agent:v1:" + mintedSecret + const mintedFullToken = "pss_agent:v2:" + mintedSecret + ":" + mintedSecret + ":" + mintedSecret + ":" + mintedSecret const sessionCredential = "session-credential" session := SessionAuth{SessionUID: "session-123", Credential: SensitiveString(sessionCredential)} @@ -595,7 +595,7 @@ func TestAuthenticationFromPhaseToken(t *testing.T) { kind AuthenticationType token string }{ - {kind: AuthenticationAgent, token: "pss_agent:v1:" + lookup}, + {kind: AuthenticationAgent, token: "pss_agent:v2:" + strings.Join(fullParts, ":")}, {kind: AuthenticationUser, token: "pss_user:v1:" + strings.Join(fullParts, ":")}, {kind: AuthenticationServiceAccount, token: "pss_service:v2:" + strings.Join(fullParts, ":")}, } @@ -610,14 +610,14 @@ func TestAuthenticationFromPhaseToken(t *testing.T) { } }) } - if _, err := AuthenticationFromPhaseToken("pss_agent:v1:bad"); err == nil { + if _, err := AuthenticationFromPhaseToken("pss_agent:v2:bad"); err == nil { t.Fatal("expected malformed token error") } - if _, err := AuthenticationFromPhaseToken("pss_agent:v2:" + strings.Join(fullParts, ":")); err == nil { + if _, err := AuthenticationFromPhaseToken("pss_agent:v1:" + lookup); err == nil { t.Fatal("expected unsupported Agent token version error") } - if _, err := AuthenticationFromPhaseToken("pss_agent:v1:" + lookup + ":" + lookup); err == nil { - t.Fatal("expected Agent key-share token to be rejected") + if _, err := AuthenticationFromPhaseToken("pss_agent:v2:" + lookup); err == nil { + t.Fatal("expected Agent token without key shares to be rejected") } if _, err := AuthenticationFromPhaseToken("pss_user:v2:" + strings.Join(fullParts, ":")); err == nil { t.Fatal("expected unsupported PAT version error") diff --git a/src/internal/agentsdk/types.go b/src/internal/agentsdk/types.go index 841fc7f6..fc2ca664 100644 --- a/src/internal/agentsdk/types.go +++ b/src/internal/agentsdk/types.go @@ -387,8 +387,8 @@ func ServiceAccountAuthentication(token string) Authentication { // AuthenticationFromPhaseToken extracts the bearer lookup value from a full // Agent, Personal Access, or Service Account token without involving phase.New -// or the secrets client. Agent tokens are opaque v1 bearer secrets. Personal -// Access Tokens use v1 and Service Account tokens use v2. +// or the secrets client. All three carry key shares; Personal Access Tokens use +// v1, and Agent and Service Account tokens use v2. func AuthenticationFromPhaseToken(token string) (Authentication, error) { parts := strings.Split(token, ":") if len(parts) < 2 { @@ -396,10 +396,10 @@ func AuthenticationFromPhaseToken(token string) (Authentication, error) { } switch parts[0] { case "pss_agent": - if parts[1] != "v1" { + if parts[1] != "v2" { return Authentication{}, fmt.Errorf("agents: invalid Agent token version") } - if len(parts) != 3 || !isHex64(parts[2]) { + if !validKeyShareToken(parts) { return Authentication{}, fmt.Errorf("agents: invalid Phase token") } return AgentAuthentication(parts[2]), nil diff --git a/src/internal/aiproxy/remote_integration_test.go b/src/internal/aiproxy/remote_integration_test.go index f635ed1b..532cea98 100644 --- a/src/internal/aiproxy/remote_integration_test.go +++ b/src/internal/aiproxy/remote_integration_test.go @@ -488,7 +488,7 @@ func writeRemoteTLSIdentity(t *testing.T, host string) (string, string, *x509.Ce } func remoteAgentToken() string { - return "pss_agent:v1:" + strings.Repeat("a", 64) + return "pss_agent:v2:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) } func remoteProxyHTTPClient(t *testing.T, rawProxyURL string, roots *x509.CertPool) *http.Client { diff --git a/src/internal/airuntime/runtime_test.go b/src/internal/airuntime/runtime_test.go index f9132f3f..8ac53e62 100644 --- a/src/internal/airuntime/runtime_test.go +++ b/src/internal/airuntime/runtime_test.go @@ -43,7 +43,7 @@ func TestRuntimeRejectsLegacyServiceToken(t *testing.T) { } func TestSavedLoginRejectsObsoleteAgentTokenAsAuthenticationRequired(t *testing.T) { - token := "pss_agent:v2:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) + token := "pss_agent:v1:" + strings.Repeat("a", 64) _, err := clientForSavedLogin("https://console.phase.dev", token) if !errors.Is(err, ErrAuthenticationRequired) { t.Fatalf("obsolete Agent token error = %v, want ErrAuthenticationRequired", err) @@ -51,7 +51,7 @@ func TestSavedLoginRejectsObsoleteAgentTokenAsAuthenticationRequired(t *testing. } func TestSavedLoginRejectsInvalidHostAsAuthenticationRequired(t *testing.T) { - token := "pss_agent:v1:" + strings.Repeat("a", 64) + token := "pss_agent:v2:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) _, err := clientForSavedLogin("not-a-url", token) if !errors.Is(err, ErrAuthenticationRequired) { t.Fatalf("invalid saved host error = %v, want ErrAuthenticationRequired", err) diff --git a/src/pkg/phase/agent_scope_test.go b/src/pkg/phase/agent_scope_test.go index 5f15788d..c41fc6c7 100644 --- a/src/pkg/phase/agent_scope_test.go +++ b/src/pkg/phase/agent_scope_test.go @@ -6,7 +6,7 @@ import ( ) func TestNewPhaseRejectsAgentTokenForLegacyAPIs(t *testing.T) { - token := "pss_agent:v1:" + strings.Repeat("a", 64) + token := "pss_agent:v2:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) _, err := NewPhase(false, token, "https://console.phase.dev") if err == nil || !strings.Contains(err.Error(), "phase ai") { t.Fatalf("expected clear Agent scope error, got %v", err) @@ -14,7 +14,7 @@ func TestNewPhaseRejectsAgentTokenForLegacyAPIs(t *testing.T) { } func TestNewPhaseRejectsAgentTokenFromEnvironment(t *testing.T) { - t.Setenv("PHASE_SERVICE_TOKEN", "pss_agent:v1:"+strings.Repeat("a", 64)) + t.Setenv("PHASE_SERVICE_TOKEN", "pss_agent:v2:"+strings.Repeat("a", 64)+":"+strings.Repeat("b", 64)+":"+strings.Repeat("c", 64)+":"+strings.Repeat("d", 64)) _, err := NewPhase(true, "", "") if err == nil || !strings.Contains(err.Error(), "phase ai") { t.Fatalf("expected clear Agent scope error, got %v", err) From 775ee6893cd6962caa7097694d919c3fabd1a142 Mon Sep 17 00:00:00 2001 From: Nimish Date: Wed, 28 Oct 2026 18:46:05 +0530 Subject: [PATCH 2/2] feat(ai): version key-bearing Agent tokens as v1 No released CLI or Console has ever handled pss_agent tokens, so the first released form is pss_agent:v1:{token}:{kx_pub}:{share_a}:{wrap_key}. The prefix alone selects Agent authentication, so the version does not need to match Service Account tokens. v2 and the bearer-only form are rejected. --- src/internal/agentapi/client_test.go | 29 ++++++++++--------- src/internal/agentsdk/client_test.go | 10 +++---- src/internal/agentsdk/types.go | 6 ++-- .../aiproxy/remote_integration_test.go | 2 +- src/internal/airuntime/runtime_test.go | 2 +- src/pkg/phase/agent_scope_test.go | 4 +-- 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/internal/agentapi/client_test.go b/src/internal/agentapi/client_test.go index f81753d0..b973a917 100644 --- a/src/internal/agentapi/client_test.go +++ b/src/internal/agentapi/client_test.go @@ -37,7 +37,7 @@ func TestParseDurableTokenCompatibility(t *testing.T) { {testToken("pss_user", 2, ""), TokenUser}, {testToken("pss_service", 1, ""), TokenService}, {testToken("pss_service", 2, ""), TokenServiceAccount}, - {testToken("pss_agent", 2, ""), TokenAgent}, + {testToken("pss_agent", 1, ""), TokenAgent}, } for _, test := range tests { got, lookup, err := ParseDurableToken(test.token) @@ -52,6 +52,7 @@ func TestParseDurableTokenCompatibility(t *testing.T) { "", "pss_agent:v1:" + strings.Repeat("a:", 4) + "a", "pss_agent:v1:" + strings.Repeat("a", 64), + testToken("pss_agent", 2, ""), "pss_user:v1:not-hex", } { if _, _, err := ParseDurableToken(invalid); err == nil { @@ -63,7 +64,7 @@ func TestParseDurableTokenCompatibility(t *testing.T) { func TestClientBypassesDataPlaneProxyInsideManagedChild(t *testing.T) { t.Setenv("PHASE_AI_ENABLED", "1") t.Setenv("HTTPS_PROXY", "http://127.0.0.1:1") - client, err := NewClient("https://console.phase.dev", testToken("pss_agent", 2, ""), nil) + client, err := NewClient("https://console.phase.dev", testToken("pss_agent", 1, ""), nil) if err != nil { t.Fatal(err) } @@ -89,7 +90,7 @@ func TestClientSendsCapturedSharedUserAgentOnEveryAgentRoute(t *testing.T) { })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -169,7 +170,7 @@ func TestRequestRoutesUseSharedSDKWithoutChangingRelayContract(t *testing.T) { })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -205,7 +206,7 @@ func TestRequestRoutesUseSharedSDKWithoutChangingRelayContract(t *testing.T) { } func TestSharedSDKRequestErrorIsBoundedAndMapped(t *testing.T) { - secret := testToken("pss_agent", 2, "") + secret := testToken("pss_agent", 1, "") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") if strings.HasSuffix(r.URL.Path, "/sessions/") { @@ -275,7 +276,7 @@ func TestClientNeverFollowsSameOrCrossOriginRedirects(t *testing.T) { originalRedirect := func(*http.Request, []*http.Request) error { return nil } supplied := &http.Client{Transport: source.Client().Transport, CheckRedirect: originalRedirect} - client, err := NewClient(source.URL, testToken("pss_agent", 2, ""), supplied) + client, err := NewClient(source.URL, testToken("pss_agent", 1, ""), supplied) if err != nil { t.Fatal(err) } @@ -305,7 +306,7 @@ func TestAgentAPIBasePathNormalization(t *testing.T) { {"https://example.test/phase/service/public/v1/agents", "/phase/service/public/v1/agents/sessions/"}, } for _, test := range tests { - client, err := NewClient(test.host, testToken("pss_agent", 2, ""), http.DefaultClient) + client, err := NewClient(test.host, testToken("pss_agent", 1, ""), http.DefaultClient) if err != nil { t.Fatalf("NewClient(%q): %v", test.host, err) } @@ -322,7 +323,7 @@ func TestAgentAPIBasePathNormalization(t *testing.T) { "https://example.test?tenant=one", "https://example.test#fragment", } { - if _, err := NewClient(invalid, testToken("pss_agent", 2, ""), http.DefaultClient); err == nil { + if _, err := NewClient(invalid, testToken("pss_agent", 1, ""), http.DefaultClient); err == nil { t.Fatalf("expected unsafe host rejection for %q", invalid) } } @@ -345,7 +346,7 @@ func TestAgentAPIRequiresHTTPSOutsideExplicitLoopback(t *testing.T) { } for _, test := range tests { t.Run(test.host, func(t *testing.T) { - _, err := NewClient(test.host, testToken("pss_agent", 2, ""), http.DefaultClient) + _, err := NewClient(test.host, testToken("pss_agent", 1, ""), http.DefaultClient) if (err == nil) != test.allowed { t.Fatalf("NewClient(%q) error=%v, allowed=%t", test.host, err, test.allowed) } @@ -678,7 +679,7 @@ func TestAgentIdentityUsesAgentOnlyEndpoint(t *testing.T) { })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -705,7 +706,7 @@ func TestAgentIdentityRejectsNonAgentTokensAndIncompleteResponses(t *testing.T) _, _ = w.Write([]byte(`{"schemaVersion":1,"principalType":"agent","agent":{"id":"agent-123","name":"Claude"}}`)) })) defer server.Close() - agentClient, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) + agentClient, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -725,7 +726,7 @@ func TestRuntimeRoutesCarrySessionCredential(t *testing.T) { _, _ = w.Write([]byte(`{}`)) })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) if err != nil { t.Fatal(err) } @@ -756,7 +757,7 @@ func TestRuntimeRoutesCarrySessionCredential(t *testing.T) { } func TestAPIErrorDropsDetailsAndRedactsCredentialShapedMessage(t *testing.T) { - secret := testToken("pss_agent", 2, "") + secret := testToken("pss_agent", 1, "") server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusUnauthorized) _, _ = fmt.Fprintf(w, `{"error":{"code":"denied","message":"bad %s","retryable":false,"details":{"raw":"live-secret"}}}`, secret) @@ -796,7 +797,7 @@ func TestAPIErrorCarriesRetryMetadata(t *testing.T) { })) defer server.Close() - client, err := NewClient(server.URL, testToken("pss_agent", 2, ""), server.Client()) + client, err := NewClient(server.URL, testToken("pss_agent", 1, ""), server.Client()) if err != nil { t.Fatal(err) } diff --git a/src/internal/agentsdk/client_test.go b/src/internal/agentsdk/client_test.go index 1c50cfe6..4bdef681 100644 --- a/src/internal/agentsdk/client_test.go +++ b/src/internal/agentsdk/client_test.go @@ -25,7 +25,7 @@ func TestClientRoutesStatusesAndDualAuthentication(t *testing.T) { const durableToken = "durable-token" const mintedSecret = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" - const mintedFullToken = "pss_agent:v2:" + mintedSecret + ":" + mintedSecret + ":" + mintedSecret + ":" + mintedSecret + const mintedFullToken = "pss_agent:v1:" + mintedSecret + ":" + mintedSecret + ":" + mintedSecret + ":" + mintedSecret const sessionCredential = "session-credential" session := SessionAuth{SessionUID: "session-123", Credential: SensitiveString(sessionCredential)} @@ -595,7 +595,7 @@ func TestAuthenticationFromPhaseToken(t *testing.T) { kind AuthenticationType token string }{ - {kind: AuthenticationAgent, token: "pss_agent:v2:" + strings.Join(fullParts, ":")}, + {kind: AuthenticationAgent, token: "pss_agent:v1:" + strings.Join(fullParts, ":")}, {kind: AuthenticationUser, token: "pss_user:v1:" + strings.Join(fullParts, ":")}, {kind: AuthenticationServiceAccount, token: "pss_service:v2:" + strings.Join(fullParts, ":")}, } @@ -610,13 +610,13 @@ func TestAuthenticationFromPhaseToken(t *testing.T) { } }) } - if _, err := AuthenticationFromPhaseToken("pss_agent:v2:bad"); err == nil { + if _, err := AuthenticationFromPhaseToken("pss_agent:v1:bad"); err == nil { t.Fatal("expected malformed token error") } - if _, err := AuthenticationFromPhaseToken("pss_agent:v1:" + lookup); err == nil { + if _, err := AuthenticationFromPhaseToken("pss_agent:v2:" + strings.Join(fullParts, ":")); err == nil { t.Fatal("expected unsupported Agent token version error") } - if _, err := AuthenticationFromPhaseToken("pss_agent:v2:" + lookup); err == nil { + if _, err := AuthenticationFromPhaseToken("pss_agent:v1:" + lookup); err == nil { t.Fatal("expected Agent token without key shares to be rejected") } if _, err := AuthenticationFromPhaseToken("pss_user:v2:" + strings.Join(fullParts, ":")); err == nil { diff --git a/src/internal/agentsdk/types.go b/src/internal/agentsdk/types.go index fc2ca664..e634c722 100644 --- a/src/internal/agentsdk/types.go +++ b/src/internal/agentsdk/types.go @@ -387,8 +387,8 @@ func ServiceAccountAuthentication(token string) Authentication { // AuthenticationFromPhaseToken extracts the bearer lookup value from a full // Agent, Personal Access, or Service Account token without involving phase.New -// or the secrets client. All three carry key shares; Personal Access Tokens use -// v1, and Agent and Service Account tokens use v2. +// or the secrets client. All three carry key shares; Agent and Personal Access +// Tokens use v1, and Service Account tokens use v2. func AuthenticationFromPhaseToken(token string) (Authentication, error) { parts := strings.Split(token, ":") if len(parts) < 2 { @@ -396,7 +396,7 @@ func AuthenticationFromPhaseToken(token string) (Authentication, error) { } switch parts[0] { case "pss_agent": - if parts[1] != "v2" { + if parts[1] != "v1" { return Authentication{}, fmt.Errorf("agents: invalid Agent token version") } if !validKeyShareToken(parts) { diff --git a/src/internal/aiproxy/remote_integration_test.go b/src/internal/aiproxy/remote_integration_test.go index 532cea98..018f3bf3 100644 --- a/src/internal/aiproxy/remote_integration_test.go +++ b/src/internal/aiproxy/remote_integration_test.go @@ -488,7 +488,7 @@ func writeRemoteTLSIdentity(t *testing.T, host string) (string, string, *x509.Ce } func remoteAgentToken() string { - return "pss_agent:v2:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) + return "pss_agent:v1:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) } func remoteProxyHTTPClient(t *testing.T, rawProxyURL string, roots *x509.CertPool) *http.Client { diff --git a/src/internal/airuntime/runtime_test.go b/src/internal/airuntime/runtime_test.go index 8ac53e62..e5e7533a 100644 --- a/src/internal/airuntime/runtime_test.go +++ b/src/internal/airuntime/runtime_test.go @@ -51,7 +51,7 @@ func TestSavedLoginRejectsObsoleteAgentTokenAsAuthenticationRequired(t *testing. } func TestSavedLoginRejectsInvalidHostAsAuthenticationRequired(t *testing.T) { - token := "pss_agent:v2:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) + token := "pss_agent:v1:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) _, err := clientForSavedLogin("not-a-url", token) if !errors.Is(err, ErrAuthenticationRequired) { t.Fatalf("invalid saved host error = %v, want ErrAuthenticationRequired", err) diff --git a/src/pkg/phase/agent_scope_test.go b/src/pkg/phase/agent_scope_test.go index c41fc6c7..f9e0af85 100644 --- a/src/pkg/phase/agent_scope_test.go +++ b/src/pkg/phase/agent_scope_test.go @@ -6,7 +6,7 @@ import ( ) func TestNewPhaseRejectsAgentTokenForLegacyAPIs(t *testing.T) { - token := "pss_agent:v2:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) + token := "pss_agent:v1:" + strings.Repeat("a", 64) + ":" + strings.Repeat("b", 64) + ":" + strings.Repeat("c", 64) + ":" + strings.Repeat("d", 64) _, err := NewPhase(false, token, "https://console.phase.dev") if err == nil || !strings.Contains(err.Error(), "phase ai") { t.Fatalf("expected clear Agent scope error, got %v", err) @@ -14,7 +14,7 @@ func TestNewPhaseRejectsAgentTokenForLegacyAPIs(t *testing.T) { } func TestNewPhaseRejectsAgentTokenFromEnvironment(t *testing.T) { - t.Setenv("PHASE_SERVICE_TOKEN", "pss_agent:v2:"+strings.Repeat("a", 64)+":"+strings.Repeat("b", 64)+":"+strings.Repeat("c", 64)+":"+strings.Repeat("d", 64)) + t.Setenv("PHASE_SERVICE_TOKEN", "pss_agent:v1:"+strings.Repeat("a", 64)+":"+strings.Repeat("b", 64)+":"+strings.Repeat("c", 64)+":"+strings.Repeat("d", 64)) _, err := NewPhase(true, "", "") if err == nil || !strings.Contains(err.Error(), "phase ai") { t.Fatalf("expected clear Agent scope error, got %v", err)