Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 55 additions & 3 deletions internal/handlers/oidc_handling_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1225,7 +1225,7 @@ func TestOIDCURLsAreAuthenticated(t *testing.T) {
},
urlMocks: []mockHttpRequest{},
expectedLogLines: []string{
"registered aws OIDC credentials for terraform registry: terraform.example.com",
"registered aws OIDC credentials for terraform registry: https://terraform.example.com",
},
urlsToAuthenticate: []string{
"https://terraform.example.com/some-package",
Expand Down Expand Up @@ -1268,7 +1268,7 @@ func TestOIDCURLsAreAuthenticated(t *testing.T) {
},
urlMocks: []mockHttpRequest{},
expectedLogLines: []string{
"registered jfrog OIDC credentials for terraform registry: jfrog.example.com",
"registered jfrog OIDC credentials for terraform registry: https://jfrog.example.com",
},
urlsToAuthenticate: []string{
"https://jfrog.example.com/some-package",
Expand All @@ -1291,7 +1291,7 @@ func TestOIDCURLsAreAuthenticated(t *testing.T) {
},
urlMocks: []mockHttpRequest{},
expectedLogLines: []string{
"registered cloudsmith OIDC credentials for terraform registry: cloudsmith.example.com",
"registered cloudsmith OIDC credentials for terraform registry: https://cloudsmith.example.com",
},
urlsToAuthenticate: []string{
"https://cloudsmith.example.com/some-package",
Expand Down Expand Up @@ -1390,3 +1390,55 @@ func TestOIDCURLsAreAuthenticated(t *testing.T) {
})
}
}

// TestOIDCSameHostDifferentPaths verifies that two OIDC credentials on the same
// host with different URL paths do not collide — each request is authenticated
// with the credential whose path is the longest prefix match.
func TestOIDCSameHostDifferentPaths(t *testing.T) {
httpmock.Activate()
defer httpmock.DeactivateAndReset()

tenantA := "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa"
tenantB := "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
clientId := "87654321-4321-4321-4321-210987654321"

tokenUrl := "https://token.actions.example.com" //nolint:gosec // test URL
httpmock.RegisterResponder("GET", tokenUrl,
httpmock.NewStringResponder(200, `{"count":1,"value":"sometoken"}`))

// Two different Azure tenants → two different tokens
httpmock.RegisterResponder("POST", fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenantA),
httpmock.NewStringResponder(200, `{"access_token":"__token_A__","expires_in":3600,"token_type":"Bearer"}`))
httpmock.RegisterResponder("POST", fmt.Sprintf("https://login.microsoftonline.com/%s/oauth2/v2.0/token", tenantB),
httpmock.NewStringResponder(200, `{"access_token":"__token_B__","expires_in":3600,"token_type":"Bearer"}`))

t.Setenv("ACTIONS_ID_TOKEN_REQUEST_URL", tokenUrl)
t.Setenv("ACTIONS_ID_TOKEN_REQUEST_TOKEN", "sometoken")

creds := config.Credentials{
config.Credential{
"type": "terraform_registry",
"url": "https://terraform.example.com/org/feed-A",
"tenant-id": tenantA,
"client-id": clientId,
},
config.Credential{
"type": "terraform_registry",
"url": "https://terraform.example.com/org/feed-B",
"tenant-id": tenantB,
"client-id": clientId,
},
}

handler := NewTerraformRegistryHandler(creds)

// Request to feed-A path should get token A
reqA := httptest.NewRequest("GET", "https://terraform.example.com/org/feed-A/v1/providers/org/name", nil)
reqA = handleRequestAndClose(handler, reqA, nil)
assertHasTokenAuth(t, reqA, "Bearer", "__token_A__", "feed-A should use token A")

// Request to feed-B path should get token B
reqB := httptest.NewRequest("GET", "https://terraform.example.com/org/feed-B/v1/providers/org/name", nil)
reqB = handleRequestAndClose(handler, reqB, nil)
assertHasTokenAuth(t, reqB, "Bearer", "__token_B__", "feed-B should use token B")
}
24 changes: 8 additions & 16 deletions internal/handlers/terraform_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package handlers

import (
"net/http"
"sync"

"github.com/elazarl/goproxy"

Expand All @@ -13,34 +12,27 @@ import (
)

type TerraformRegistryHandler struct {
credentials map[string]string
oidcCredentials map[string]*oidc.OIDCCredential
mutex sync.RWMutex
credentials map[string]string
oidcRegistry *oidc.OIDCRegistry
}

func NewTerraformRegistryHandler(credentials config.Credentials) *TerraformRegistryHandler {
handler := TerraformRegistryHandler{
credentials: make(map[string]string),
oidcCredentials: make(map[string]*oidc.OIDCCredential),
credentials: make(map[string]string),
oidcRegistry: oidc.NewOIDCRegistry(),
}

for _, credential := range credentials {
if credential["type"] != "terraform_registry" {
continue
}

host := credential.Host()

oidcCredential, _ := oidc.CreateOIDCCredential(credential)
if oidcCredential != nil {
if host != "" {
handler.oidcCredentials[host] = oidcCredential
logging.RequestLogf(nil, "registered %s OIDC credentials for terraform registry: %s", oidcCredential.Provider(), host)
}
// OIDC credentials are not used as static credentials.
if oidcCred, _, _ := handler.oidcRegistry.Register(credential, []string{"url"}, "terraform registry"); oidcCred != nil {
continue
Comment on lines +30 to 32
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The result of oidcRegistry.Register includes a boolean indicating whether the OIDC credential was actually registered (i.e., a usable URL/host key was resolved and parsed). The current check only looks at oidcCred != nil, so an OIDC-configured credential that fails registration will still be skipped for static token setup, leaving it unusable with no handler-level signal. Consider checking the returned registered flag (and logging when it’s false), and only continue when registration succeeds.

Copilot uses AI. Check for mistakes.
}

handler.credentials[host] = credential.GetString("token")
handler.credentials[credential.Host()] = credential.GetString("token")
}
return &handler
}
Expand All @@ -51,7 +43,7 @@ func (h *TerraformRegistryHandler) HandleRequest(request *http.Request, context
}

// Try OIDC credentials first
if oidc.TryAuthOIDCRequestWithPrefix(&h.mutex, h.oidcCredentials, request, context) {
if h.oidcRegistry.TryAuth(request, context) {
return request, nil
}

Expand Down
Loading