diff --git a/src/internal/agentapi/client_test.go b/src/internal/agentapi/client_test.go index c41273d1..b973a917 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)) } @@ -54,7 +51,8 @@ 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), + testToken("pss_agent", 2, ""), "pss_user:v1:not-hex", } { if _, _, err := ParseDurableToken(invalid); err == nil { diff --git a/src/internal/agentsdk/client_test.go b/src/internal/agentsdk/client_test.go index 7b3c3a17..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:v1:" + 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:v1:" + lookup}, + {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, ":")}, } @@ -616,8 +616,8 @@ func TestAuthenticationFromPhaseToken(t *testing.T) { if _, err := AuthenticationFromPhaseToken("pss_agent:v2:" + strings.Join(fullParts, ":")); 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: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 { t.Fatal("expected unsupported PAT version error") diff --git a/src/internal/agentsdk/types.go b/src/internal/agentsdk/types.go index 841fc7f6..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. 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; 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 { @@ -399,7 +399,7 @@ func AuthenticationFromPhaseToken(token string) (Authentication, error) { if parts[1] != "v1" { 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..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:v1:" + strings.Repeat("a", 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 f9132f3f..e5e7533a 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: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 5f15788d..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:v1:" + strings.Repeat("a", 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:v1:"+strings.Repeat("a", 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)