Skip to content

ex-azure/ex_azure_identity

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

ExAzureIdentity

Azure Active Directory authentication library for Elixir using Workload Identity Federation.

This library enables passwordless authentication to Azure services by exchanging external identity tokens (from GitHub Actions, AWS, Kubernetes, etc.) for Azure AD access tokens.

Features

  • πŸ” Passwordless Authentication - No secrets or certificates to manage
  • πŸ€– Auto-detection - Automatically detects the runtime environment
  • πŸš€ Multiple Providers - GitHub Actions, Kubernetes, AWS, and custom providers
  • ⚑ Token Caching - Built-in caching with automatic refresh
  • 🌍 Multi-cloud - Supports Azure Public, Government, China, and Germany clouds

Installation

Add ex_azure_identity to your list of dependencies in mix.exs:

def deps do
  [
    {:ex_azure_identity, "~> 0.1.0"}
  ]
end

Quick Start

1. Configure Azure AD

First, configure your Azure AD application to trust your external identity provider:

# Example for GitHub Actions
az ad app federated-credential create \
  --id <APPLICATION_ID> \
  --parameters '{
    "name": "github-deploy",
    "issuer": "https://token.actions.githubusercontent.com",
    "subject": "repo:myorg/myrepo:ref:refs/heads/main",
    "audiences": ["api://AzureADTokenExchange"]
  }'

2. Use in Your Application

# Auto-detect the environment (GitHub Actions, Kubernetes, etc.)
{:ok, credential} = ExAzureIdentity.Credentials.ClientAssertionCredential.new(
  tenant_id: System.get_env("AZURE_TENANT_ID"),
  client_id: System.get_env("AZURE_CLIENT_ID")
)

# Get an access token
{:ok, token} = ExAzureIdentity.get_token(credential, "https://graph.microsoft.com/.default")

# Use the token
headers = [{"Authorization", "Bearer #{token.access_token}"}]

Supported Environments

GitHub Actions

Automatically uses GitHub's OIDC token when running in GitHub Actions:

# .github/workflows/deploy.yml
jobs:
  deploy:
    permissions:
      id-token: write  # Required for OIDC
    steps:
      - uses: actions/checkout@v3
      - run: mix deps.get
      - run: mix run my_azure_script.exs

Kubernetes

Uses the projected service account token or standard service account token:

{:ok, credential} = ClientAssertionCredential.new(
  tenant_id: "tenant-id",
  client_id: "client-id",
  token_provider: :kubernetes
)

AWS

Exchange AWS Cognito tokens for Azure AD tokens:

# Set the Cognito token in environment
System.put_env("AZURE_FEDERATED_TOKEN", cognito_token)

{:ok, credential} = ClientAssertionCredential.new(
  tenant_id: "tenant-id",
  client_id: "client-id",
  token_provider: :aws
)

Custom Providers

Use any OIDC-compliant identity provider:

# From environment variable
{:ok, credential} = ClientAssertionCredential.new(
  tenant_id: "tenant-id",
  client_id: "client-id",
  token_provider: {:env, "MY_OIDC_TOKEN"}
)

# From file
{:ok, credential} = ClientAssertionCredential.new(
  tenant_id: "tenant-id",
  client_id: "client-id",
  token_provider: {:file, "/path/to/token"}
)

# Custom function
{:ok, credential} = ClientAssertionCredential.new(
  tenant_id: "tenant-id",
  client_id: "client-id",
  token_provider: {:callback, fn ->
    # Your custom token acquisition logic
    {:ok, get_my_token()}
  end}
)

Integration with ex_azure

This library integrates seamlessly with ex_azure:

# Create a token provider
provider = ExAzureIdentity.create_token_provider(credential, "https://graph.microsoft.com/.default")

# Use with ex_azure
request = %ExAzure.Request{
  auth: {:bearer_token, provider},
  method: :get,
  base_url: "https://graph.microsoft.com",
  path: "/v1.0/users"
}

{:ok, users} = ExAzure.request(request)

Token Caching

Tokens are automatically cached to minimize API calls:

# First call fetches from Azure AD
{:ok, token1} = ExAzureIdentity.get_token(credential, scope)

# Subsequent calls use cached token (if still valid)
{:ok, token2} = ExAzureIdentity.get_token(credential, scope)

# Force refresh if needed
{:ok, new_token} = ExAzureIdentity.refresh_token(credential, scope)

Multi-Cloud Support

Support for different Azure clouds:

{:ok, credential} = ClientAssertionCredential.new(
  tenant_id: "tenant-id",
  client_id: "client-id",
  cloud: :government  # :public (default), :government, :china, :germany
)

Environment Variables

The library supports these environment variables:

  • GitHub Actions (auto-detected):

    • ACTIONS_ID_TOKEN_REQUEST_URL
    • ACTIONS_ID_TOKEN_REQUEST_TOKEN
    • AZURE_FEDERATED_TOKEN_AUDIENCE (optional, defaults to "api://AzureADTokenExchange")
  • Kubernetes:

    • AZURE_FEDERATED_TOKEN_FILE (optional, for projected tokens)
  • AWS/Generic:

    • AZURE_FEDERATED_TOKEN (for pre-acquired tokens)

Error Handling

The library provides detailed error messages:

case ExAzureIdentity.get_token(credential, scope) do
  {:ok, token} ->
    # Use token
    token.access_token

  {:error, {:invalid_client, message}} ->
    # Handle authentication error
    Logger.error("Authentication failed: #{message}")

  {:error, {:invalid_tenant, message}} ->
    # Handle tenant configuration error
    Logger.error("Tenant error: #{message}")

  {:error, reason} ->
    # Handle other errors
    Logger.error("Failed to get token: #{inspect(reason)}")
end

Security Considerations

  1. No Secrets: This library uses Workload Identity Federation, eliminating the need for client secrets or certificates
  2. Token Validation: Azure AD validates the external token's issuer, subject, and audience
  3. Scope Limitations: Always request the minimum required scope
  4. Token Expiry: Tokens are automatically refreshed before expiry (with a 5-minute buffer)

Troubleshooting

No provider detected

Ensure your environment has the necessary variables or files:

  • GitHub Actions: Check that id-token: write permission is set
  • Kubernetes: Verify service account token is mounted
  • AWS: Ensure AZURE_FEDERATED_TOKEN is set

Invalid client or tenant

Verify your Azure AD configuration:

az ad app show --id <CLIENT_ID>
az ad app federated-credential list --id <CLIENT_ID>

Token exchange fails

Check that the federated credential configuration matches your token:

  • Issuer URL must match exactly (no trailing slashes)
  • Subject must match the token's sub claim
  • Audience should be "api://AzureADTokenExchange"

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages