/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ package com.github.copilot.sdk; import static org.junit.jupiter.api.Assertions.*; import java.util.HashMap; import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import com.github.copilot.sdk.generated.rpc.SessionAuthGetStatusResult; import com.github.copilot.sdk.json.CopilotClientOptions; import com.github.copilot.sdk.json.PermissionHandler; import com.github.copilot.sdk.json.SessionConfig; /** * Tests for per-session GitHub authentication. * *

* These tests verify that a per-session GitHub token is resolved into a full * identity by the CLI runtime and that sessions with different tokens are * isolated from each other. *

*/ public class PerSessionAuthTest { private static E2ETestContext ctx; @BeforeAll static void setup() throws Exception { ctx = E2ETestContext.create(); } @AfterAll static void teardown() throws Exception { if (ctx != null) { ctx.close(); } } /** * Creates a CopilotClient with the GitHub API URL redirected to the proxy so * that per-session auth token resolution (fetchCopilotUser) is intercepted. */ private CopilotClient createAuthTestClient() { Map env = new HashMap<>(ctx.getEnvironment()); env.put("COPILOT_DEBUG_GITHUB_API_URL", ctx.getProxyUrl()); return ctx.createClient(new CopilotClientOptions().setEnvironment(env)); } private void setupCopilotUsers() throws Exception { // Initialize proxy state before registering tokens — the proxy requires its // internal state to be initialized (via /config) before it can handle the // /copilot_internal/user endpoint used for per-session auth resolution. ctx.initializeProxy(); ctx.setCopilotUserByToken("token-alice", "alice", "individual_pro", ctx.getProxyUrl(), "https://localhost:1/telemetry", "alice-tracking-id"); ctx.setCopilotUserByToken("token-bob", "bob", "business", ctx.getProxyUrl(), "https://localhost:1/telemetry", "bob-tracking-id"); } @Test void shouldAuthenticateWithGitHubToken() throws Exception { setupCopilotUsers(); try (CopilotClient client = createAuthTestClient()) { CopilotSession session = client.createSession(new SessionConfig().setGitHubToken("token-alice") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); try { SessionAuthGetStatusResult authStatus = session.getRpc().auth.getStatus().get(); assertTrue(authStatus.isAuthenticated(), "Expected session to be authenticated"); assertEquals("alice", authStatus.login()); } finally { session.close(); } } } @Test void shouldIsolateAuthBetweenSessions() throws Exception { setupCopilotUsers(); try (CopilotClient client = createAuthTestClient()) { CopilotSession sessionA = client.createSession(new SessionConfig().setGitHubToken("token-alice") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); CopilotSession sessionB = client.createSession(new SessionConfig().setGitHubToken("token-bob") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); try { SessionAuthGetStatusResult statusA = sessionA.getRpc().auth.getStatus().get(); SessionAuthGetStatusResult statusB = sessionB.getRpc().auth.getStatus().get(); assertTrue(statusA.isAuthenticated(), "Expected session A to be authenticated"); assertEquals("alice", statusA.login()); assertTrue(statusB.isAuthenticated(), "Expected session B to be authenticated"); assertEquals("bob", statusB.login()); } finally { sessionA.close(); sessionB.close(); } } } @Test void shouldBeUnauthenticatedWithoutToken() throws Exception { try (CopilotClient client = createAuthTestClient()) { CopilotSession session = client .createSession(new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); try { SessionAuthGetStatusResult authStatus = session.getRpc().auth.getStatus().get(); // Without a per-session token, there is no per-session identity. // In CI the process-level fake token may still authenticate globally, // so we check login rather than isAuthenticated. assertNull(authStatus.login(), "Expected no login without per-session token"); } finally { session.close(); } } } @Test void shouldFailWithInvalidToken() throws Exception { setupCopilotUsers(); try (CopilotClient client = createAuthTestClient()) { Exception ex = assertThrows(Exception.class, () -> { CopilotSession session = client.createSession(new SessionConfig().setGitHubToken("invalid-token") .setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get(); session.close(); }); assertNotNull(ex); } } }