Add --oidc-request-header flag (#1359)

* fix(authcode): Set Origin header on token request

Allow passing Azure AD CORS checks.

on-behalf-of: @eon-se opensource@eon.com
Signed-off-by: Maximilian Blatt <maximilian.blatt.external@eon.com>

* Add `--oidc-request-header` flag

* Add doc

---------

Signed-off-by: Maximilian Blatt <maximilian.blatt.external@eon.com>
Co-authored-by: Maximilian Blatt <maximilian.blatt.external@eon.com>
This commit is contained in:
Hidetake Iwata
2025-07-13 11:04:40 +09:00
committed by GitHub
parent 8537977819
commit 3981c78387
8 changed files with 55 additions and 9 deletions

View File

@@ -129,6 +129,7 @@ func TestCmd_Run(t *testing.T) {
"--oidc-client-secret", "YOUR_CLIENT_SECRET",
"--oidc-extra-scope", "email",
"--oidc-extra-scope", "profile",
"--oidc-request-header", "Origin=localhost:8080",
"--token-cache-storage", "keyring",
"-v1",
},
@@ -138,6 +139,9 @@ func TestCmd_Run(t *testing.T) {
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
ExtraScopes: []string{"email", "profile"},
RequestHeaders: map[string]string{
"Origin": "localhost:8080",
},
},
TokenCacheConfig: tokencache.Config{
Directory: filepath.Join(userHomeDir, ".kube/cache/oidc-login"),

View File

@@ -19,6 +19,7 @@ type getTokenOptions struct {
RedirectURL string
ExtraScopes []string
UseAccessToken bool
RequestHeaders map[string]string
tokenCacheOptions tokenCacheOptions
tlsOptions tlsOptions
pkceOptions pkceOptions
@@ -33,6 +34,7 @@ func (o *getTokenOptions) addFlags(f *pflag.FlagSet) {
f.StringVar(&o.RedirectURL, "oidc-redirect-url", "", "[authcode, authcode-keyboard] Redirect URL")
f.StringSliceVar(&o.ExtraScopes, "oidc-extra-scope", nil, "Scopes to request to the provider")
f.BoolVar(&o.UseAccessToken, "oidc-use-access-token", false, "Instead of using the id_token, use the access_token to authenticate to Kubernetes")
f.StringToStringVar(&o.RequestHeaders, "oidc-request-header", nil, "HTTP headers to send with an authentication request")
f.BoolVar(&o.ForceRefresh, "force-refresh", false, "If set, refresh the ID token regardless of its expiration time")
o.tokenCacheOptions.addFlags(f)
o.tlsOptions.addFlags(f)
@@ -95,6 +97,7 @@ func (cmd *GetToken) New() *cobra.Command {
PKCEMethod: pkceMethod,
UseAccessToken: o.UseAccessToken,
ExtraScopes: o.ExtraScopes,
RequestHeaders: o.RequestHeaders,
},
ForceRefresh: o.ForceRefresh,
TokenCacheConfig: tokenCacheConfig,

View File

@@ -18,6 +18,7 @@ type setupOptions struct {
RedirectURL string
ExtraScopes []string
UseAccessToken bool
RequestHeaders map[string]string
tlsOptions tlsOptions
pkceOptions pkceOptions
authenticationOptions authenticationOptions
@@ -30,6 +31,7 @@ func (o *setupOptions) addFlags(f *pflag.FlagSet) {
f.StringVar(&o.RedirectURL, "oidc-redirect-url", "", "[authcode, authcode-keyboard] Redirect URL")
f.StringSliceVar(&o.ExtraScopes, "oidc-extra-scope", nil, "Scopes to request to the provider")
f.BoolVar(&o.UseAccessToken, "oidc-use-access-token", false, "Instead of using the id_token, use the access_token to authenticate to Kubernetes")
f.StringToStringVar(&o.RequestHeaders, "oidc-request-header", nil, "HTTP headers to send with an authentication request")
o.tlsOptions.addFlags(f)
o.pkceOptions.addFlags(f)
o.authenticationOptions.addFlags(f)
@@ -79,6 +81,7 @@ func (cmd *Setup) New() *cobra.Command {
RedirectURL: o.RedirectURL,
ExtraScopes: o.ExtraScopes,
UseAccessToken: o.UseAccessToken,
RequestHeaders: o.RequestHeaders,
PKCEMethod: pkceMethod,
GrantOptionSet: grantOptionSet,
TLSClientConfig: o.tlsOptions.tlsClientConfig(),

View File

@@ -40,16 +40,17 @@ func (f *Factory) New(ctx context.Context, prov oidc.Provider, tlsClientConfig t
if err != nil {
return nil, fmt.Errorf("could not load the TLS client config: %w", err)
}
baseTransport := &http.Transport{
TLSClientConfig: rawTLSClientConfig,
Proxy: http.ProxyFromEnvironment,
}
loggingTransport := &transport.WithLogging{
Base: baseTransport,
Logger: f.Logger,
}
httpClient := &http.Client{
Transport: loggingTransport,
Transport: &transport.WithHeader{
Base: &transport.WithLogging{
Base: &http.Transport{
TLSClientConfig: rawTLSClientConfig,
Proxy: http.ProxyFromEnvironment,
},
Logger: f.Logger,
},
RequestHeaders: prov.RequestHeaders,
},
}
ctx = context.WithValue(ctx, oauth2.HTTPClient, httpClient)

View File

@@ -0,0 +1,21 @@
package transport
import "net/http"
// WithHeader is a RoundTripper that adds custom headers to each request.
//
// Token retrievel fails when an auth code has been retrieved using Azure AD
// Single Page Application due to the missing "Origin" header for CORS
// validation.
// https://github.com/int128/kubelogin/issues/1048
type WithHeader struct {
Base http.RoundTripper
RequestHeaders map[string]string
}
func (t *WithHeader) RoundTrip(req *http.Request) (*http.Response, error) {
for key, value := range t.RequestHeaders {
req.Header.Set(key, value)
}
return t.Base.RoundTrip(req)
}

View File

@@ -18,6 +18,7 @@ type Provider struct {
RedirectURL string // optional
PKCEMethod PKCEMethod
UseAccessToken bool
RequestHeaders map[string]string
}
// PKCEMethod represents a preferred method of PKCE.

View File

@@ -45,6 +45,7 @@ type Input struct {
RedirectURL string
ExtraScopes []string
UseAccessToken bool
RequestHeaders map[string]string
PKCEMethod oidc.PKCEMethod
GrantOptionSet authentication.GrantOptionSet
TLSClientConfig tlsclientconfig.Config
@@ -62,6 +63,7 @@ func (u Setup) Do(ctx context.Context, in Input) error {
ExtraScopes: in.ExtraScopes,
PKCEMethod: in.PKCEMethod,
UseAccessToken: in.UseAccessToken,
RequestHeaders: in.RequestHeaders,
},
GrantOptionSet: in.GrantOptionSet,
TLSClientConfig: in.TLSClientConfig,