From e37146d3a15f5aeb22942a588dce4d76c38c70f0 Mon Sep 17 00:00:00 2001 From: jarvis Date: Mon, 31 Aug 2026 00:21:58 -0400 Subject: [PATCH 1/2] feat(auth): propagate runtime session identity --- internal/agent/auth_hook.go | 12 +++++-- internal/agent/credential_source.go | 6 ++-- internal/agent/credential_source_test.go | 33 +++++++++++++++++++ internal/agent/jwt.go | 15 +++++++-- internal/agent/jwt_test.go | 7 +++++ internal/agent/runtime.go | 40 +++++++++++++----------- internal/agent/runtime_test.go | 39 +++++++++++++---------- internal/agent/state.go | 1 + 8 files changed, 113 insertions(+), 40 deletions(-) diff --git a/internal/agent/auth_hook.go b/internal/agent/auth_hook.go index ffe7894..82b994a 100644 --- a/internal/agent/auth_hook.go +++ b/internal/agent/auth_hook.go @@ -201,7 +201,8 @@ func usableProtocolCredential( return dpopCredential{}, errors.New("Realmroot Agent protocol OAuth credential is unavailable") } protocol := *state.ProtocolCredential - if protocol.AccessToken != "" && protocol.ExpiresAt != nil && time.Now().Add(5*time.Second).Before(*protocol.ExpiresAt) { + if protocol.AccessToken != "" && protocol.ExpiresAt != nil && time.Now().Add(5*time.Second).Before(*protocol.ExpiresAt) && + credentialMatchesAgentSession(state, protocol) { return protocol, nil } if len(protocol.Scopes) == 0 { @@ -429,7 +430,8 @@ func ensureProtocolCredential( } if credential.AccessToken == "" || credential.ExpiresAt == nil || !time.Now().Add(5*time.Second).Before(*credential.ExpiresAt) || - !scopesContain(credential.Scopes, requiredScopes) { + !scopesContain(credential.Scopes, requiredScopes) || + !credentialMatchesAgentSession(state, *credential) { requestedScopes := retainedBootstrapScopes(credential.Scopes, requiredScopes, configuration.AgentBootstrapScopes) updated, err := requestProtocolToken(ctx, client, state, *credential, configuration, requestedScopes) if err != nil { @@ -492,9 +494,15 @@ func requestProtocolToken( expiresAt := time.Now().Add(time.Duration(response.ExpiresIn) * time.Second) credential.ExpiresAt = &expiresAt credential.Scopes = append([]string(nil), requiredScopes...) + credential.RuntimeSessionID, _ = agentSession(state.Runtime) return credential, nil } +func credentialMatchesAgentSession(state agentState, credential dpopCredential) bool { + sessionID, _ := agentSession(state.Runtime) + return credential.RuntimeSessionID == sessionID +} + func sameOrigin(value string, origin string) bool { endpoint, err := url.Parse(value) if err != nil || endpoint.Scheme == "" || endpoint.Host == "" { diff --git a/internal/agent/credential_source.go b/internal/agent/credential_source.go index 900e29f..c838063 100644 --- a/internal/agent/credential_source.go +++ b/internal/agent/credential_source.go @@ -306,7 +306,8 @@ func ensureInternalProtocolCredential( } if credential.AccessToken == "" || credential.ExpiresAt == nil || !time.Now().Add(5*time.Second).Before(*credential.ExpiresAt) || - !scopesContain(credential.Scopes, requiredScopes) { + !scopesContain(credential.Scopes, requiredScopes) || + !credentialMatchesAgentSession(reference.state, *credential) { requestedScopes := retainedBootstrapScopes(credential.Scopes, requiredScopes, configuration.AgentBootstrapScopes) updated, err := requestProtocolToken( ctx, client, reference.state, *credential, configuration, requestedScopes, @@ -452,7 +453,8 @@ func reusableInternalProtocolCredential(state agentState, requiredScopes []strin } credential := *state.ProtocolCredential return credential, credential.AccessToken != "" && credential.ExpiresAt != nil && - time.Now().Add(5*time.Second).Before(*credential.ExpiresAt) && scopesContain(credential.Scopes, requiredScopes) + time.Now().Add(5*time.Second).Before(*credential.ExpiresAt) && scopesContain(credential.Scopes, requiredScopes) && + credentialMatchesAgentSession(state, credential) } func realmrootDPoPNonceChallenge(err error) (*dpopNonceChallenge, bool) { diff --git a/internal/agent/credential_source_test.go b/internal/agent/credential_source_test.go index e5d5c9e..dba6749 100644 --- a/internal/agent/credential_source_test.go +++ b/internal/agent/credential_source_test.go @@ -155,6 +155,39 @@ func TestInternalProtocolCredentialRetainsPreviouslyIssuedBootstrapScopes(t *tes } } +func TestInternalProtocolCredentialRefreshesWhenRuntimeSessionChanges(t *testing.T) { + t.Setenv("CODEX_THREAD_ID", "session-new") + offer := testCredential(t, "", time.Time{}) + states := newCredentialState(t, offer) + states.state.Runtime = "codex" + states.state.ProtocolCredential.RuntimeSessionID = "session-old" + tokenRequests := 0 + client := roundTripFunc(func(request *http.Request) (*http.Response, error) { + tokenRequests++ + return jsonResponse(http.StatusOK, map[string]any{ + "access_token": "session-token", "token_type": "DPoP", "expires_in": 300, + }), nil + }) + configuration := agentConfiguration{ + AgentTokenEndpoint: "https://auth.example.com/api/auth/oauth2/token", + AgentBootstrapScopes: []string{"agent:read", "access-requests:read", "access-requests:write"}, + } + reference := credentialSourceStateReference{ + path: "memory", state: states.state, reference: testCredentialSourceReference, + source: states.state.CredentialSources[testCredentialSourceReference], + } + + credential, err := ensureInternalProtocolCredential( + context.Background(), client, states, reference, configuration, []string{"agent:read"}, + ) + if err != nil { + t.Fatal(err) + } + if tokenRequests != 1 || credential.RuntimeSessionID != "session-new" || credential.AccessToken != "session-token" { + t.Fatalf("token requests = %d, credential = %#v", tokenRequests, credential) + } +} + func TestCredentialSourceDescribesStoredOfferWithoutCredentialMaterial(t *testing.T) { offer := testCredential(t, "", time.Time{}) states := newCredentialState(t, offer) diff --git a/internal/agent/jwt.go b/internal/agent/jwt.go index 4bb3fc9..dd83696 100644 --- a/internal/agent/jwt.go +++ b/internal/agent/jwt.go @@ -12,6 +12,8 @@ import ( "time" ) +const realmrootAgentBindingClaim = "urn:realmroot:params:agent:binding" + func newSigningKey(prefix string) (ed25519.PublicKey, ed25519.PrivateKey, string, error) { publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { @@ -54,11 +56,20 @@ func signAgentJWT(state agentState, issuer string, now time.Time) (string, error if err != nil { return "", err } - return signJWT(privateKey, state.AgentKeyID, "agent+jwt", map[string]any{ + claims := map[string]any{ "iss": state.HostID, "sub": state.AgentID, "aud": issuer, - }, now) + } + if sessionID, ok := agentSession(state.Runtime); ok { + claims[realmrootAgentBindingClaim] = map[string]string{ + "protocol_agent_id": state.AgentID, + "host_id": state.HostID, + "runtime": state.Runtime, + "session_id": sessionID, + } + } + return signJWT(privateKey, state.AgentKeyID, "agent+jwt", claims, now) } func signHostJWT(state hostState, issuer string, now time.Time) (string, error) { diff --git a/internal/agent/jwt_test.go b/internal/agent/jwt_test.go index 08e3560..815574d 100644 --- a/internal/agent/jwt_test.go +++ b/internal/agent/jwt_test.go @@ -10,6 +10,7 @@ import ( ) func TestSignAgentJWTProducesVerifiablePossessionProof(t *testing.T) { + t.Setenv("CODEX_THREAD_ID", "thread-raw-123") publicKey, privateKey, keyID, err := newSigningKey("agent") if err != nil { t.Fatal(err) @@ -21,6 +22,7 @@ func TestSignAgentJWTProducesVerifiablePossessionProof(t *testing.T) { HostID: "host-123", AgentKeyID: keyID, AgentPrivateKey: encodePrivateKey(privateKey), + Runtime: "codex", } token, err := signAgentJWT(state, "https://auth.example.com/api/auth", now) @@ -52,6 +54,11 @@ func TestSignAgentJWTProducesVerifiablePossessionProof(t *testing.T) { if claims["aud"] != "https://auth.example.com/api/auth" { t.Fatalf("unexpected JWT audience: %#v", claims["aud"]) } + if binding, ok := claims[realmrootAgentBindingClaim].(map[string]any); !ok || + binding["protocol_agent_id"] != "agent-123" || binding["host_id"] != "host-123" || + binding["runtime"] != "codex" || binding["session_id"] != "thread-raw-123" { + t.Fatalf("unexpected Agent runtime session binding: %#v", claims[realmrootAgentBindingClaim]) + } if claims["iat"] != float64(now.Unix()) || claims["exp"] != float64(now.Add(2*time.Minute).Unix()) { t.Fatalf("unexpected JWT lifetime: %#v", claims) } diff --git a/internal/agent/runtime.go b/internal/agent/runtime.go index ad2f8f7..bbb04b9 100644 --- a/internal/agent/runtime.go +++ b/internal/agent/runtime.go @@ -1,8 +1,6 @@ package agent import ( - "crypto/sha256" - "encoding/hex" "errors" "os" "strings" @@ -13,10 +11,13 @@ const ( defaultAgentRuntimeDisplayName = "Realmroot Toolbox" ) -var sessionEnvironmentNames = []string{ - "AGENT_SESSION_ID", - "CODEX_THREAD_ID", - "HERMES_SESSION_KEY", +var sessionEnvironmentNames = map[string][]string{ + "claude": {"CLAUDE_CODE_SESSION_ID"}, + "codex": {"CODEX_THREAD_ID"}, + "copilot": {"COPILOT_AGENT_SESSION_ID"}, + "goose": {"AGENT_SESSION_ID"}, + "hermes": {"HERMES_SESSION_ID", "HERMES_SESSION_KEY"}, + "pi": {"PI_SESSION_ID"}, } type environmentLookup func(string) (string, bool) @@ -34,30 +35,33 @@ var runtimeDetectors = []runtimeDetector{ {name: "qwen", displayName: "Qwen", matches: hasEnvironment("QWEN_CODE")}, {name: "cursor", displayName: "Cursor", matches: hasEnvironment("CURSOR_AGENT")}, {name: "kiro", displayName: "Kiro", matches: hasEnvironments("AGENT_DISPLAY_OUT", "AGENT_CONTEXT_OUT")}, - {name: "pi", displayName: "Pi", matches: hasEnvironment("PI_CODING_AGENT")}, - {name: "codex", displayName: "Codex", matches: hasEnvironment("CODEX_CI")}, - {name: "copilot", displayName: "Copilot", matches: hasEnvironment("COPILOT_CLI")}, + {name: "pi", displayName: "Pi", matches: hasAnyEnvironment("PI_CODING_AGENT", "PI_SESSION_ID")}, + {name: "codex", displayName: "Codex", matches: hasAnyEnvironment("CODEX_CI", "CODEX_THREAD_ID")}, + {name: "copilot", displayName: "Copilot", matches: hasAnyEnvironment("COPILOT_CLI", "COPILOT_AGENT_SESSION_ID")}, {name: "gemini", displayName: "Gemini", matches: hasEnvironment("GEMINI_CLI")}, - {name: "claude", displayName: "Claude", matches: hasEnvironment("CLAUDECODE")}, - {name: "hermes", displayName: "Hermes", matches: hasAnyEnvironment("HERMES_INTERACTIVE", "HERMES_SESSION_KEY")}, + {name: "claude", displayName: "Claude", matches: hasAnyEnvironment("CLAUDECODE", "CLAUDE_CODE_SESSION_ID")}, + {name: "hermes", displayName: "Hermes", matches: hasAnyEnvironment("HERMES_INTERACTIVE", "HERMES_SESSION_ID", "HERMES_SESSION_KEY")}, } func agentRuntime() (string, error) { return detectAgentRuntime(os.LookupEnv) } -func agentSession() string { - return detectAgentSession(os.LookupEnv) +func agentSession(runtime string) (string, bool) { + return detectAgentSession(runtime, os.LookupEnv) } -func detectAgentSession(lookup environmentLookup) string { - for _, name := range sessionEnvironmentNames { +func detectAgentSession(runtime string, lookup environmentLookup) (string, bool) { + names := sessionEnvironmentNames[runtime] + if len(names) == 0 { + names = []string{"AGENT_SESSION_ID"} + } + for _, name := range names { if value, ok := lookup(name); ok && strings.TrimSpace(value) != "" { - digest := sha256.Sum256([]byte(name + "\x00" + value)) - return strings.ToLower(name) + ":" + hex.EncodeToString(digest[:16]) + return value, true } } - return "default" + return "", false } func detectAgentRuntime(lookup environmentLookup) (string, error) { diff --git a/internal/agent/runtime_test.go b/internal/agent/runtime_test.go index 5c54bd4..bd28e1c 100644 --- a/internal/agent/runtime_test.go +++ b/internal/agent/runtime_test.go @@ -1,7 +1,6 @@ package agent import ( - "strings" "testing" ) @@ -30,11 +29,11 @@ func TestDetectAgentRuntimeRecognizesAgentTools(t *testing.T) { {environment: map[string]string{"CURSOR_AGENT": ""}, expected: "cursor"}, {environment: map[string]string{"AGENT_DISPLAY_OUT": "", "AGENT_CONTEXT_OUT": ""}, expected: "kiro"}, {environment: map[string]string{"PI_CODING_AGENT": ""}, expected: "pi"}, - {environment: map[string]string{"CODEX_CI": ""}, expected: "codex"}, - {environment: map[string]string{"COPILOT_CLI": ""}, expected: "copilot"}, + {environment: map[string]string{"CODEX_THREAD_ID": "thread-1"}, expected: "codex"}, + {environment: map[string]string{"COPILOT_AGENT_SESSION_ID": "session-1"}, expected: "copilot"}, {environment: map[string]string{"GEMINI_CLI": ""}, expected: "gemini"}, - {environment: map[string]string{"CLAUDECODE": ""}, expected: "claude"}, - {environment: map[string]string{"HERMES_SESSION_KEY": ""}, expected: "hermes"}, + {environment: map[string]string{"CLAUDE_CODE_SESSION_ID": "session-2"}, expected: "claude"}, + {environment: map[string]string{"HERMES_SESSION_ID": "session-3"}, expected: "hermes"}, } { runtime, err := detectAgentRuntime(testEnvironment(test.environment)) if err != nil { @@ -84,18 +83,26 @@ func TestNormalizeDeviceDisplayNameRejectsMissingDeviceName(t *testing.T) { } } -func TestDetectAgentSessionIsolatesConcurrentSessionsWithoutPersistingRawIdentifiers(t *testing.T) { - first := detectAgentSession(testEnvironment(map[string]string{"CODEX_THREAD_ID": "thread-secret-1"})) - again := detectAgentSession(testEnvironment(map[string]string{"CODEX_THREAD_ID": "thread-secret-1"})) - second := detectAgentSession(testEnvironment(map[string]string{"CODEX_THREAD_ID": "thread-secret-2"})) - if first != again || first == second { - t.Fatalf("session keys are not stable and isolated: %q %q %q", first, again, second) - } - if strings.Contains(first, "thread-secret") { - t.Fatalf("session key contains the raw external identifier: %q", first) +func TestDetectAgentSessionReturnsRawRuntimeIdentifier(t *testing.T) { + for _, test := range []struct { + runtime string + environment map[string]string + expected string + }{ + {runtime: "codex", environment: map[string]string{"CODEX_THREAD_ID": "thread-1"}, expected: "thread-1"}, + {runtime: "claude", environment: map[string]string{"CLAUDE_CODE_SESSION_ID": "session-2"}, expected: "session-2"}, + {runtime: "copilot", environment: map[string]string{"COPILOT_AGENT_SESSION_ID": "session-3"}, expected: "session-3"}, + {runtime: "goose", environment: map[string]string{"AGENT_SESSION_ID": "session-4"}, expected: "session-4"}, + {runtime: "hermes", environment: map[string]string{"HERMES_SESSION_ID": "session-5"}, expected: "session-5"}, + {runtime: "pi", environment: map[string]string{"PI_SESSION_ID": "session-6"}, expected: "session-6"}, + } { + sessionID, ok := detectAgentSession(test.runtime, testEnvironment(test.environment)) + if !ok || sessionID != test.expected { + t.Fatalf("runtime %q session = %q, %v", test.runtime, sessionID, ok) + } } - if fallback := detectAgentSession(testEnvironment(nil)); fallback != "default" { - t.Fatalf("fallback session = %q", fallback) + if sessionID, ok := detectAgentSession("codex", testEnvironment(nil)); ok || sessionID != "" { + t.Fatalf("missing session = %q, %v", sessionID, ok) } } diff --git a/internal/agent/state.go b/internal/agent/state.go index 94a4461..9b77e98 100644 --- a/internal/agent/state.go +++ b/internal/agent/state.go @@ -55,6 +55,7 @@ type dpopCredential struct { AccessToken string `json:"access_token,omitempty"` ExpiresAt *time.Time `json:"expires_at,omitempty"` Scopes []string `json:"scopes,omitempty"` + RuntimeSessionID string `json:"runtime_session_id,omitempty"` } type credentialSource struct { From 08027cd2c6370feb18656266bbf40c4170d69f80 Mon Sep 17 00:00:00 2001 From: jarvis Date: Mon, 31 Aug 2026 00:35:19 -0400 Subject: [PATCH 2/2] fix(auth): isolate Resource tokens by session --- internal/agent/runtime.go | 15 ++++++++++ internal/agent/runtime_test.go | 18 ++++++++++++ internal/cli/cli_test.go | 43 ++++++++++++++++++++++++++--- internal/cli/operation_authority.go | 11 +++++++- 4 files changed, 82 insertions(+), 5 deletions(-) diff --git a/internal/agent/runtime.go b/internal/agent/runtime.go index bbb04b9..b9fca86 100644 --- a/internal/agent/runtime.go +++ b/internal/agent/runtime.go @@ -1,6 +1,8 @@ package agent import ( + "crypto/sha256" + "encoding/hex" "errors" "os" "strings" @@ -51,6 +53,19 @@ func agentSession(runtime string) (string, bool) { return detectAgentSession(runtime, os.LookupEnv) } +func AgentSessionCacheKey() (string, error) { + runtime, err := agentRuntime() + if err != nil { + return "", err + } + sessionID, ok := agentSession(runtime) + if !ok { + return runtime + "-none", nil + } + digest := sha256.Sum256([]byte(runtime + "\x00" + sessionID)) + return runtime + "-" + hex.EncodeToString(digest[:16]), nil +} + func detectAgentSession(runtime string, lookup environmentLookup) (string, bool) { names := sessionEnvironmentNames[runtime] if len(names) == 0 { diff --git a/internal/agent/runtime_test.go b/internal/agent/runtime_test.go index bd28e1c..b2e3650 100644 --- a/internal/agent/runtime_test.go +++ b/internal/agent/runtime_test.go @@ -1,6 +1,7 @@ package agent import ( + "strings" "testing" ) @@ -106,6 +107,23 @@ func TestDetectAgentSessionReturnsRawRuntimeIdentifier(t *testing.T) { } } +func TestAgentSessionCacheKeySeparatesRawSessionIdentifiers(t *testing.T) { + t.Setenv("AGENT", "codex") + t.Setenv("CODEX_THREAD_ID", "thread-secret-1") + first, err := AgentSessionCacheKey() + if err != nil { + t.Fatal(err) + } + t.Setenv("CODEX_THREAD_ID", "thread-secret-2") + second, err := AgentSessionCacheKey() + if err != nil { + t.Fatal(err) + } + if first == second || strings.Contains(first, "thread-secret") || strings.Contains(second, "thread-secret") { + t.Fatalf("session cache keys = %q, %q", first, second) + } +} + func testEnvironment(values map[string]string) environmentLookup { return func(name string) (string, bool) { value, ok := values[name] diff --git a/internal/cli/cli_test.go b/internal/cli/cli_test.go index b3dc1ba..c4b248c 100644 --- a/internal/cli/cli_test.go +++ b/internal/cli/cli_test.go @@ -398,7 +398,11 @@ func TestPrepareOperationCredentialsBindsOpenAPICredentialID(t *testing.T) { } profile := config.APIs["github"].Profiles["default"] credential := profile.Credentials["realmrootOidc"] - if profile.Auth != nil || credential == nil || credential.Auth == nil || credential.Auth.Type != "dpop" { + if credential == nil { + t.Fatalf("profile = %#v", profile) + } + auth := config.AuthProfiles[credential.AuthRef] + if profile.Auth != nil || credential.Auth != nil || auth == nil || auth.Type != "dpop" { t.Fatalf("profile = %#v", profile) } if strings.Join(credential.Satisfies, " ") != "issues:read" { @@ -418,7 +422,11 @@ func TestPrepareOperationCredentialsReplacesStaleProfileReference(t *testing.T) t.Fatal(err) } credential := config.APIs["github"].Profiles["default"].Credentials["realmrootOidc"] - if credential == nil || credential.Auth == nil || credential.Auth.Params["reference"] != "selected-reference" { + if credential == nil { + t.Fatal("credential was not bound") + } + auth := config.AuthProfiles[credential.AuthRef] + if credential.Auth != nil || auth == nil || auth.Params["reference"] != "selected-reference" { t.Fatalf("credential = %#v", credential) } } @@ -514,11 +522,34 @@ func TestBindProfileCredentialsSupportsGenericHTTPRequests(t *testing.T) { t.Fatal(err) } credential := config.APIs["platform"].Profiles["default"].Credentials["realmrootOidc"] - if credential == nil || credential.Auth == nil || credential.Auth.Type != "dpop" || credential.Auth.Params["reference"] != binding.Reference || strings.Join(credential.Satisfies, " ") != "resource-servers:read" { + if credential == nil { + t.Fatal("credential was not bound") + } + auth := config.AuthProfiles[credential.AuthRef] + if credential.Auth != nil || auth == nil || auth.Type != "dpop" || auth.Params["reference"] != binding.Reference || strings.Join(credential.Satisfies, " ") != "resource-servers:read" { t.Fatalf("credential = %#v", credential) } } +func TestBindProfileCredentialsSeparatesRestishTokensByAgentSession(t *testing.T) { + t.Setenv("AGENT", "codex") + binding := agent.CredentialBinding{Reference: "selected-reference", Scopes: []string{"issues:read"}} + bind := func(sessionID string) string { + t.Helper() + t.Setenv("CODEX_THREAD_ID", sessionID) + config := &restish.Config{APIs: map[string]*restish.APIConfig{"github": {}}} + if err := bindProfileCredentials(config, catalog.ResourceServer{CommandName: "github"}, githubOperationInspection(), "default", binding); err != nil { + t.Fatal(err) + } + return config.APIs["github"].Profiles["default"].Credentials["realmrootOidc"].AuthRef + } + first := bind("thread-secret-1") + second := bind("thread-secret-2") + if first == second || strings.Contains(first, "thread-secret") || strings.Contains(second, "thread-secret") { + t.Fatalf("session auth references = %q, %q", first, second) + } +} + func TestOperationScopeAlternativesPreserveOAuthAlternatives(t *testing.T) { operation := githubOperationInspection().Operations[0] if got := operationCredentialScopeAlternatives(operation); len(got) != 2 || strings.Join(got[0], " ") != "issues:read" || strings.Join(got[1], " ") != "metadata:read" { @@ -547,7 +578,11 @@ func TestOperationAuthoritySupportsStandardOpenIDKind(t *testing.T) { t.Fatal(err) } credential := config.APIs["ama"].Profiles["default"].Credentials["realmrootOidc"] - if credential == nil || credential.Auth == nil || credential.Auth.Type != "dpop" { + if credential == nil { + t.Fatal("credential was not bound") + } + auth := config.AuthProfiles[credential.AuthRef] + if credential.Auth != nil || auth == nil || auth.Type != "dpop" { t.Fatalf("credential = %#v", credential) } } diff --git a/internal/cli/operation_authority.go b/internal/cli/operation_authority.go index c5243ca..81d046e 100644 --- a/internal/cli/operation_authority.go +++ b/internal/cli/operation_authority.go @@ -287,6 +287,15 @@ func bindProfileCredentials( auth := &restish.AuthConfig{Type: "dpop", Params: map[string]string{ "source": "realmroot", "reference": binding.Reference, }} + sessionCacheKey, err := agent.AgentSessionCacheKey() + if err != nil { + return err + } + authReference := "realmroot-session-" + sessionCacheKey + if config.AuthProfiles == nil { + config.AuthProfiles = map[string]*restish.AuthConfig{} + } + config.AuthProfiles[authReference] = auth profile.Auth = nil if profile.Credentials == nil { profile.Credentials = map[string]*restishconfig.CredentialConfig{} @@ -298,7 +307,7 @@ func bindProfileCredentials( continue } profile.Credentials[requirement.ID] = &restishconfig.CredentialConfig{ - Auth: auth, Satisfies: append([]string(nil), binding.Scopes...), + AuthRef: authReference, Satisfies: append([]string(nil), binding.Scopes...), } } }