@@ -7,6 +7,7 @@ import (
7 "io/ioutil"
7 "io/ioutil"
12 "github.com/coreos/go-oidc/v3/oidc"
13 "github.com/coreos/go-oidc/v3/oidc"
@@ -32,15 +33,10 @@ type ProviderData struct {
32 ProfileURL *url.URL
33 ProfileURL *url.URL
33 ProtectedResource *url.URL
34 ProtectedResource *url.URL
34 ValidateURL *url.URL
35 ValidateURL *url.URL
35− // Auth request params & related, see
36− //https://openid.net/specs/openid-connect-basic-1_0.html#rfc.section.2.1.1.1
38− ApprovalPrompt string // NOTE: Renamed to "prompt" in OAuth2
39− ClientID string
36+ ClientID string
40− ClientSecret string
37+ ClientSecret string
41− ClientSecretFile string
38+ ClientSecretFile string
43− Prompt string
39+ Scope string
45 // Common OIDC options for any OIDC-based providers to consume
41 // Common OIDC options for any OIDC-based providers to consume
46 AllowUnverifiedEmail bool
42 AllowUnverifiedEmail bool
@@ -54,6 +50,8 @@ type ProviderData struct {
54 AllowedGroups map[string]struct{}
50 AllowedGroups map[string]struct{}
56 getAuthorizationHeaderFunc func(string) http.Header
52 getAuthorizationHeaderFunc func(string) http.Header
53+ loginURLParameterDefaults url.Values
54+ loginURLParameterOverrides map[string]*regexp.Regexp
59// Data returns the ProviderData
57// Data returns the ProviderData
@@ -73,6 +71,100 @@ func (p *ProviderData) GetClientSecret() (clientSecret string, err error) {
73 return string(fileClientSecret), nil
71 return string(fileClientSecret), nil
74+// LoginURLParams returns the parameter values that should be passed to the IdP
75+// login URL. This is the default set of parameters configured for this provider,
76+// optionally overridden by the given overrides (typically from the URL of the
77+// /oauth2/start request) according to the configured rules for this provider.
78+func (p *ProviderData) LoginURLParams(overrides url.Values) url.Values {
79+ // the returned url.Values may be modified later in the request handling process
80+ // so shallow clone the default map
81+ params := url.Values{}
82+ for k, v := range p.loginURLParameterDefaults {
85+ if len(overrides) > 0 {
86+ for param, re := range p.loginURLParameterOverrides {
87+ if reqValues, ok := overrides[param]; ok {
88+ actualValues := make([]string, 0, len(reqValues))
89+ for _, val := range reqValues {
90+ if re.MatchString(val) {
91+ actualValues = append(actualValues, val)
94+ if len(actualValues) > 0 {
96+ params[param] = actualValues
104+// Compile the given set of LoginURLParameter options into the internal defaults
105+// and regular expressions used to validate any overrides.
106+func (p *ProviderData) compileLoginParams(paramConfig []options.LoginURLParameter) []error {
108+ p.loginURLParameterDefaults = url.Values{}
109+ p.loginURLParameterOverrides = make(map[string]*regexp.Regexp)
111+ for _, param := range paramConfig {
112+ if p.seenParameter(param.Name) {
113+ errs = append(errs, fmt.Errorf("parameter %s provided more than once in loginURLParameters", param.Name))
115+ // record default if parameter declares one
116+ if len(param.Default) > 0 {
117+ p.loginURLParameterDefaults[param.Name] = param.Default
119+ // record allow rules if any
120+ if len(param.Allow) > 0 {
121+ errs = p.convertAllowRules(errs, param)
128+// Converts the list of allow rules for the given parameter into a regexp
129+// and store it for use at runtime when validating overrides of that parameter.
130+func (p *ProviderData) convertAllowRules(errs []error, param options.LoginURLParameter) []error {
131+ var allowREs []string
132+ for idx, rule := range param.Allow {
133+ if (rule.Value == nil) == (rule.Pattern == nil) {
134+ errs = append(errs, fmt.Errorf("rule %d in LoginURLParameter %s must have exactly one of value or pattern", idx, param.Name))
136+ allowREs = append(allowREs, regexpForRule(rule))
139+ if re, err := regexp.Compile(strings.Join(allowREs, "|")); err != nil {
140+ errs = append(errs, err)
142+ p.loginURLParameterOverrides[param.Name] = re
147+// Check whether we have already processed a configuration for the given parameter name
148+func (p *ProviderData) seenParameter(name string) bool {
149+ _, seenDefault := p.loginURLParameterDefaults[name]
150+ _, seenOverride := p.loginURLParameterOverrides[name]
151+ return seenDefault || seenOverride
154+// Generate a validating regular expression pattern for a given URLParameterRule.
155+// If the rule is for a fixed value then returns a regexp that matches exactly
156+// that value, if the rule is itself a regexp just use that as-is.
157+func regexpForRule(rule options.URLParameterRule) string {
158+ if rule.Value != nil {
159+ // convert literal value into an equivalent regexp,
160+ // anchored at start and end
161+ return "^" + regexp.QuoteMeta(*rule.Value) + "$"
163+ // just use the pattern as-is, but wrap in a non-capture group
164+ // to avoid any possibility of confusing the outer disjunction.
165+ return "(?:" + *rule.Pattern + ")"
76// setAllowedGroups organizes a group list into the AllowedGroups map
168// setAllowedGroups organizes a group list into the AllowedGroups map
77// to be consumed by Authorize implementations
169// to be consumed by Authorize implementations
78func (p *ProviderData) setAllowedGroups(groups []string) {
170func (p *ProviderData) setAllowedGroups(groups []string) {