Skip to content

Commit

Permalink
Add possibility to forward query params to idp (#454)
Browse files Browse the repository at this point in the history
  • Loading branch information
p53 authored Apr 30, 2024
1 parent 988bcbc commit 9dd9042
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 0 deletions.
1 change: 1 addition & 0 deletions pkg/apperrors/apperrors.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var (
ErrPKCEWithCodeOnly = errors.New("pkce can be enabled only with no-redirect=false")
ErrPKCECodeCreation = errors.New("creation of code verifier failed")
ErrPKCECookieEmpty = errors.New("seems that pkce code verifier cookie value is empty string")
ErrQueryParamValueMismatch = errors.New("query param value is not allowed")

ErrSessionExpiredVerifyOff = errors.New("the session has expired and verification switch off")
ErrSessionExpiredRefreshOff = errors.New("session expired and access token refreshing is disabled")
Expand Down
1 change: 1 addition & 0 deletions pkg/config/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type Configs interface {
GetHeaders() map[string]string
GetMatchClaims() map[string]string
GetTags() map[string]string
GetAllowedQueryParams() map[string]string
}

type CommonConfig struct{}
Expand Down
7 changes: 7 additions & 0 deletions pkg/google/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ type Config struct {
ResponseHeaders map[string]string `json:"response-headers" usage:"custom headers to added to the http response key=value" yaml:"response-headers"`
// CustomHTTPMethods is a list of additional non-standard http methods. If additional method is required it has to explicitly allowed at resource allowed method definition.
CustomHTTPMethods []string `json:"custom-http-methods" usage:"list of additional non-standard http methods" yaml:"custom-http-methods"`
// Allowed Query Params sent to IDP
AllowedQueryParams map[string]string `json:"allowed-query-params" usage:"allowed query params, sent to IDP key=optional value" yaml:"allowed-query-params"`

// EnableSelfSignedTLS indicates we should create a self-signed ceritificate for the service
EnabledSelfSignedTLS bool `env:"ENABLE_SELF_SIGNED_TLS" json:"enable-self-signed-tls" usage:"create self signed certificates for the proxy" yaml:"enable-self-signed-tls"`
Expand Down Expand Up @@ -332,6 +334,7 @@ func NewDefaultConfig() *Config {
EnableTokenHeader: true,
HTTPOnlyCookie: true,
Headers: make(map[string]string),
AllowedQueryParams: make(map[string]string),
LetsEncryptCacheDir: "./cache/",
MatchClaims: make(map[string]string),
MaxIdleConns: 100,
Expand Down Expand Up @@ -390,6 +393,10 @@ func (r *Config) GetTags() map[string]string {
return r.Tags
}

func (r *Config) GetAllowedQueryParams() map[string]string {
return r.AllowedQueryParams
}

// readConfigFile reads and parses the configuration file
func (r *Config) ReadConfigFile(filename string) error {
content, err := os.ReadFile(filename)
Expand Down
7 changes: 7 additions & 0 deletions pkg/keycloak/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ type Config struct {
ResponseHeaders map[string]string `json:"response-headers" usage:"custom headers to added to the http response key=value" yaml:"response-headers"`
// CustomHTTPMethods is a list of additional non-standard http methods. If additional method is required it has to explicitly allowed at resource allowed method definition.
CustomHTTPMethods []string `json:"custom-http-methods" usage:"list of additional non-standard http methods" yaml:"custom-http-methods"`
// Allowed Query Params sent to IDP
AllowedQueryParams map[string]string `json:"allowed-query-params" usage:"allowed query params, sent to IDP key=optional value" yaml:"allowed-query-params"`

// EnableSelfSignedTLS indicates we should create a self-signed ceritificate for the service
EnabledSelfSignedTLS bool `env:"ENABLE_SELF_SIGNED_TLS" json:"enable-self-signed-tls" usage:"create self signed certificates for the proxy" yaml:"enable-self-signed-tls"`
Expand Down Expand Up @@ -348,6 +350,7 @@ func NewDefaultConfig() *Config {
EnableIDPSessionCheck: true,
HTTPOnlyCookie: true,
Headers: make(map[string]string),
AllowedQueryParams: make(map[string]string),
LetsEncryptCacheDir: "./cache/",
MatchClaims: make(map[string]string),
MaxIdleConns: 100,
Expand Down Expand Up @@ -406,6 +409,10 @@ func (r *Config) GetTags() map[string]string {
return r.Tags
}

func (r *Config) GetAllowedQueryParams() map[string]string {
return r.AllowedQueryParams
}

// readConfigFile reads and parses the configuration file
func (r *Config) ReadConfigFile(filename string) error {
content, err := os.ReadFile(filename)
Expand Down
23 changes: 23 additions & 0 deletions pkg/keycloak/proxy/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ func getRedirectionURL(
}

// oauthAuthorizationHandler is responsible for performing the redirection to oauth provider
//
//nolint:cyclop
func oauthAuthorizationHandler(
logger *zap.Logger,
skipTokenVerification bool,
Expand All @@ -116,6 +118,7 @@ func oauthAuthorizationHandler(
newOAuth2Config func(redirectionURL string) *oauth2.Config,
getRedirectionURL func(wrt http.ResponseWriter, req *http.Request) string,
customSignInPage func(wrt http.ResponseWriter, authURL string),
allowedQueryParams map[string]string,
) func(wrt http.ResponseWriter, req *http.Request) {
return func(wrt http.ResponseWriter, req *http.Request) {
if skipTokenVerification {
Expand Down Expand Up @@ -161,6 +164,26 @@ func oauthAuthorizationHandler(
cookManager.DropPKCECookie(wrt, codeVerifier)
}

if len(allowedQueryParams) > 0 {
for key, val := range allowedQueryParams {
if param := req.URL.Query().Get(key); param != "" {
if val != "" {
if val != param {
logger.Error(
apperrors.ErrQueryParamValueMismatch.Error(),
zap.String("param", key),
)
return
}
}
authCodeOptions = append(
authCodeOptions,
oauth2.SetAuthURLParam(key, param),
)
}
}
}

authURL := conf.AuthCodeURL(
req.URL.Query().Get("state"),
authCodeOptions...,
Expand Down
19 changes: 19 additions & 0 deletions pkg/keycloak/proxy/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ func WithOAuthURI(baseURI string, oauthURI string) func(uri string) string {
}

// redirectToAuthorization redirects the user to authorization handler
//
//nolint:cyclop
func redirectToAuthorization(
logger *zap.Logger,
noRedirects bool,
Expand All @@ -179,6 +181,7 @@ func redirectToAuthorization(
noProxy bool,
baseURI string,
oAuthURI string,
allowedQueryParams map[string]string,
) func(wrt http.ResponseWriter, req *http.Request) context.Context {
return func(wrt http.ResponseWriter, req *http.Request) context.Context {
if noRedirects {
Expand All @@ -190,6 +193,22 @@ func redirectToAuthorization(
uuid := cookManager.DropStateParameterCookie(req, wrt)
authQuery := fmt.Sprintf("?state=%s", uuid)

if len(allowedQueryParams) > 0 {
query := ""
for key, val := range allowedQueryParams {
if param := req.URL.Query().Get(key); param != "" {
if val != "" {
if val != param {
wrt.WriteHeader(http.StatusForbidden)
return revokeProxy(logger, req)
}
}
query += fmt.Sprintf("&%s=%s", key, param)
}
}
authQuery += query
}

// step: if verification is switched off, we can't authorization
if skipTokenVerification {
logger.Error(
Expand Down
2 changes: 2 additions & 0 deletions pkg/keycloak/proxy/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ func (r *OauthProxy) CreateReverseProxy() error {
r.Config.NoProxy,
r.Config.BaseURI,
r.Config.OAuthURI,
r.Config.AllowedQueryParams,
)

if r.Config.EnableHmac {
Expand Down Expand Up @@ -532,6 +533,7 @@ func (r *OauthProxy) CreateReverseProxy() error {
r.newOAuth2Config,
r.getRedirectionURL,
r.customSignInPage,
r.Config.AllowedQueryParams,
)

// step: add the routing for oauth
Expand Down
8 changes: 8 additions & 0 deletions pkg/proxy/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@ func parseCLIOptions(cliCtx *cli.Context, config core.Configs) error {
utils.MergeMaps(config.GetHeaders(), headers)
}

if cliCtx.IsSet("allowed-query-params") {
headers, err := utils.DecodeKeyPairs(cliCtx.StringSlice("allowed-query-params"))
if err != nil {
return err
}
utils.MergeMaps(config.GetAllowedQueryParams(), headers)
}

if cliCtx.IsSet("resources") {
for _, x := range cliCtx.StringSlice("resources") {
resource, err := authorization.NewResource().Parse(x)
Expand Down

0 comments on commit 9dd9042

Please sign in to comment.