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-19 16:11:09 +00:00
parent b547fe0b37
commit 63727103db
21 changed files with 501 additions and 92 deletions
+18 -3
View File
@@ -632,10 +632,7 @@ func (l *LegacyProvider) convert() (Providers, error) {
ProtectedResource: l.ProtectedResource,
ValidateURL: l.ValidateURL,
Scope: l.Scope,
Prompt: l.Prompt,
ApprovalPrompt: l.ApprovalPrompt,
AllowedGroups: l.AllowedGroups,
AcrValues: l.AcrValues,
}
// This part is out of the switch section for all providers that support OIDC
@@ -708,6 +705,24 @@ func (l *LegacyProvider) convert() (Providers, error) {
provider.ID = l.ProviderType + "=" + l.ClientID
}
// handle AcrValues, Prompt and ApprovalPrompt
var urlParams []LoginURLParameter
if l.AcrValues != "" {
urlParams = append(urlParams, LoginURLParameter{Name: "acr_values", Default: []string{l.AcrValues}})
}
switch {
case l.Prompt != "":
urlParams = append(urlParams, LoginURLParameter{Name: "prompt", Default: []string{l.Prompt}})
case l.ApprovalPrompt != "":
urlParams = append(urlParams, LoginURLParameter{Name: "approval_prompt", Default: []string{l.ApprovalPrompt}})
default:
// match legacy behaviour by default - if neither prompt nor approval_prompt
// specified, use approval_prompt=force
urlParams = append(urlParams, LoginURLParameter{Name: "approval_prompt", Default: []string{"force"}})
}
provider.LoginURLParameters = urlParams
providers = append(providers, provider)
return providers, nil
+35 -6
View File
@@ -118,6 +118,9 @@ var _ = Describe("Legacy Options", func() {
opts.Providers[0].OIDCConfig.InsecureSkipNonce = true
opts.Providers[0].OIDCConfig.AudienceClaims = []string{"aud"}
opts.Providers[0].OIDCConfig.ExtraAudiences = []string{}
opts.Providers[0].LoginURLParameters = []LoginURLParameter{
{Name: "approval_prompt", Default: []string{"force"}},
}
converted, err := legacyOpts.ToOptions()
Expect(err).ToNot(HaveOccurred())
@@ -891,21 +894,41 @@ var _ = Describe("Legacy Options", func() {
// Non defaults for these options
clientID := "abcd"
defaultURLParams := []LoginURLParameter{
{Name: "approval_prompt", Default: []string{"force"}},
}
defaultProvider := Provider{
ID: "google=" + clientID,
ClientID: clientID,
Type: "google",
ID: "google=" + clientID,
ClientID: clientID,
Type: "google",
LoginURLParameters: defaultURLParams,
}
defaultLegacyProvider := LegacyProvider{
ClientID: clientID,
ProviderType: "google",
}
displayNameProvider := Provider{
ID: "displayName",
Name: "displayName",
defaultProviderWithPrompt := Provider{
ID: "google=" + clientID,
ClientID: clientID,
Type: "google",
LoginURLParameters: []LoginURLParameter{
{Name: "prompt", Default: []string{"switch_user"}},
},
}
defaultLegacyProviderWithPrompt := LegacyProvider{
ClientID: clientID,
ProviderType: "google",
Prompt: "switch_user",
}
displayNameProvider := Provider{
ID: "displayName",
Name: "displayName",
ClientID: clientID,
Type: "google",
LoginURLParameters: defaultURLParams,
}
displayNameLegacyProvider := LegacyProvider{
@@ -923,6 +946,7 @@ var _ = Describe("Legacy Options", func() {
ServiceAccountJSON: "test.json",
Groups: []string{"1", "2"},
},
LoginURLParameters: defaultURLParams,
}
internalConfigLegacyProvider := LegacyProvider{
@@ -950,6 +974,11 @@ var _ = Describe("Legacy Options", func() {
expectedProviders: Providers{defaultProvider},
errMsg: "",
}),
Entry("with prompt setting", &convertProvidersTableInput{
legacyProvider: defaultLegacyProviderWithPrompt,
expectedProviders: Providers{defaultProviderWithPrompt},
errMsg: "",
}),
Entry("with provider display name", &convertProvidersTableInput{
legacyProvider: displayNameLegacyProvider,
expectedProviders: Providers{displayNameProvider},
+101
View File
@@ -0,0 +1,101 @@
package options
// LoginURLParameter is the configuration for a single query parameter that
// can be passed through from the `/oauth2/start` endpoint to the IdP login
// URL. The "default" option specifies the default value or values (if any)
// that will be passed to the IdP for this parameter, and "allow" is a list
// of options for ways in which this parameter can be set or overridden via
// the query string to `/oauth2/start`.
// If _only_ a default is specified and no "allow" then the parameter is
// effectively fixed - the default value will always be used and anything
// passed to the start URL will be ignored. If _only_ "allow" is specified
// but no default then the parameter will only be passed on to the IdP if
// the caller provides it, and no value will be sent otherwise.
//
// Examples:
//
// A parameter whose value is fixed
//
// ```
// name: organization
// default:
// - myorg
// ```
//
// A parameter that is not passed by default, but may be set to one of a
// fixed set of values
//
// ```
// name: prompt
// allow:
// - value: login
// - value: consent
// - value: select_account
// ```
//
// A parameter that is passed by default but may be overridden by one of
// a fixed set of values
//
// ```
// name: prompt
// default: ["login"]
// allow:
// - value: consent
// - value: select_account
// ```
//
// A parameter that may be overridden, but only by values that match a
// regular expression. For example to restrict `login_hint` to email
// addresses in your organization's domain:
//
// ```
// name: login_hint
// allow:
// - pattern: '^[^@]*@example\.com$'
// # this allows at most one "@" sign, and requires "example.com" domain.
// ```
//
// Note that the YAML rules around exactly which characters are allowed
// and/or require escaping in different types of string literals are
// convoluted. For regular expressions the single quoted form is simplest
// as backslash is not considered to be an escape character. Alternatively
// use the "chomped block" format `|-`:
//
// ```
// - pattern: |-
// ^[^@]*@example\.com$
// ```
//
// The hyphen is important, a `|` block would have a trailing newline
// character.
type LoginURLParameter struct {
// Name specifies the name of the query parameter.
Name string `json:"name"`
// Default specifies a default value or values that will be
// passed to the IdP if not overridden.
//+optional
Default []string `json:"default,omitempty"`
// Allow specifies rules about how the default (if any) may be
// overridden via the query string to `/oauth2/start`. Only
// values that match one or more of the allow rules will be
// forwarded to the IdP.
//+optional
Allow []URLParameterRule `json:"allow,omitempty"`
}
// URLParameterRule represents a rule by which query parameters
// passed to the `/oauth2/start` endpoint are checked to determine whether
// they are valid overrides for the given parameter passed to the IdP's
// login URL. Either Value or Pattern should be supplied, not both.
type URLParameterRule struct {
// A Value rule matches just this specific value
Value *string `json:"value,omitempty"`
// A Pattern rule gives a regular expression that must be matched by
// some substring of the value. The expression is _not_ automatically
// anchored to the start and end of the value, if you _want_ to restrict
// the whole parameter value you must anchor it yourself with `^` and `$`.
Pattern *string `json:"pattern,omitempty"`
}
+3 -11
View File
@@ -62,6 +62,8 @@ type Provider struct {
// LoginURL is the authentication endpoint
LoginURL string `json:"loginURL,omitempty"`
// LoginURLParameters defines the parameters that can be passed from the start URL to the IdP login URL
LoginURLParameters []LoginURLParameter `json:"loginURLParameters,omitempty"`
// RedeemURL is the token redemption endpoint
RedeemURL string `json:"redeemURL,omitempty"`
// ProfileURL is the profile access endpoint
@@ -72,16 +74,8 @@ type Provider struct {
ValidateURL string `json:"validateURL,omitempty"`
// Scope is the OAuth scope specification
Scope string `json:"scope,omitempty"`
// Prompt is OIDC prompt
Prompt string `json:"prompt,omitempty"`
// ApprovalPrompt is the OAuth approval_prompt
// default is set to 'force'
ApprovalPrompt string `json:"approvalPrompt,omitempty"`
// AllowedGroups is a list of restrict logins to members of this group
AllowedGroups []string `json:"allowedGroups,omitempty"`
// AcrValues is a string of acr values
AcrValues string `json:"acrValues,omitempty"`
}
// ProviderType is used to enumerate the different provider type options
@@ -243,9 +237,7 @@ type LoginGovOptions struct {
func providerDefaults() Providers {
providers := Providers{
{
Type: "google",
Prompt: "", // Change to "login" when ApprovalPrompt officially deprecated
ApprovalPrompt: "force",
Type: "google",
AzureConfig: AzureOptions{
Tenant: "common",
},