Add --oidc-redirect-url to override redirect URL (#1263)

This commit is contained in:
Hidetake Iwata
2025-06-16 13:42:48 +09:00
committed by GitHub
parent a85488b4fc
commit 0f2f54d4bf
13 changed files with 54 additions and 52 deletions

View File

@@ -12,8 +12,6 @@ import (
"github.com/spf13/pflag"
)
const oobRedirectURI = "urn:ietf:wg:oauth:2.0:oob"
type authenticationOptions struct {
GrantType string
ListenAddress []string
@@ -23,8 +21,8 @@ type authenticationOptions struct {
LocalServerCertFile string
LocalServerKeyFile string
OpenURLAfterAuthentication string
RedirectURLHostname string
RedirectURLAuthCodeKeyboard string
RedirectURLHostname string // DEPRECATED
RedirectURLAuthCodeKeyboard string // DEPRECATED
AuthRequestExtraParams map[string]string
Username string
Password string
@@ -47,8 +45,14 @@ func (o *authenticationOptions) addFlags(f *pflag.FlagSet) {
f.StringVar(&o.LocalServerCertFile, "local-server-cert", "", "[authcode] Certificate path for the local server")
f.StringVar(&o.LocalServerKeyFile, "local-server-key", "", "[authcode] Certificate key path for the local server")
f.StringVar(&o.OpenURLAfterAuthentication, "open-url-after-authentication", "", "[authcode] If set, open the URL in the browser after authentication")
f.StringVar(&o.RedirectURLHostname, "oidc-redirect-url-hostname", "localhost", "[authcode] Hostname of the redirect URL")
f.StringVar(&o.RedirectURLAuthCodeKeyboard, "oidc-redirect-url-authcode-keyboard", oobRedirectURI, "[authcode-keyboard] Redirect URL")
f.StringVar(&o.RedirectURLHostname, "oidc-redirect-url-hostname", "", "[authcode] Hostname of the redirect URL")
if err := f.MarkDeprecated("oidc-redirect-url-hostname", "use --oidc-redirect-url instead."); err != nil {
panic(err)
}
f.StringVar(&o.RedirectURLAuthCodeKeyboard, "oidc-redirect-url-authcode-keyboard", "", "Equivalent to --oidc-redirect-url")
if err := f.MarkDeprecated("oidc-redirect-url-authcode-keyboard", "use --oidc-redirect-url instead."); err != nil {
panic(err)
}
f.StringToStringVar(&o.AuthRequestExtraParams, "oidc-auth-request-extra-params", nil, "[authcode, authcode-keyboard] Extra query parameters to send with an authentication request")
f.StringVar(&o.Username, "username", "", "[password] Username for resource owner password credentials grant")
f.StringVar(&o.Password, "password", "", "[password] Password for resource owner password credentials grant")
@@ -76,7 +80,6 @@ func (o *authenticationOptions) grantOptionSet() (s authentication.GrantOptionSe
case o.GrantType == "authcode-keyboard":
s.AuthCodeKeyboardOption = &authcode.KeyboardOption{
AuthRequestExtraParams: o.AuthRequestExtraParams,
RedirectURL: o.RedirectURLAuthCodeKeyboard,
}
case o.GrantType == "password" || (o.GrantType == "auto" && o.Username != ""):
s.ROPCOption = &ropc.Option{

View File

@@ -21,7 +21,6 @@ func Test_authenticationOptions_grantOptionSet(t *testing.T) {
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
},
},
@@ -61,19 +60,19 @@ func Test_authenticationOptions_grantOptionSet(t *testing.T) {
"--grant-type", "authcode-keyboard",
},
want: authentication.GrantOptionSet{
AuthCodeKeyboardOption: &authcode.KeyboardOption{
RedirectURL: oobRedirectURI,
},
AuthCodeKeyboardOption: &authcode.KeyboardOption{},
},
},
"GrantType=authcode-keyboard with full options": {
args: []string{
"--grant-type", "authcode-keyboard",
"--oidc-redirect-url-authcode-keyboard", "http://localhost",
"--oidc-auth-request-extra-params", "ttl=86400",
"--oidc-auth-request-extra-params", "reauth=true",
},
want: authentication.GrantOptionSet{
AuthCodeKeyboardOption: &authcode.KeyboardOption{
RedirectURL: "http://localhost",
AuthRequestExtraParams: map[string]string{"ttl": "86400", "reauth": "true"},
},
},
},

View File

@@ -29,7 +29,6 @@ func TestCmd_Run(t *testing.T) {
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
},
}
@@ -188,7 +187,6 @@ func TestCmd_Run(t *testing.T) {
AuthCodeBrowserOption: &authcode.BrowserOption{
BindAddress: defaultListenAddress,
AuthenticationTimeout: defaultAuthenticationTimeoutSec * time.Second,
RedirectURLHostname: "localhost",
LocalServerCertFile: filepath.Join(userHomeDir, ".kube/oidc-server.crt"),
LocalServerKeyFile: filepath.Join(userHomeDir, ".kube/oidc-server.key"),
},

View File

@@ -16,6 +16,7 @@ type getTokenOptions struct {
IssuerURL string
ClientID string
ClientSecret string
RedirectURL string
ExtraScopes []string
UseAccessToken bool
tokenCacheOptions tokenCacheOptions
@@ -29,6 +30,7 @@ func (o *getTokenOptions) addFlags(f *pflag.FlagSet) {
f.StringVar(&o.IssuerURL, "oidc-issuer-url", "", "Issuer URL of the provider (mandatory)")
f.StringVar(&o.ClientID, "oidc-client-id", "", "Client ID of the provider (mandatory)")
f.StringVar(&o.ClientSecret, "oidc-client-secret", "", "Client secret of the provider")
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.BoolVar(&o.ForceRefresh, "force-refresh", false, "If set, refresh the ID token regardless of its expiration time")
@@ -80,11 +82,16 @@ func (cmd *GetToken) New() *cobra.Command {
if err != nil {
return fmt.Errorf("get-token: %w", err)
}
redirectURL := o.RedirectURL
if o.authenticationOptions.RedirectURLAuthCodeKeyboard != "" {
redirectURL = o.authenticationOptions.RedirectURLAuthCodeKeyboard
}
in := credentialplugin.Input{
Provider: oidc.Provider{
IssuerURL: o.IssuerURL,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
RedirectURL: redirectURL,
PKCEMethod: pkceMethod,
UseAccessToken: o.UseAccessToken,
ExtraScopes: o.ExtraScopes,

View File

@@ -15,6 +15,7 @@ type setupOptions struct {
IssuerURL string
ClientID string
ClientSecret string
RedirectURL string
ExtraScopes []string
UseAccessToken bool
tlsOptions tlsOptions
@@ -26,6 +27,7 @@ func (o *setupOptions) addFlags(f *pflag.FlagSet) {
f.StringVar(&o.IssuerURL, "oidc-issuer-url", "", "Issuer URL of the provider")
f.StringVar(&o.ClientID, "oidc-client-id", "", "Client ID of the provider")
f.StringVar(&o.ClientSecret, "oidc-client-secret", "", "Client secret of the provider")
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")
o.tlsOptions.addFlags(f)
@@ -74,6 +76,7 @@ func (cmd *Setup) New() *cobra.Command {
IssuerURL: o.IssuerURL,
ClientID: o.ClientID,
ClientSecret: o.ClientSecret,
RedirectURL: o.RedirectURL,
ExtraScopes: o.ExtraScopes,
UseAccessToken: o.UseAccessToken,
PKCEMethod: pkceMethod,

View File

@@ -31,15 +31,13 @@ type AuthCodeURLInput struct {
State string
Nonce string
PKCEParams pkce.Params
RedirectURI string
AuthRequestExtraParams map[string]string
}
type ExchangeAuthCodeInput struct {
Code string
PKCEParams pkce.Params
Nonce string
RedirectURI string
Code string
PKCEParams pkce.Params
Nonce string
}
type GetTokenByAuthCodeInput struct {
@@ -47,7 +45,7 @@ type GetTokenByAuthCodeInput struct {
State string
Nonce string
PKCEParams pkce.Params
RedirectURLHostname string
RedirectURLHostname string // DEPRECATED
AuthRequestExtraParams map[string]string
LocalServerSuccessHTML string
LocalServerCertFile string
@@ -97,19 +95,15 @@ func (c *client) GetTokenByAuthCode(ctx context.Context, in GetTokenByAuthCodeIn
// GetAuthCodeURL returns the URL of authentication request for the authorization code flow.
func (c *client) GetAuthCodeURL(in AuthCodeURLInput) string {
cfg := c.oauth2Config
cfg.RedirectURL = in.RedirectURI
opts := authorizationRequestOptions(in.Nonce, in.PKCEParams, in.AuthRequestExtraParams)
return cfg.AuthCodeURL(in.State, opts...)
return c.oauth2Config.AuthCodeURL(in.State, opts...)
}
// ExchangeAuthCode exchanges the authorization code and token.
func (c *client) ExchangeAuthCode(ctx context.Context, in ExchangeAuthCodeInput) (*oidc.TokenSet, error) {
ctx = c.wrapContext(ctx)
cfg := c.oauth2Config
cfg.RedirectURL = in.RedirectURI
opts := tokenRequestOptions(in.PKCEParams)
token, err := cfg.Exchange(ctx, in.Code, opts...)
token, err := c.oauth2Config.Exchange(ctx, in.Code, opts...)
if err != nil {
return nil, fmt.Errorf("exchange error: %w", err)
}

View File

@@ -72,6 +72,7 @@ func (f *Factory) New(ctx context.Context, prov oidc.Provider, tlsClientConfig t
Endpoint: provider.Endpoint(),
ClientID: prov.ClientID,
ClientSecret: prov.ClientSecret,
RedirectURL: prov.RedirectURL,
Scopes: append(prov.ExtraScopes, gooidc.ScopeOpenID),
},
clock: f.Clock,

View File

@@ -15,6 +15,7 @@ type Provider struct {
ClientID string
ClientSecret string // optional
ExtraScopes []string // optional
RedirectURL string // optional
PKCEMethod PKCEMethod
UseAccessToken bool
}

View File

@@ -19,7 +19,7 @@ type BrowserOption struct {
BindAddress []string
AuthenticationTimeout time.Duration
OpenURLAfterAuthentication string
RedirectURLHostname string
RedirectURLHostname string // DEPRECATED
AuthRequestExtraParams map[string]string
LocalServerCertFile string
LocalServerKeyFile string

View File

@@ -28,7 +28,6 @@ func TestBrowser_Do(t *testing.T) {
LocalServerCertFile: "/path/to/local-server-cert",
LocalServerKeyFile: "/path/to/local-server-key",
OpenURLAfterAuthentication: "https://example.com/success.html",
RedirectURLHostname: "localhost",
AuthRequestExtraParams: map[string]string{"ttl": "86400", "reauth": "true"},
}
mockClient := client_mock.NewMockInterface(t)
@@ -42,9 +41,6 @@ func TestBrowser_Do(t *testing.T) {
if diff := cmp.Diff(BrowserRedirectHTML("https://example.com/success.html"), in.LocalServerSuccessHTML); diff != "" {
t.Errorf("LocalServerSuccessHTML mismatch (-want +got):\n%s", diff)
}
if diff := cmp.Diff(o.RedirectURLHostname, in.RedirectURLHostname); diff != "" {
t.Errorf("RedirectURLHostname mismatch (-want +got):\n%s", diff)
}
if diff := cmp.Diff(o.AuthRequestExtraParams, in.AuthRequestExtraParams); diff != "" {
t.Errorf("AuthRequestExtraParams mismatch (-want +got):\n%s", diff)
}

View File

@@ -15,7 +15,6 @@ const keyboardPrompt = "Enter code: "
type KeyboardOption struct {
AuthRequestExtraParams map[string]string
RedirectURL string
}
// Keyboard provides the authorization code flow with keyboard interactive.
@@ -42,7 +41,6 @@ func (u *Keyboard) Do(ctx context.Context, o *KeyboardOption, oidcClient client.
State: state,
Nonce: nonce,
PKCEParams: pkceParams,
RedirectURI: o.RedirectURL,
AuthRequestExtraParams: o.AuthRequestExtraParams,
})
u.Logger.Printf("Please visit the following URL in your browser: %s", authCodeURL)
@@ -53,10 +51,9 @@ func (u *Keyboard) Do(ctx context.Context, o *KeyboardOption, oidcClient client.
u.Logger.V(1).Infof("exchanging the code and token")
tokenSet, err := oidcClient.ExchangeAuthCode(ctx, client.ExchangeAuthCodeInput{
Code: code,
PKCEParams: pkceParams,
Nonce: nonce,
RedirectURI: o.RedirectURL,
Code: code,
PKCEParams: pkceParams,
Nonce: nonce,
})
if err != nil {
return nil, fmt.Errorf("could not exchange the authorization code: %w", err)

View File

@@ -42,6 +42,7 @@ type Input struct {
IssuerURL string
ClientID string
ClientSecret string
RedirectURL string
ExtraScopes []string
UseAccessToken bool
PKCEMethod oidc.PKCEMethod
@@ -57,6 +58,7 @@ func (u Setup) Do(ctx context.Context, in Input) error {
IssuerURL: in.IssuerURL,
ClientID: in.ClientID,
ClientSecret: in.ClientSecret,
RedirectURL: in.RedirectURL,
ExtraScopes: in.ExtraScopes,
PKCEMethod: in.PKCEMethod,
UseAccessToken: in.UseAccessToken,