From 3bd9e4c665e45cd3578667de15bde54b0ac5fba2 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Wed, 18 Feb 2026 20:10:58 +0100 Subject: [PATCH 1/6] chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado --- core/oidcadapters/azuredevops.go | 73 ++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 core/oidcadapters/azuredevops.go diff --git a/core/oidcadapters/azuredevops.go b/core/oidcadapters/azuredevops.go new file mode 100644 index 000000000..f913138cb --- /dev/null +++ b/core/oidcadapters/azuredevops.go @@ -0,0 +1,73 @@ +package oidcadapters + +import ( + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" +) + +const ( + adoPipelineOIDCAPIVersion = "7.1" + adoAudience = "api://AzureADTokenExchange" +) + +func RequestAzureDevOpsOIDCToken(oidcRequestUrl, oidcRequestToken, serviceConnectionID string) OIDCTokenFunc { + return func(ctx context.Context) (string, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, oidcRequestUrl, http.NoBody) + if err != nil { + return "", fmt.Errorf("azureDevOpsAssertion: failed to build request: %w", err) + } + + query, err := url.ParseQuery(req.URL.RawQuery) + if err != nil { + return "", fmt.Errorf("azureDevOpsAssertion: cannot parse URL query") + } + + if query.Get("api-version") == "" { + query.Add("api-version", adoPipelineOIDCAPIVersion) + } + + if query.Get("serviceConnectionId") == "" && serviceConnectionID != "" { + query.Add("serviceConnectionId", serviceConnectionID) + } + + if query.Get("audience") == "" { + query.Set("audience", adoAudience) // Azure DevOps requires this specific audience for OIDC tokens + } + + req.URL.RawQuery = query.Encode() + + req.Header.Set("Accept", "application/json") + req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", oidcRequestToken)) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", fmt.Errorf("azureDevOpsAssertion: cannot request token: %w", err) + } + + defer func() { + _ = resp.Body.Close() + }() + body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) + if err != nil { + return "", fmt.Errorf("azureDevOpsAssertion: cannot parse response: %w", err) + } + + if c := resp.StatusCode; c < 200 || c > 299 { + return "", fmt.Errorf("azureDevOpsAssertion: received HTTP status %d with response: %s", resp.StatusCode, body) + } + + var tokenRes struct { + Value string `json:"value"` + } + if err := json.Unmarshal(body, &tokenRes); err != nil { + return "", fmt.Errorf("azureDevOpsAssertion: cannot unmarshal response: %w", err) + } + + return tokenRes.Value, nil + } +} From 9a89bbf9f02f7abc7ed9dd46c1ef9e20da0e3ae7 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Wed, 18 Feb 2026 20:15:20 +0100 Subject: [PATCH 2/6] chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado --- CHANGELOG.md | 2 ++ core/CHANGELOG.md | 3 +++ core/VERSION | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0059e780..d6b7c2563 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ ## Release (2026-mm-dd) +- `core`: [v0.22.0](core/CHANGELOG.md#v0220) + - **Feature:** Support Azure DevOps OIDC adapter - `core`: [v0.21.1](core/CHANGELOG.md#v0211) - **Dependencies**: Bump `github.com/golang-jwt/jwt/v5` from `v5.3.0` to `v5.3.1` - `alb`: diff --git a/core/CHANGELOG.md b/core/CHANGELOG.md index 6b2630971..c66211445 100644 --- a/core/CHANGELOG.md +++ b/core/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.22.0 +- **Feature:** Support Azure DevOps OIDC adapter + ## v0.21.1 - **Dependencies:** Bump `github.com/golang-jwt/jwt/v5` from `v5.3.0` to `v5.3.1` diff --git a/core/VERSION b/core/VERSION index 40c85001a..4f2794371 100644 --- a/core/VERSION +++ b/core/VERSION @@ -1 +1 @@ -v0.21.1 +v0.22.0 From c1e0812ff15fc081cb39d28a0a9ee36033b59580 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Wed, 18 Feb 2026 20:16:05 +0100 Subject: [PATCH 3/6] chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado --- CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d6b7c2563..e467fa5ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,9 @@ ## Release (2026-mm-dd) -- `core`: [v0.22.0](core/CHANGELOG.md#v0220) - - **Feature:** Support Azure DevOps OIDC adapter -- `core`: [v0.21.1](core/CHANGELOG.md#v0211) - - **Dependencies**: Bump `github.com/golang-jwt/jwt/v5` from `v5.3.0` to `v5.3.1` +- `core`: + - [v0.22.0](core/CHANGELOG.md#v0220) + - **Feature:** Support Azure DevOps OIDC adapter + - [v0.21.1](core/CHANGELOG.md#v0211) + - **Dependencies**: Bump `github.com/golang-jwt/jwt/v5` from `v5.3.0` to `v5.3.1` - `alb`: - [v0.9.3](services/alb/CHANGELOG.md#v093) - Bump STACKIT SDK core module from `v0.21.0` to `v0.21.1` From 89bc44e1a2902c1a5b5fbf88c467cb8cffba2f3a Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Thu, 19 Feb 2026 00:05:50 +0100 Subject: [PATCH 4/6] chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado --- core/oidcadapters/azuredevops.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/oidcadapters/azuredevops.go b/core/oidcadapters/azuredevops.go index f913138cb..1e57019e1 100644 --- a/core/oidcadapters/azuredevops.go +++ b/core/oidcadapters/azuredevops.go @@ -16,7 +16,7 @@ const ( func RequestAzureDevOpsOIDCToken(oidcRequestUrl, oidcRequestToken, serviceConnectionID string) OIDCTokenFunc { return func(ctx context.Context) (string, error) { - req, err := http.NewRequestWithContext(ctx, http.MethodGet, oidcRequestUrl, http.NoBody) + req, err := http.NewRequestWithContext(ctx, http.MethodPost, oidcRequestUrl, http.NoBody) if err != nil { return "", fmt.Errorf("azureDevOpsAssertion: failed to build request: %w", err) } From 0eade063c837fa8ac932edb9b376744a7f41eb1b Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Thu, 19 Feb 2026 00:14:15 +0100 Subject: [PATCH 5/6] chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado --- core/oidcadapters/azuredevops.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/oidcadapters/azuredevops.go b/core/oidcadapters/azuredevops.go index 1e57019e1..757307429 100644 --- a/core/oidcadapters/azuredevops.go +++ b/core/oidcadapters/azuredevops.go @@ -42,7 +42,7 @@ func RequestAzureDevOpsOIDCToken(oidcRequestUrl, oidcRequestToken, serviceConnec req.Header.Set("Accept", "application/json") req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", oidcRequestToken)) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + req.Header.Set("Content-Type", "application/json") resp, err := http.DefaultClient.Do(req) if err != nil { From 3f219dc9de13a464e0bc968963e9a7f6abe68e31 Mon Sep 17 00:00:00 2001 From: Jorge Turrado Date: Thu, 19 Feb 2026 00:30:50 +0100 Subject: [PATCH 6/6] chore: Add Azure DevOps adapter Signed-off-by: Jorge Turrado --- core/oidcadapters/azuredevops.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/oidcadapters/azuredevops.go b/core/oidcadapters/azuredevops.go index 757307429..12617dbd2 100644 --- a/core/oidcadapters/azuredevops.go +++ b/core/oidcadapters/azuredevops.go @@ -62,12 +62,12 @@ func RequestAzureDevOpsOIDCToken(oidcRequestUrl, oidcRequestToken, serviceConnec } var tokenRes struct { - Value string `json:"value"` + Value *string `json:"oidcToken"` } - if err := json.Unmarshal(body, &tokenRes); err != nil { + if err := json.Unmarshal(body, &tokenRes); err != nil || tokenRes.Value == nil { return "", fmt.Errorf("azureDevOpsAssertion: cannot unmarshal response: %w", err) } - return tokenRes.Value, nil + return *tokenRes.Value, nil } }