이 가이드에서는 Azure ID SDK API를 사용하여 GitHub Copilot SDK를 통해 Microsoft Foundry 모델로 인증하는 방법을 보여 줍니다. 대부분의 언어는 DefaultAzureCredential를 사용합니다. Rust는 로컬에서는 DeveloperToolsCredential를, Azure에서는 ManagedIdentityCredential를 사용합니다.
작동 방식
Microsoft Foundry의 OpenAI 호환 엔드포인트(https://<resource-name>.openai.azure.com/openai/v1/)는 정적 API 키 대신 Microsoft Entra ID 전달자 토큰을 허용합니다. 이 가이드에서는 토큰 공급자 콜백을 사용하므로 GitHub Copilot SDK 런타임에서 요청 시 새 토큰을 요청할 수 있습니다.
Python 예로 사용하는 흐름은 다음과 같습니다.
- 사용자 환경에 맞게
DefaultAzureCredential구성합니다. DefaultAzureCredential를 사용하여https://ai.azure.com/.default범위에 대한 토큰을 가져오는 콜백을 BYOK 프로바이더 구성의bearer_token_provider에 전달합니다.- GitHub Copilot SDK가 해당 콜백을 통해 요청 시 새 토큰을 요청하도록 합니다.

코드 샘플
사전 요구 사항
언어에 대한 Azure ID 및 GitHub Copilot SDK 패키지를 설치합니다.
dotnet add package GitHub.Copilot.SDK
dotnet add package Azure.Core
go get github.com/github/copilot-sdk/go
go get github.com/Azure/azure-sdk-for-go/sdk/azidentity
<dependency>
<groupId>com.github</groupId>
<artifactId>copilot-sdk-java</artifactId>
<version>${copilot.sdk.version}</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>${azure.identity.version}</version>
</dependency>
pip install github-copilot-sdk azure-identity
cargo add github-copilot-sdk azure_identity azure_core
cargo add tokio --features macros,rt-multi-thread
npm install @github/copilot-sdk @azure/identity
토큰 공급자 콜백 사용
GitHub Copilot SDK 런타임에서 제공하는 콜백을 통해 요청 시 새 토큰을 요청하려는 경우 이 방법을 사용합니다. Azure ID SDK는 토큰 캐싱 및 새로 고침 타이밍을 처리합니다.
언어별 구현은 다음과 같습니다.
using Azure.Core;
using Azure.Identity;
using GitHub.Copilot;
DefaultAzureCredential credential = new(
DefaultAzureCredential.DefaultEnvironmentVariableName);
await using CopilotClient client = new();
string foundryUrl = Environment.GetEnvironmentVariable("FOUNDRY_RESOURCE_URL")!;
await using CopilotSession session = await client.CreateSessionAsync(new SessionConfig
{
Model = "gpt-5.5",
Provider = new ProviderConfig
{
Type = "openai",
BaseUrl = $"{foundryUrl}/openai/v1/",
BearerTokenProvider = async _ =>
{
AccessToken token = await credential.GetTokenAsync(
new TokenRequestContext(["https://ai.azure.com/.default"]));
return token.Token;
},
WireApi = "responses",
},
});
AssistantMessageEvent? response = await session.SendAndWaitAsync(
new MessageOptions { Prompt = "Hello from Managed Identity!" });
Console.WriteLine(response?.Data.Content);
package main
import (
"context"
"fmt"
"log"
"os"
"time"
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
copilot "github.com/github/copilot-sdk/go"
)
func main() {
opts := azidentity.DefaultAzureCredentialOptions{RequireAzureTokenCredentials: true}
credential, err := azidentity.NewDefaultAzureCredential(&opts)
if err != nil {
log.Fatal(err)
}
getBearerToken := func(args copilot.ProviderTokenArgs) (string, error) {
token, err := credential.GetToken(context.Background(), policy.TokenRequestOptions{
Scopes: []string{"https://ai.azure.com/.default"},
})
if err != nil {
return "", err
}
return token.Token, nil
}
client := copilot.NewClient(nil)
if err := client.Start(context.Background()); err != nil {
log.Fatal(err)
}
defer client.Stop()
foundryURL := os.Getenv("FOUNDRY_RESOURCE_URL")
session, err := client.CreateSession(context.Background(), &copilot.SessionConfig{
Model: "gpt-5.5",
Provider: &copilot.ProviderConfig{
Type: "openai",
BaseURL: fmt.Sprintf("%s/openai/v1/", foundryURL),
BearerTokenProvider: getBearerToken,
WireAPI: "responses",
},
})
if err != nil {
log.Fatal(err)
}
defer session.Disconnect()
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
response, err := session.SendAndWait(ctx, copilot.MessageOptions{
Prompt: "Hello from Managed Identity!",
})
if err != nil {
log.Fatal(err)
}
if response != nil {
if data, ok := response.Data.(*copilot.AssistantMessageData); ok {
fmt.Println(data.Content)
}
}
}
import com.azure.core.credential.TokenRequestContext;
import com.azure.identity.AzureIdentityEnvVars;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.github.copilot.CopilotClient;
import com.github.copilot.generated.AssistantMessageEvent;
import com.github.copilot.rpc.BearerTokenProvider;
import com.github.copilot.rpc.MessageOptions;
import com.github.copilot.rpc.ProviderConfig;
import com.github.copilot.rpc.SessionConfig;
public class ManagedIdentityExample {
public static void main(String[] args) throws Exception {
var credential = new DefaultAzureCredentialBuilder()
.requireEnvVars(AzureIdentityEnvVars.AZURE_TOKEN_CREDENTIALS)
.build();
BearerTokenProvider tokenProvider = providerArgs ->
credential
.getToken(new TokenRequestContext().addScopes("https://ai.azure.com/.default"))
.map(accessToken -> accessToken.getToken())
.toFuture();
String foundryUrl = System.getenv("FOUNDRY_RESOURCE_URL");
try (var client = new CopilotClient()) {
client.start().get();
var session = client.createSession(new SessionConfig()
.setModel("gpt-5.5")
.setProvider(new ProviderConfig()
.setType("openai")
.setBaseUrl(foundryUrl + "/openai/v1/")
.setBearerTokenProvider(tokenProvider)
.setWireApi("responses")))
.get();
AssistantMessageEvent response = session
.sendAndWait(new MessageOptions().setPrompt("Hello from Managed Identity!"))
.get();
System.out.println(response.getData().content());
session.disconnect().get();
}
}
}
import asyncio
import os
from azure.identity.aio import DefaultAzureCredential
from copilot import CopilotClient
from copilot.session import PermissionHandler, ProviderConfig
async def main():
credential = DefaultAzureCredential(require_envvar=True)
async def get_bearer_token(_args) -> str:
token = await credential.get_token("https://ai.azure.com/.default")
return token.token
foundry_url = os.environ["FOUNDRY_RESOURCE_URL"]
client = CopilotClient()
await client.start()
session = await client.create_session(
on_permission_request=PermissionHandler.approve_all,
model="gpt-5.5",
provider=ProviderConfig(
type="openai",
base_url=f"{foundry_url.rstrip('/')}/openai/v1/",
bearer_token_provider=get_bearer_token,
wire_api="responses",
),
)
response = await session.send_and_wait("Hello from Managed Identity!")
print(response.data.content)
await client.stop()
await credential.close()
asyncio.run(main())
use std::sync::Arc;
use azure_core::credentials::TokenCredential;
use azure_identity::{DeveloperToolsCredential, ManagedIdentityCredential};
use github_copilot_sdk::{BearerTokenError, Client, ClientOptions, MessageOptions, ProviderTokenArgs};
use github_copilot_sdk::types::{ProviderConfig, SessionConfig};
fn credential_for_environment() -> azure_core::Result<Arc<dyn TokenCredential>> {
match std::env::var("AZURE_TOKEN_CREDENTIALS").as_deref() {
Ok("ManagedIdentityCredential") => Ok(ManagedIdentityCredential::new(None)?),
_ => Ok(DeveloperToolsCredential::new(None)?),
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let credential = credential_for_environment()?;
let foundry_url = std::env::var("FOUNDRY_RESOURCE_URL")?;
let get_bearer_token = {
let credential = credential.clone();
move |_args: ProviderTokenArgs| {
let credential = credential.clone();
async move {
let token = credential
.get_token(&["https://ai.azure.com/.default"], None)
.await
.map_err(|err| BearerTokenError::message(err.to_string()))?;
Ok(token.token.secret().to_string())
}
}
};
let mut provider = ProviderConfig::default();
provider.provider_type = Some("openai".to_string());
provider.base_url = format!("{}/openai/v1/", foundry_url.trim_end_matches('/'));
provider.bearer_token_provider = Some(Arc::new(get_bearer_token));
provider.wire_api = Some("responses".to_string());
let mut config = SessionConfig::default();
config.model = Some("gpt-5.5".to_string());
config.provider = Some(provider);
let client = Client::start(ClientOptions::default()).await?;
let session = client.create_session(config).await?;
session
.send_and_wait(MessageOptions::new("Hello from Managed Identity!"))
.await?;
session.disconnect().await?;
client.stop().await?;
Ok(())
}
import { DefaultAzureCredential } from "@azure/identity";
import { CopilotClient } from "@github/copilot-sdk";
const credential = new DefaultAzureCredential({
requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"],
});
const getBearerToken = async () => {
const tokenResponse = await credential.getToken("https://ai.azure.com/.default");
return tokenResponse.token;
};
const client = new CopilotClient();
const session = await client.createSession({
model: "gpt-5.5",
provider: {
type: "openai",
baseUrl: `${process.env.FOUNDRY_RESOURCE_URL}/openai/v1/`,
bearerTokenProvider: getBearerToken,
wireApi: "responses",
},
});
const response = await session.sendAndWait({ prompt: "Hello from Managed Identity!" });
console.log(response?.data.content);
await client.stop();
환경 구성
| 변수 | Description | Example |
|---|---|---|
AZURE_TOKEN_CREDENTIALS | ||
Azure에서 실행하는 경우 Managed로 설정합니다. | ||
로컬에서 실행할 때는 이를 dev 또는 Azure와 같은 개발자 도구 자격 증명 이름으로 설정하세요. | Managed | |
AZURE_CLIENT_ID | ||
| 선택 사항. | ||
Azure에서 실행하는 경우, Managed를 사용할 때 이 값을 사용자 할당 관리형 ID의 클라이언트 ID로 설정합니다. 설정하지 않으면 Azure 시스템 할당 관리 ID를 사용합니다. | 11111111-2222-3333-4444-555555555555 | |
FOUNDRY_RESOURCE_ | 사용자의 Microsoft Foundry 리소스 URL | https:/ |
API 키 환경 변수가 필요하지 않습니다. 인증은 Azure ID 자격 증명에 의해 처리됩니다. .NET Go, Java, Python 및 TypeScript는 DefaultAzureCredential 자동으로 다음을 지원합니다.
- 관리 ID(시스템 할당 또는 사용자 할당): Azure 호스팅 앱
- Azure CLI(
az login): 로컬 개발용 - 환경 변수 (
AZURE_CLIENT_ID,AZURE_TENANT_ID,AZURE_CLIENT_SECRET): 서비스 주체의 경우 - 워크로드 ID: Kubernetes의 경우
.NET, Go, Java, Python 및 TypeScript에서는 ManagedIdentityCredential가 사용자 할당 관리 ID를 선택하기 위해 AZURE_CLIENT_ID를 읽습니다. Rust는 이 가이드의 예외입니다.
Rust에서는 로컬 개발 시 DeveloperToolsCredential를 사용하고 Azure에서 실행할 때는 ManagedIdentityCredential를 사용합니다. 다른 언어의 경우 전체 자격 증명 체인에 대한 DefaultAzureCredential 설명서를 참조하세요.
이 패턴을 사용하는 경우
| Scenario | Recommendation |
|---|---|
| 관리 ID를 사용하는 Azure 호스팅 앱 | |
| ✅ 이 패턴 사용 | |
| 기존 Microsoft Entra 서비스 주체를 사용하는 앱 | |
| ✅ 이 패턴 사용 | |
az login를 사용한 로컬 개발 | |
| ✅ 이 패턴 사용 | |
| 정적 API 키를 사용하는 비 Azure 환경 | |
| BYOK(사용자 고유의 키 가져오기) 사용 | |
| GitHub Copilot 구독 사용 가능 | |
| GitHub OAuth 설정 사용 |
참고하십시오
- BYOK(사용자 고유의 키 가져오기): 정적 API 키 구성
- 백 엔드 서비스 설정: 서버 쪽 배포