Azure Managed Identity with BYOK

February 27, 2026 · View on GitHub

The Copilot SDK's BYOK mode accepts static API keys, but Azure deployments often use Managed Identity (Entra ID) instead of long-lived keys. Since the SDK does not natively support Entra ID authentication, you can obtain a short-lived bearer token and pass it via the :bearer-token provider config field.

This guide shows how to use DefaultAzureCredential from the Azure Identity SDK to authenticate with Azure AI Foundry models through the Copilot SDK.

How It Works

Azure AI Foundry's OpenAI-compatible endpoint accepts bearer tokens from Entra ID in place of static API keys. The pattern is:

  1. Use DefaultAzureCredential to obtain a token for the https://cognitiveservices.azure.com/.default scope
  2. Pass the token as :bearer-token in the BYOK provider config
  3. Refresh the token before it expires (tokens are typically valid for ~1 hour)

Clojure Example

Prerequisites

Add the Azure Identity SDK to your deps.edn:

;; deps.edn
{:deps {com.azure/azure-identity {:mvn/version "1.15.4"}
        io.github.copilot-community-sdk/copilot-sdk-clojure {:mvn/version "LATEST"}}}

Basic Usage

(require '[github.copilot-sdk :as copilot])
(require '[github.copilot-sdk.helpers :as h])

(import '[com.azure.identity DefaultAzureCredentialBuilder]
        '[com.azure.core.credential TokenRequestContext])

(def cognitive-services-scope "https://cognitiveservices.azure.com/.default")

(defn get-azure-token
  "Obtain a short-lived bearer token string from Entra ID."
  []
  (let [credential (.build (DefaultAzureCredentialBuilder.))
        context    (doto (TokenRequestContext.)
                     (.addScopes (into-array String [cognitive-services-scope])))]
    (-> (.getToken credential context)
        (.block)
        (.getToken))))

(def foundry-url (System/getenv "AZURE_AI_FOUNDRY_RESOURCE_URL"))

(copilot/with-client-session [session
                              {:model "gpt-4.1"
                               :provider {:provider-type :openai
                                          :base-url (str foundry-url "/openai/v1/")
                                          :bearer-token (get-azure-token)
                                          :wire-api :responses}}]
  (println (h/query "Hello from Managed Identity!" :session session)))

Token Refresh for Long-Running Applications

Bearer tokens expire (typically after ~1 hour). For servers or long-running agents, refresh the token before creating each session:

(require '[github.copilot-sdk :as copilot])
(require '[github.copilot-sdk.helpers :as h])

(import '[com.azure.identity DefaultAzureCredentialBuilder]
        '[com.azure.core.credential TokenRequestContext])

(def cognitive-services-scope "https://cognitiveservices.azure.com/.default")
(def credential (.build (DefaultAzureCredentialBuilder.)))
(def context (doto (TokenRequestContext.)
               (.addScopes (into-array String [cognitive-services-scope]))))

(defn fresh-provider-config
  "Build a provider config with a freshly obtained bearer token."
  [foundry-url]
  (let [token (-> (.getToken credential context)
                  (.block)
                  (.getToken))]
    {:provider-type :openai
     :base-url (str foundry-url "/openai/v1/")
     :bearer-token token
     :wire-api :responses}))

(def foundry-url (System/getenv "AZURE_AI_FOUNDRY_RESOURCE_URL"))

;; Each session gets a fresh token
(copilot/with-client [client {}]
  (dotimes [_ 3]
    (copilot/with-session [session client
                           {:model "gpt-4.1"
                            :provider (fresh-provider-config foundry-url)}]
      (println (h/query "Hello!" :session session)))))

Environment Configuration

VariableDescriptionExample
AZURE_AI_FOUNDRY_RESOURCE_URLYour Azure AI Foundry resource URLhttps://myresource.openai.azure.com

No API key environment variable is needed — authentication is handled by DefaultAzureCredential, which automatically supports:

  • Managed Identity (system-assigned or user-assigned) — for Azure-hosted apps
  • Azure CLI (az login) — for local development
  • Environment variables (AZURE_CLIENT_ID, AZURE_TENANT_ID, AZURE_CLIENT_SECRET) — for service principals
  • Workload Identity — for Kubernetes

See the DefaultAzureCredential documentation for the full credential chain.

When to Use This Pattern

ScenarioRecommendation
Azure-hosted app with Managed Identity✅ Use this pattern
App with existing Azure AD service principal✅ Use this pattern
Local development with az login✅ Use this pattern
Non-Azure environment with static API keyUse standard BYOK
GitHub Copilot subscription availableUse GitHub auth

See Also