From 677ab3602040a05f335c651d1d8ae0b6938ec9a7 Mon Sep 17 00:00:00 2001 From: Abhisek Datta Date: Mon, 15 Apr 2024 22:41:09 +0530 Subject: [PATCH] feat: Add support for configurable prompt type for Google connector (#3475) Signed-off-by: abhisek Signed-off-by: Maksim Nabokikh Co-authored-by: Maksim Nabokikh --- connector/google/google.go | 14 +++++++- connector/google/google_test.go | 60 +++++++++++++++++++++++++++++++++ connector/oidc/oidc.go | 2 +- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/connector/google/google.go b/connector/google/google.go index d590867285..c2df11007c 100644 --- a/connector/google/google.go +++ b/connector/google/google.go @@ -58,6 +58,10 @@ type Config struct { // If this field is true, fetch direct group membership and transitive group membership FetchTransitiveGroupMembership bool `json:"fetchTransitiveGroupMembership"` + + // Optional value for the prompt parameter, defaults to consent when offline_access + // scope is requested + PromptType *string `json:"promptType"` } // Open returns a connector which can be used to login users through Google. @@ -107,6 +111,11 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e } } + promptType := "consent" + if c.PromptType != nil { + promptType = *c.PromptType + } + clientID := c.ClientID return &googleConnector{ redirectURI: c.RedirectURI, @@ -128,6 +137,7 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e domainToAdminEmail: c.DomainToAdminEmail, fetchTransitiveGroupMembership: c.FetchTransitiveGroupMembership, adminSrv: adminSrv, + promptType: promptType, }, nil } @@ -148,6 +158,7 @@ type googleConnector struct { domainToAdminEmail map[string]string fetchTransitiveGroupMembership bool adminSrv map[string]*admin.Service + promptType string } func (c *googleConnector) Close() error { @@ -170,8 +181,9 @@ func (c *googleConnector) LoginURL(s connector.Scopes, callbackURL, state string } if s.OfflineAccess { - opts = append(opts, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", "consent")) + opts = append(opts, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", c.promptType)) } + return c.oauth2Config.AuthCodeURL(state, opts...), nil } diff --git a/connector/google/google_test.go b/connector/google/google_test.go index aaefb87994..c56a5e78ba 100644 --- a/connector/google/google_test.go +++ b/connector/google/google_test.go @@ -6,6 +6,7 @@ import ( "fmt" "net/http" "net/http/httptest" + "net/url" "os" "testing" @@ -13,6 +14,8 @@ import ( "github.com/stretchr/testify/assert" admin "google.golang.org/api/admin/directory/v1" "google.golang.org/api/option" + + "github.com/dexidp/dex/connector" ) var ( @@ -291,3 +294,60 @@ func TestDomainToAdminEmailConfig(t *testing.T) { }) } } + +func TestPromptTypeConfig(t *testing.T) { + promptTypeLogin := "login" + cases := []struct { + name string + promptType *string + expectedPromptTypeValue string + }{ + { + name: "prompt type is nil", + promptType: nil, + expectedPromptTypeValue: "consent", + }, + { + name: "prompt type is empty", + promptType: new(string), + expectedPromptTypeValue: "", + }, + { + name: "prompt type is set", + promptType: &promptTypeLogin, + expectedPromptTypeValue: "login", + }, + } + + ts := testSetup() + defer ts.Close() + + serviceAccountFilePath, err := tempServiceAccountKey() + assert.Nil(t, err) + + os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", serviceAccountFilePath) + + for _, test := range cases { + t.Run(test.name, func(t *testing.T) { + conn, err := newConnector(&Config{ + ClientID: "testClient", + ClientSecret: "testSecret", + RedirectURI: ts.URL + "/callback", + Scopes: []string{"openid", "groups", "offline_access"}, + DomainToAdminEmail: map[string]string{"dexidp.com": "admin@dexidp.com"}, + PromptType: test.promptType, + }) + + assert.Nil(t, err) + assert.Equal(t, test.expectedPromptTypeValue, conn.promptType) + + loginURL, err := conn.LoginURL(connector.Scopes{OfflineAccess: true}, ts.URL+"/callback", "state") + assert.Nil(t, err) + + urlp, err := url.Parse(loginURL) + assert.Nil(t, err) + + assert.Equal(t, test.expectedPromptTypeValue, urlp.Query().Get("prompt")) + }) + } +} diff --git a/connector/oidc/oidc.go b/connector/oidc/oidc.go index c2918006c8..fd715970b1 100644 --- a/connector/oidc/oidc.go +++ b/connector/oidc/oidc.go @@ -75,7 +75,7 @@ type Config struct { UserNameKey string `json:"userNameKey"` - // PromptType will be used fot the prompt parameter (when offline_access, by default prompt=consent) + // PromptType will be used for the prompt parameter (when offline_access, by default prompt=consent) PromptType *string `json:"promptType"` // OverrideClaimMapping will be used to override the options defined in claimMappings.