Skip to content

[FEATURE]: Azure AI Foundry Microsoft Entra (OAuth) authentication #21658

Open

Description

@NoTuxNoBux

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

It appears Microsoft Entra authentication with the Azure AI Foundry is not supported yet (or I can't find anything about it 😉).

A customer of ours deployed Anthropic models in an Azure AI Foundry with a custom base URL and provided instructions only for Anthropic CLI. The existing provider relies on an API key, but the customer expressly does not want to provide one to strengthen security and enforces Entra authentication using the Azure CLI instead.

These are the steps they document (not using OpenCode):

  1. export ANTHROPIC_FOUNDRY_BASE_URL=https://their-custom-domain.azure-api.net/ai/anthropic
  2. export CLAUDE_CODE_USE_FOUNDRY="1"
  3. az login to sign in to Azure using Entra Authentication (OAuth).
  4. claude run

Steps 1, 2 and 4 appear already possible with OpenCode (#4474 (comment)), but for 3 an API key is expected by OpenCode instead.

They also mention the Azure CLI session can expire every so often (10 hours in their case), in which case az logout followed by az login is required - mentioning it as reauthenticating might be relevant for OpenCode also.

Activity

  1. added
    discussionUsed for feature requests, proposals, ideas, etc. Open discussion
    on Apr 9, 2026
  2. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Apr 9, 2026
  3. removed
    discussionUsed for feature requests, proposals, ideas, etc. Open discussion
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on May 3, 2026
  4. OpeOginni commented on Jun 16, 2026

    @OpeOginni
    Contributor

    Got this PR ready to go, adds OAuth through the az cli #31351

  5. github-actions commented on Aug 16, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

  6. mkemmerz commented on Aug 16, 2026

    @mkemmerz

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

    Please reopen - great feature

  7. OpeOginni commented on Aug 28, 2026

    @OpeOginni
    Contributor

    Should work after new release!

  8. NoTuxNoBux commented on Aug 31, 2026

    @NoTuxNoBux
    Author

    I'm testing out the new functionality, but still not entirely sure how to configure it for my scenario in the OP. This is the flow I'm seeing now:

    1. Open OpenCode TUI.
    2. Connect provider.
    3. Select Azure.
    4. Select Entra ID.
    5. It uses the Azure CLI to look for my credentials, that works.
    6. It presents me a list of resource names. I select the Foundry.
    7. I get the model selection, I select a model that I know should work.
    8. I ask a test question and get PermissionDenied without further details.

    The customer I'm working for has a bit of a special setup:

    1. They have a custom Azure API Management service hosted at https://their-custom-domain.azure-api.net/ that seems to forward all calls (so /ai/*) to https://their-actual-foundry-swedencentral-001.cognitiveservices.azure.com (so e.g. Claude CLI uses https://their-custom-domain.azure-api.net/ai/anthropic).
    2. On this API forwarding rule, they have a rule validate-azure-ad-token set.

    In other words they don't seem to support directly using the Foundry URL in question and Entra-authenticated calls need to go through a custom URL/domain instead. Is there any way to configure opencode.jsonc to use the new Entra authentication instead of an API key? The main problem seems to be that I need to be able to enter a fully custom URL (as opposed to just a resource name).

  9. OpeOginni commented on Aug 31, 2026

    @OpeOginni
    Contributor

    @NoTuxNoBux are you making use of the Opencode v2?

  10. NoTuxNoBux commented on Aug 31, 2026

    @NoTuxNoBux
    Author

    Thanks for the response. I'm not sure what you mean by v2, is that a new in-progress version on the main branch? I'm currently on 1.18.25.

  11. OpeOginni commented on Aug 31, 2026

    @OpeOginni
    Contributor

    No v2 is the new version of clone code coming soon, you can download it here try that an see if it works, it's still wip and should be official in some weeks https://opencode.ai/v2/docs

  12. neriousy commented on Sep 1, 2026

    @neriousy
    Member

    Yeah, the custom URL stuff probably needs to be set through config. I'll take a look at it later today!

  13. opencodejr commented on Oct 29, 2026

    @opencodejr

    Thanks for the request and for testing the new Azure CLI method so carefully.

    To restate it: you'd like Microsoft Entra ID (az login) authentication for Claude models on Azure AI Foundry when they sit behind a fully custom gateway URL (your API Management endpoint https://…azure-api.net/ai/anthropic with validate-azure-ad-token), not just a Foundry resource name.

    The Microsoft Entra ID (Azure CLI) method added in #53794 only works for the built-in azure provider. That provider is built around a resource name and uses OpenAI-style APIs. So it can't point Entra auth at a custom gateway or talk to Foundry's Anthropic endpoint, which is why you got PermissionDenied. Maintainers will consider proper support for this.

    In the meantime, a small plugin should cover your setup. It defines a custom provider that uses the Anthropic API with your gateway URL. Before each request, the plugin removes the API key header and adds a bearer token from the Azure CLI. It type-checks against the current plugin API, but I haven't been able to try it against a real gateway.

    .opencode/plugins/azure-entra/index.ts:

    import { execFile } from "node:child_process"
    import { promisify } from "node:util"
    import { Plugin } from "@opencode/plugin"
    
    const run = promisify(execFile)
    
    export default Plugin.define({
      id: "azure-entra-gateway",
      async setup(ctx) {
        const providerID = String(ctx.options.provider ?? "foundry")
        const scope = String(ctx.options.scope ?? "https://cognitiveservices.azure.com/.default")
        let cached: { token: string; expires: number } | undefined
    
        const token = async () => {
          if (cached && cached.expires - 5 * 60_000 > Date.now()) return cached.token
          const { stdout } = await run("az", ["account", "get-access-token", "--scope", scope, "--output", "json"], {
            shell: process.platform === "win32",
          })
          const result = JSON.parse(stdout) as { accessToken: string; expires_on?: number; expiresOn?: string }
          const expires = result.expires_on
            ? result.expires_on * 1000
            : result.expiresOn
              ? Date.parse(result.expiresOn)
              : Date.now() + 30 * 60_000
          cached = { token: result.accessToken, expires }
          return cached.token
        }
    
        await ctx.session.hook(
          "http.request",
          async (event) => {
            if (event.model.providerID !== providerID) return
            event.request.headers.delete("x-api-key")
            event.request.headers.delete("api-key")
            event.request.headers.set("authorization", `Bearer ${await token()}`)
          },
          { providerID },
        )
      },
    })

    opencode.jsonc (change the URL, provider ID and model IDs to match your deployments):

    {
      "$schema": "https://opencode.ai/config.json",
      "plugins": [
        {
          "package": "./.opencode/plugins/azure-entra",
          "options": { "provider": "foundry", "scope": "https://cognitiveservices.azure.com/.default" }
        }
      ],
      "providers": {
        "foundry": {
          "name": "Azure Foundry (Entra)",
          "package": "@opencode/ai/providers/anthropic",
          "settings": {
            "baseURL": "https://their-custom-domain.azure-api.net/ai/anthropic/v1",
            // placeholder only: the plugin removes it and sends the Entra token instead
            "apiKey": "entra"
          },
          "models": {
            "claude-sonnet-4-5": { "name": "Claude Sonnet 4.5" }
          }
        }
      }
    }

    Some notes:

    • Run az login first. The plugin caches the token and gets a new one from the Azure CLI shortly before it expires. If your Azure CLI session itself expires (your 10 hours), run az login again; you don't need to restart OpenCode.
    • If the gateway rejects the token, check which audience the validate-azure-ad-token policy expects and set scope to match (for example api://<app-id>/.default).
    • Whether baseURL needs the trailing /v1 depends on how the gateway forwards requests: Claude Code takes the URL without it, while OpenCode's Anthropic package may expect it. If you get 404s, try the other form.

    Related:

  14. NoTuxNoBux commented on Oct 30, 2026

    @NoTuxNoBux
    Author

    Just for the record: the above workaround seems to work for me.

    .opencode/plugins/azure-entra/index.ts had to be ~/.config/opencode/.opencode/plugins/azure-entra/index.ts instead, but apart from that the example worked as-is for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions