Skip to content

Commit 9d83e5b

Browse files
authored
feat: support azure entra id service principal authentication (#1133)
## Summary Added Azure Entra ID (Service Principal) authentication support to the Azure provider, allowing for more secure token-based authentication with automatic token management. ## Fixes #1019 ## Changes - Implemented Service Principal authentication for Azure using `ClientID`, `ClientSecret`, and `TenantID` credentials - Added token caching with a sync.Map to avoid repeated authentication requests - Updated the Azure provider to use a priority-based authentication approach: 1. Service Principal (if credentials provided) 2. Context token (if available) 3. API key (fallback) - Added new fields to the AzureKeyConfig schema in both Go and UI - Created database migration for the new Azure authentication fields - Updated documentation to explain the new authentication options - Added UI form fields for the new Azure authentication parameters ## Type of change - [x] Feature - [x] Documentation ## Affected areas - [x] Core (Go) - [x] Transports (HTTP) - [x] Providers/Integrations - [x] UI (Next.js) - [x] Docs ## How to test 1. Configure Azure Service Principal credentials: ```sh export AZURE_CLIENT_ID="your-client-id" export AZURE_CLIENT_SECRET="your-client-secret" export AZURE_TENANT_ID="your-tenant-id" export AZURE_ENDPOINT="https://your-azure-endpoint.openai.azure.com" ``` 2. Use the credentials in your application: ```go key := schemas.Key{ Models: []string{"gpt-4o"}, AzureKeyConfig: &schemas.AzureKeyConfig{ Endpoint: os.Getenv("AZURE_ENDPOINT"), ClientID: bifrost.Ptr(os.Getenv("AZURE_CLIENT_ID")), ClientSecret: bifrost.Ptr(os.Getenv("AZURE_CLIENT_SECRET")), TenantID: bifrost.Ptr(os.Getenv("AZURE_TENANT_ID")), Deployments: map[string]string{ "gpt-4o": "gpt-4o-deployment", }, }, } ``` 3. Run tests to verify authentication works: ```sh go test ./providers/azure/... ``` ## Breaking changes - [x] No ## Related issues Enhances Azure authentication options for enterprise deployments. ## Security considerations - Service Principal credentials are sensitive and should be stored securely - The implementation uses the official Azure SDK for authentication - Token caching is implemented to reduce authentication requests - Credentials can be provided via environment variables for better security ## Checklist - [x] I added/updated tests where appropriate - [x] I updated documentation where needed - [x] I verified builds succeed (Go and UI)
2 parents c2af83f + cced13e commit 9d83e5b

File tree

21 files changed

+685
-89
lines changed

21 files changed

+685
-89
lines changed

.github/workflows/release-pipeline.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ jobs:
100100
BEDROCK_API_KEY: ${{ secrets.BEDROCK_API_KEY }}
101101
AZURE_ENDPOINT: ${{ secrets.AZURE_ENDPOINT }}
102102
AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }}
103+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
104+
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
105+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
103106
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
104107
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
105108
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
@@ -175,6 +178,9 @@ jobs:
175178
BEDROCK_API_KEY: ${{ secrets.BEDROCK_API_KEY }}
176179
AZURE_ENDPOINT: ${{ secrets.AZURE_ENDPOINT }}
177180
AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }}
181+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
182+
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
183+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
178184
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
179185
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
180186
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
@@ -252,6 +258,9 @@ jobs:
252258
BEDROCK_API_KEY: ${{ secrets.BEDROCK_API_KEY }}
253259
AZURE_ENDPOINT: ${{ secrets.AZURE_ENDPOINT }}
254260
AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }}
261+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
262+
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
263+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
255264
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
256265
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
257266
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}
@@ -341,6 +350,9 @@ jobs:
341350
BEDROCK_API_KEY: ${{ secrets.BEDROCK_API_KEY }}
342351
AZURE_ENDPOINT: ${{ secrets.AZURE_ENDPOINT }}
343352
AZURE_API_KEY: ${{ secrets.AZURE_API_KEY }}
353+
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
354+
AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }}
355+
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
344356
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
345357
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
346358
MISTRAL_API_KEY: ${{ secrets.MISTRAL_API_KEY }}

core/changelog.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
- fix: empty string handling in Anthropic provider to prevent sending empty content blocks in chat requests
2-
- fix: Gemini/Vertex tool conversion to append all function declarations to a single Tool object
2+
- fix: Gemini/Vertex tool conversion to append all function declarations to a single Tool object
3+
- feat: added Azure Entra ID (Service Principal) authentication support to the Azure provider

core/go.mod

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ module github.com/maximhq/bifrost/core
33
go 1.25.5
44

55
require (
6+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0
7+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
68
github.com/aws/aws-sdk-go-v2 v1.41.0
79
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4
810
github.com/aws/aws-sdk-go-v2/config v1.32.6
@@ -22,6 +24,8 @@ require (
2224

2325
require (
2426
cloud.google.com/go/compute/metadata v0.9.0 // indirect
27+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
28+
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
2529
github.com/andybalholm/brotli v1.2.0 // indirect
2630
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect
2731
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect
@@ -42,12 +46,15 @@ require (
4246
github.com/bytedance/sonic/loader v0.4.0 // indirect
4347
github.com/cloudwego/base64x v0.1.6 // indirect
4448
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
49+
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
4550
github.com/invopop/jsonschema v0.13.0 // indirect
4651
github.com/klauspost/compress v1.18.2 // indirect
4752
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
53+
github.com/kylelemons/godebug v1.1.0 // indirect
4854
github.com/mailru/easyjson v0.9.1 // indirect
4955
github.com/mattn/go-colorable v0.1.14 // indirect
5056
github.com/mattn/go-isatty v0.0.20 // indirect
57+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
5158
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
5259
github.com/rogpeppe/go-internal v1.14.1 // indirect
5360
github.com/spf13/cast v1.10.0 // indirect
@@ -56,8 +63,8 @@ require (
5663
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
5764
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
5865
golang.org/x/arch v0.23.0 // indirect
66+
golang.org/x/crypto v0.46.0 // indirect
5967
golang.org/x/net v0.48.0 // indirect
6068
golang.org/x/sys v0.39.0 // indirect
61-
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
6269
gopkg.in/yaml.v3 v3.0.1 // indirect
6370
)

core/go.sum

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
22
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
3+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc=
4+
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw=
5+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
6+
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
7+
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
8+
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
9+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
10+
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
11+
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
12+
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
13+
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
14+
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
315
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
416
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
517
github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4=
@@ -60,6 +72,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
6072
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
6173
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
6274
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
75+
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
76+
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
6377
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
6478
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
6579
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -69,17 +83,18 @@ github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRS
6983
github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo=
7084
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
7185
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
86+
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
87+
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
7288
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
7389
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
7490
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
7591
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
76-
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
7792
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
7893
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
79-
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
80-
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
8194
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
8295
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
96+
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
97+
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
8398
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
8499
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
85100
github.com/mark3labs/mcp-go v0.43.2 h1:21PUSlWWiSbUPQwXIJ5WKlETixpFpq+WBpbMGDSVy/I=
@@ -91,6 +106,8 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
91106
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
92107
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
93108
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
109+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
110+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
94111
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
95112
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
96113
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
@@ -126,12 +143,15 @@ github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zI
126143
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
127144
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
128145
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
146+
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
147+
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
129148
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
130149
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
131150
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
132151
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
133152
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
134153
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
154+
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
135155
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
136156
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
137157
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=

core/internal/testutil/account.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ func (account *ComprehensiveTestAccount) GetKeysForProvider(ctx *context.Context
221221
"o1": "o1",
222222
"text-embedding-ada-002": "text-embedding-ada-002",
223223
},
224+
ClientID: bifrost.Ptr(os.Getenv("AZURE_CLIENT_ID")),
225+
ClientSecret: bifrost.Ptr(os.Getenv("AZURE_CLIENT_SECRET")),
226+
TenantID: bifrost.Ptr(os.Getenv("AZURE_TENANT_ID")),
224227
},
225228
UseForBatchAPI: bifrost.Ptr(true),
226229
},

0 commit comments

Comments
 (0)