Skip to content

Commit c3b04bc

Browse files
feat: Add Azure DevOps adapter (#4963)
* chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado <jorge.turrado@mail.schwarz> * chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado <jorge.turrado@mail.schwarz> * chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado <jorge.turrado@mail.schwarz> * chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado <jorge.turrado@mail.schwarz> * chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado <jorge.turrado@mail.schwarz> * chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado <jorge.turrado@mail.schwarz> --------- Signed-off-by: Jorge Turrado <jorge.turrado@mail.schwarz> Co-authored-by: cgoetz-inovex <carlo.goetz@inovex.de>
1 parent 63bef7d commit c3b04bc

File tree

4 files changed

+79
-1
lines changed

4 files changed

+79
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Release (2026-DD-MM)
22

3+
- `core`: [v0.22.0](core/CHANGELOG.md#v0220)
4+
- **Feature:** Support Azure DevOps OIDC adapter
35
- `alb`: [v0.10.0](services/alb/CHANGELOG.md#v0100)
46
- **Feature:** Add new field `AltPort` to `ActiveHealthCheck`
57
- **Feature:** Add new field `Tls` to `HttpHealthCheck`

core/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## v0.22.0
2+
- **Feature:** Support Azure DevOps OIDC adapter
3+
14
## v0.21.1
25
- **Dependencies:** Bump `github.com/golang-jwt/jwt/v5` from `v5.3.0` to `v5.3.1`
36

core/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.21.1
1+
v0.22.0

core/oidcadapters/azuredevops.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package oidcadapters
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
"net/http"
9+
"net/url"
10+
)
11+
12+
const (
13+
adoPipelineOIDCAPIVersion = "7.1"
14+
adoAudience = "api://AzureADTokenExchange"
15+
)
16+
17+
func RequestAzureDevOpsOIDCToken(oidcRequestUrl, oidcRequestToken, serviceConnectionID string) OIDCTokenFunc {
18+
return func(ctx context.Context) (string, error) {
19+
req, err := http.NewRequestWithContext(ctx, http.MethodPost, oidcRequestUrl, http.NoBody)
20+
if err != nil {
21+
return "", fmt.Errorf("azureDevOpsAssertion: failed to build request: %w", err)
22+
}
23+
24+
query, err := url.ParseQuery(req.URL.RawQuery)
25+
if err != nil {
26+
return "", fmt.Errorf("azureDevOpsAssertion: cannot parse URL query")
27+
}
28+
29+
if query.Get("api-version") == "" {
30+
query.Add("api-version", adoPipelineOIDCAPIVersion)
31+
}
32+
33+
if query.Get("serviceConnectionId") == "" && serviceConnectionID != "" {
34+
query.Add("serviceConnectionId", serviceConnectionID)
35+
}
36+
37+
if query.Get("audience") == "" {
38+
query.Set("audience", adoAudience) // Azure DevOps requires this specific audience for OIDC tokens
39+
}
40+
41+
req.URL.RawQuery = query.Encode()
42+
43+
req.Header.Set("Accept", "application/json")
44+
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", oidcRequestToken))
45+
req.Header.Set("Content-Type", "application/json")
46+
47+
resp, err := http.DefaultClient.Do(req)
48+
if err != nil {
49+
return "", fmt.Errorf("azureDevOpsAssertion: cannot request token: %w", err)
50+
}
51+
52+
defer func() {
53+
_ = resp.Body.Close()
54+
}()
55+
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
56+
if err != nil {
57+
return "", fmt.Errorf("azureDevOpsAssertion: cannot parse response: %w", err)
58+
}
59+
60+
if c := resp.StatusCode; c < 200 || c > 299 {
61+
return "", fmt.Errorf("azureDevOpsAssertion: received HTTP status %d with response: %s", resp.StatusCode, body)
62+
}
63+
64+
var tokenRes struct {
65+
Value *string `json:"oidcToken"`
66+
}
67+
if err := json.Unmarshal(body, &tokenRes); err != nil || tokenRes.Value == nil {
68+
return "", fmt.Errorf("azureDevOpsAssertion: cannot unmarshal response: %w", err)
69+
}
70+
71+
return *tokenRes.Value, nil
72+
}
73+
}

0 commit comments

Comments
 (0)