1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-08-06 22:42:56 +02:00

Support for passing through URL query parameters from /oauth2/start to the ID provider's login URL.

You must explicitly configure oauth2-proxy (alpha config only) with which parameters are allowed to pass through, and optionally provide an allow-list of valid values and/or regular expressions for each one.  Note that this mechanism subsumes the functionality of the "prompt", "approval_prompt" and "acr_values" legacy configuration options, which must be converted to the equivalent YAML when running in alpha config mode.
This commit is contained in:
Ian Roberts
2022-02-16 16:18:51 +00:00
parent b547fe0b37
commit 63727103db
21 changed files with 501 additions and 92 deletions

View File

@ -7,6 +7,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
"regexp"
"strings"
"github.com/coreos/go-oidc/v3/oidc"
@ -32,15 +33,10 @@ type ProviderData struct {
ProfileURL *url.URL
ProtectedResource *url.URL
ValidateURL *url.URL
// Auth request params & related, see
//https://openid.net/specs/openid-connect-basic-1_0.html#rfc.section.2.1.1.1
AcrValues string
ApprovalPrompt string // NOTE: Renamed to "prompt" in OAuth2
ClientID string
ClientSecret string
ClientSecretFile string
Scope string
Prompt string
ClientID string
ClientSecret string
ClientSecretFile string
Scope string
// Common OIDC options for any OIDC-based providers to consume
AllowUnverifiedEmail bool
@ -54,6 +50,8 @@ type ProviderData struct {
AllowedGroups map[string]struct{}
getAuthorizationHeaderFunc func(string) http.Header
loginURLParameterDefaults url.Values
loginURLParameterOverrides map[string]*regexp.Regexp
}
// Data returns the ProviderData
@ -73,6 +71,100 @@ func (p *ProviderData) GetClientSecret() (clientSecret string, err error) {
return string(fileClientSecret), nil
}
// LoginURLParams returns the parameter values that should be passed to the IdP
// login URL. This is the default set of parameters configured for this provider,
// optionally overridden by the given overrides (typically from the URL of the
// /oauth2/start request) according to the configured rules for this provider.
func (p *ProviderData) LoginURLParams(overrides url.Values) url.Values {
// the returned url.Values may be modified later in the request handling process
// so shallow clone the default map
params := url.Values{}
for k, v := range p.loginURLParameterDefaults {
params[k] = v
}
if len(overrides) > 0 {
for param, re := range p.loginURLParameterOverrides {
if reqValues, ok := overrides[param]; ok {
actualValues := make([]string, 0, len(reqValues))
for _, val := range reqValues {
if re.MatchString(val) {
actualValues = append(actualValues, val)
}
}
if len(actualValues) > 0 {
params.Del(param)
params[param] = actualValues
}
}
}
}
return params
}
// Compile the given set of LoginURLParameter options into the internal defaults
// and regular expressions used to validate any overrides.
func (p *ProviderData) compileLoginParams(paramConfig []options.LoginURLParameter) []error {
var errs []error
p.loginURLParameterDefaults = url.Values{}
p.loginURLParameterOverrides = make(map[string]*regexp.Regexp)
for _, param := range paramConfig {
if p.seenParameter(param.Name) {
errs = append(errs, fmt.Errorf("parameter %s provided more than once in loginURLParameters", param.Name))
} else {
// record default if parameter declares one
if len(param.Default) > 0 {
p.loginURLParameterDefaults[param.Name] = param.Default
}
// record allow rules if any
if len(param.Allow) > 0 {
errs = p.convertAllowRules(errs, param)
}
}
}
return errs
}
// Converts the list of allow rules for the given parameter into a regexp
// and store it for use at runtime when validating overrides of that parameter.
func (p *ProviderData) convertAllowRules(errs []error, param options.LoginURLParameter) []error {
var allowREs []string
for idx, rule := range param.Allow {
if (rule.Value == nil) == (rule.Pattern == nil) {
errs = append(errs, fmt.Errorf("rule %d in LoginURLParameter %s must have exactly one of value or pattern", idx, param.Name))
} else {
allowREs = append(allowREs, regexpForRule(rule))
}
}
if re, err := regexp.Compile(strings.Join(allowREs, "|")); err != nil {
errs = append(errs, err)
} else {
p.loginURLParameterOverrides[param.Name] = re
}
return errs
}
// Check whether we have already processed a configuration for the given parameter name
func (p *ProviderData) seenParameter(name string) bool {
_, seenDefault := p.loginURLParameterDefaults[name]
_, seenOverride := p.loginURLParameterOverrides[name]
return seenDefault || seenOverride
}
// Generate a validating regular expression pattern for a given URLParameterRule.
// If the rule is for a fixed value then returns a regexp that matches exactly
// that value, if the rule is itself a regexp just use that as-is.
func regexpForRule(rule options.URLParameterRule) string {
if rule.Value != nil {
// convert literal value into an equivalent regexp,
// anchored at start and end
return "^" + regexp.QuoteMeta(*rule.Value) + "$"
}
// just use the pattern as-is, but wrap in a non-capture group
// to avoid any possibility of confusing the outer disjunction.
return "(?:" + *rule.Pattern + ")"
}
// setAllowedGroups organizes a group list into the AllowedGroups map
// to be consumed by Authorize implementations
func (p *ProviderData) setAllowedGroups(groups []string) {