tsnet: allow for automatic ID token generation

Allow for optionally specifiying an audience for tsnet. This is passed
to the underlying identity federation logic to allow for tsnet auth to
use automatic ID token generation for authentication.

Updates https://github.com/tailscale/corp/issues/33316

Signed-off-by: Mario Minardi <mario@tailscale.com>
This commit is contained in:
Mario Minardi
2026-01-13 17:06:48 -07:00
committed by Mario Minardi
parent 28f163542c
commit 02af7c963c
2 changed files with 70 additions and 6 deletions
+41 -1
View File
@@ -1503,6 +1503,7 @@ func TestResolveAuthKey(t *testing.T) {
clientSecret string
clientID string
idToken string
audience string
oauthAvailable bool
wifAvailable bool
resolveViaOAuth func(ctx context.Context, clientSecret string, tags []string) (string, error)
@@ -1550,6 +1551,23 @@ func TestResolveAuthKey(t *testing.T) {
wantAuthKey: "tskey-auth-via-wif",
wantErrContains: "",
},
{
name: "successful resolution via federated audience",
clientID: "client-id-123",
audience: "api.tailscale.com",
wifAvailable: true,
resolveViaWIF: func(ctx context.Context, baseURL, clientID, idToken, audience string, tags []string) (string, error) {
if clientID != "client-id-123" {
return "", fmt.Errorf("unexpected client ID: %s", clientID)
}
if audience != "api.tailscale.com" {
return "", fmt.Errorf("unexpected ID token: %s", idToken)
}
return "tskey-auth-via-wif", nil
},
wantAuthKey: "tskey-auth-via-wif",
wantErrContains: "",
},
{
name: "failing resolution via federated ID token",
clientID: "client-id-123",
@@ -1561,7 +1579,7 @@ func TestResolveAuthKey(t *testing.T) {
wantErrContains: "resolution failed",
},
{
name: "empty client ID",
name: "empty client ID with ID token",
clientID: "",
idToken: "id-token-456",
wifAvailable: true,
@@ -1570,6 +1588,16 @@ func TestResolveAuthKey(t *testing.T) {
},
wantErrContains: "empty",
},
{
name: "empty client ID with audience",
clientID: "",
audience: "api.tailscale.com",
wifAvailable: true,
resolveViaWIF: func(ctx context.Context, baseURL, clientID, idToken, audience string, tags []string) (string, error) {
return "", fmt.Errorf("should not be called")
},
wantErrContains: "empty",
},
{
name: "empty ID token",
clientID: "client-id-123",
@@ -1580,6 +1608,17 @@ func TestResolveAuthKey(t *testing.T) {
},
wantErrContains: "empty",
},
{
name: "audience with ID token",
clientID: "client-id-123",
idToken: "id-token-456",
audience: "api.tailscale.com",
wifAvailable: true,
resolveViaWIF: func(ctx context.Context, baseURL, clientID, idToken, audience string, tags []string) (string, error) {
return "", fmt.Errorf("should not be called")
},
wantErrContains: "only one of ID token and audience",
},
{
name: "workload identity resolution skipped if resolution via OAuth token succeeds",
clientSecret: "tskey-client-secret-123",
@@ -1665,6 +1704,7 @@ func TestResolveAuthKey(t *testing.T) {
ClientSecret: tt.clientSecret,
ClientID: tt.clientID,
IDToken: tt.idToken,
Audience: tt.audience,
ControlURL: "https://control.example.com",
}
s.shutdownCtx = context.Background()