Skip to content

Support Entra ID and other short-lived bearer tokens for enterprise auth #1496

@achandmsft

Description

@achandmsft

Most enterprise customers are required by policy to use short-lived, identity-broker-issued bearer tokens (Microsoft Entra ID, Okta, Ping, Vault) instead of long-lived API keys. API keys are hard to rotate, hard to scope per user, hard to audit, and a frequent source of incidents when they leak into logs, CI configs, or repos.

Today the Anthropic client supports this only partially. auth_token: str | None accepts a bearer token, but the value is captured once at construction. Tokens from these brokers expire (Entra is ~1h), so any long-running process gets 401 Unauthorized and there's no clean way to refresh without subclassing or wiring a custom httpx.Auth.

Accepting a callable would let customers wire their existing identity provider directly:

class Anthropic:
    def __init__(
        self,
        *,
        auth_token: str | Callable[[], str] | None = None,
        ...
    ): ...

If it's a string, behave like today. If it's a callable, call it per request when building the Authorization header.

Why it matters for Anthropic

  • Unblocks enterprise procurement. Many security and compliance reviews flag "long-lived API keys only" as a non-starter. First-class support for federated, short-lived tokens removes a checklist item.
  • Reduces credential-leak blast radius. Tokens that expire in an hour are far less damaging than keys that live until someone notices.
  • Matches what enterprise developers already do with peer SDKs. The openai Python SDK accepts api_key: str | None | Callable[[], str], so this is the pattern customers already standardize on.
  • Small, low-risk change. The SDK already reads self.auth_token via a property on every request, so the wiring is mostly in place.

Workaround in use today

from typing import Callable
from anthropic import Anthropic

class AnthropicWithTokenProvider(Anthropic):
    def __init__(self, *, auth_token_provider: Callable[[], str], **kwargs):
        self._auth_token_provider = auth_token_provider
        super().__init__(auth_token="placeholder", **kwargs)

    @property
    def auth_token(self) -> str:
        return self._auth_token_provider()

    @auth_token.setter
    def auth_token(self, _value): pass

That's 12 lines every customer has to copy. Worth pulling into the SDK.

Repro

Working example: https://github.com/Azure-Samples/claude (see src/hello_claude_token_refresh.py).

Versions

  • anthropic 0.97.0
  • Python 3.11

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions