Skip to content

Commit ecf0565

Browse files
committed
feat: Support setting federated token as env
Signed-off-by: Jorge Turrado <jorge.turrado@mail.schwarz>
1 parent 48f3f79 commit ecf0565

File tree

3 files changed

+65
-3
lines changed

3 files changed

+65
-3
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,18 @@ For each authentication method, the try order is:
147147
```go
148148
// Using wokload identity federation flow
149149
config.WithWorkloadIdentityFederationAuth()
150-
// With the custom path for the external OIDC token
150+
// With the external OIDC token
151+
config.WithWorkloadIdentityFederationToken("OIDC Token")
152+
// OR With the custom path for the external OIDC token
151153
config.WithWorkloadIdentityFederationPath("/path/to/your/federated/token")
152154
// For the service account
153155
config.WithServiceAccountEmail("my-sa@sa-stackit.cloud")
154156
```
155157
**B. Environment Variables**
156158
```bash
159+
# Preferred: provide the external OIDC token directly
160+
# (has priority over STACKIT_FEDERATED_TOKEN_FILE)
161+
STACKIT_FEDERATED_TOKEN=<oidc-jwt-token>
157162
# With the custom path for the external OIDC token
158163
STACKIT_FEDERATED_TOKEN_FILE=/path/to/your/federated/token
159164
# For the service account

core/clients/workload_identity_flow.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"net/http"
77
"net/url"
8+
"os"
89
"strings"
910
"sync"
1011
"time"
@@ -15,6 +16,7 @@ import (
1516

1617
const (
1718
clientIDEnv = "STACKIT_SERVICE_ACCOUNT_EMAIL"
19+
FederatedTokenEnv = "STACKIT_FEDERATED_TOKEN" //nolint:gosec // This is not a secret, just the env variable name
1820
FederatedTokenFileEnv = "STACKIT_FEDERATED_TOKEN_FILE" //nolint:gosec // This is not a secret, just the env variable name
1921
wifTokenEndpointEnv = "STACKIT_IDP_TOKEN_ENDPOINT" //nolint:gosec // This is not a secret, just the env variable name
2022
wifTokenExpirationEnv = "STACKIT_IDP_TOKEN_EXPIRATION_SECONDS" //nolint:gosec // This is not a secret, just the env variable name
@@ -134,7 +136,13 @@ func (c *WorkloadIdentityFederationFlow) Init(cfg *WorkloadIdentityFederationFlo
134136
}
135137

136138
if c.config.FederatedTokenFunction == nil {
137-
c.config.FederatedTokenFunction = oidcadapters.ReadJWTFromFileSystem(utils.GetEnvOrDefault(FederatedTokenFileEnv, defaultFederatedTokenPath))
139+
if token, ok := os.LookupEnv(FederatedTokenEnv); ok {
140+
c.config.FederatedTokenFunction = func(_ context.Context) (string, error) {
141+
return token, nil
142+
}
143+
} else {
144+
c.config.FederatedTokenFunction = oidcadapters.ReadJWTFromFileSystem(utils.GetEnvOrDefault(FederatedTokenFileEnv, defaultFederatedTokenPath))
145+
}
138146
}
139147

140148
c.tokenExpirationLeeway = defaultTokenExpirationLeeway

core/clients/workload_identity_flow_test.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ func TestWorkloadIdentityFlowInit(t *testing.T) {
2525
customTokenUrlEnv bool
2626
tokenExpiration string
2727
validAssertion bool
28+
federatedTokenAsEnv bool
29+
tokenFilePathEnv string
2830
tokenFilePathAsEnv bool
2931
missingTokenFilePath bool
3032
wantErr bool
@@ -53,6 +55,19 @@ func TestWorkloadIdentityFlowInit(t *testing.T) {
5355
validAssertion: true,
5456
wantErr: false,
5557
},
58+
{
59+
name: "ok using federated token from env",
60+
clientID: "test@stackit.cloud",
61+
federatedTokenAsEnv: true,
62+
wantErr: false,
63+
},
64+
{
65+
name: "federated token env has priority over invalid token file",
66+
clientID: "test@stackit.cloud",
67+
federatedTokenAsEnv: true,
68+
tokenFilePathEnv: "/tmp/not-existing-token-file",
69+
wantErr: false,
70+
},
5671
{
5772
name: "missing client id",
5873
validAssertion: true,
@@ -87,7 +102,19 @@ func TestWorkloadIdentityFlowInit(t *testing.T) {
87102
flowConfig.TokenExpiration = tt.tokenExpiration
88103
}
89104

90-
if !tt.missingTokenFilePath {
105+
if tt.federatedTokenAsEnv {
106+
token, err := signTokenWithSubject("subject", time.Minute)
107+
if err != nil {
108+
t.Fatalf("failed to create token: %v", err)
109+
}
110+
t.Setenv("STACKIT_FEDERATED_TOKEN", token)
111+
}
112+
113+
if tt.tokenFilePathEnv != "" {
114+
t.Setenv("STACKIT_FEDERATED_TOKEN_FILE", tt.tokenFilePathEnv)
115+
}
116+
117+
if !tt.missingTokenFilePath && !tt.federatedTokenAsEnv {
91118
file, err := os.CreateTemp("", "*.token")
92119
if err != nil {
93120
log.Fatal(err)
@@ -118,6 +145,17 @@ func TestWorkloadIdentityFlowInit(t *testing.T) {
118145
if err := flow.Init(flowConfig); (err != nil) != tt.wantErr {
119146
t.Errorf("KeyFlow.Init() error = %v, wantErr %v", err, tt.wantErr)
120147
}
148+
149+
if tt.federatedTokenAsEnv && !tt.wantErr {
150+
tokenFromConfig, err := flow.config.FederatedTokenFunction(context.Background())
151+
if err != nil {
152+
t.Fatalf("getting federated token from config: %v", err)
153+
}
154+
tokenFromEnv := os.Getenv("STACKIT_FEDERATED_TOKEN")
155+
if tokenFromConfig != tokenFromEnv {
156+
t.Errorf("federated token mismatch, want env token")
157+
}
158+
}
121159
if flow.config == nil {
122160
t.Error("config is nil")
123161
}
@@ -156,6 +194,7 @@ func TestWorkloadIdentityFlowRoundTrip(t *testing.T) {
156194
clientID string
157195
validAssertion bool
158196
injectToken bool
197+
tokenAsEnv bool
159198
wantErr bool
160199
}{
161200
{
@@ -177,6 +216,13 @@ func TestWorkloadIdentityFlowRoundTrip(t *testing.T) {
177216
validAssertion: false,
178217
wantErr: true,
179218
},
219+
{
220+
name: "token from env ok",
221+
clientID: "test@stackit.cloud",
222+
validAssertion: true,
223+
tokenAsEnv: true,
224+
wantErr: false,
225+
},
180226
}
181227
for _, tt := range tests {
182228
t.Run(tt.name, func(t *testing.T) {
@@ -268,6 +314,9 @@ func TestWorkloadIdentityFlowRoundTrip(t *testing.T) {
268314
flowConfig.FederatedTokenFunction = func(context.Context) (string, error) {
269315
return token, nil
270316
}
317+
} else if tt.tokenAsEnv {
318+
t.Setenv("STACKIT_FEDERATED_TOKEN", token)
319+
t.Setenv("STACKIT_FEDERATED_TOKEN_FILE", "/tmp/not-existing-token-file")
271320
} else {
272321
file, err := os.CreateTemp("", "*.token")
273322
if err != nil {

0 commit comments

Comments
 (0)