From 204dbb2e3ff7692af3b7ca4362b1ee46fb43c227 Mon Sep 17 00:00:00 2001 From: Mathias Gebbe Date: Thu, 26 Feb 2026 16:09:07 +0100 Subject: [PATCH 1/2] fix(connector): update authproxy and oauth to match CallbackConnector interface (#4589) The PKCE support added in v2.45.0 changed the CallbackConnector interface signatures but missed updating the authproxy and oauth connectors. This caused a type assertion failure in handleConnectorLogin(), resulting in "Requested resource does not exist" errors when using these connectors. Update LoginURL to return (string, []byte, error) and HandleCallback to accept a []byte connData parameter for both connectors and their tests. Signed-off-by: Mathias Gebbe --- connector/authproxy/authproxy.go | 8 ++++---- connector/authproxy/authproxy_test.go | 12 ++++++------ connector/oauth/oauth.go | 8 ++++---- connector/oauth/oauth_test.go | 10 +++++----- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/connector/authproxy/authproxy.go b/connector/authproxy/authproxy.go index f3d87fcb..3fe451b2 100644 --- a/connector/authproxy/authproxy.go +++ b/connector/authproxy/authproxy.go @@ -83,20 +83,20 @@ type callback struct { } // LoginURL returns the URL to redirect the user to login with. -func (m *callback) LoginURL(s connector.Scopes, callbackURL, state string) (string, error) { +func (m *callback) LoginURL(s connector.Scopes, callbackURL, state string) (string, []byte, error) { u, err := url.Parse(callbackURL) if err != nil { - return "", fmt.Errorf("failed to parse callbackURL %q: %v", callbackURL, err) + return "", nil, fmt.Errorf("failed to parse callbackURL %q: %v", callbackURL, err) } u.Path += m.pathSuffix v := u.Query() v.Set("state", state) u.RawQuery = v.Encode() - return u.String(), nil + return u.String(), nil, nil } // HandleCallback parses the request and returns the user's identity -func (m *callback) HandleCallback(s connector.Scopes, r *http.Request) (connector.Identity, error) { +func (m *callback) HandleCallback(s connector.Scopes, _ []byte, r *http.Request) (connector.Identity, error) { remoteUser := r.Header.Get(m.userHeader) if remoteUser == "" { return connector.Identity{}, fmt.Errorf("required HTTP header %s is not set", m.userHeader) diff --git a/connector/authproxy/authproxy_test.go b/connector/authproxy/authproxy_test.go index fbdd2a5d..bd8b4f36 100644 --- a/connector/authproxy/authproxy_test.go +++ b/connector/authproxy/authproxy_test.go @@ -36,7 +36,7 @@ func TestUser(t *testing.T) { "X-Remote-User": {testUsername}, } - ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req) + ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, nil, req) expectNil(t, err) // If not specified, the userID and email should fall back to the remote user @@ -62,7 +62,7 @@ func TestExtraHeaders(t *testing.T) { "X-Remote-User-Email": {testEmail}, } - ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req) + ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, nil, req) expectNil(t, err) expectEquals(t, ident.UserID, testUserID) @@ -85,7 +85,7 @@ func TestSingleGroup(t *testing.T) { "X-Remote-Group": {testGroup1}, } - ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req) + ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, nil, req) expectNil(t, err) expectEquals(t, ident.UserID, testEmail) @@ -106,7 +106,7 @@ func TestMultipleGroup(t *testing.T) { "X-Remote-Group": {testGroup1 + ", " + testGroup2 + ", " + testGroup3 + ", " + testGroup4}, } - ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req) + ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, nil, req) expectNil(t, err) expectEquals(t, ident.UserID, testEmail) @@ -132,7 +132,7 @@ func TestMultipleGroupWithCustomSeparator(t *testing.T) { "X-Remote-Group": {testGroup1 + ";" + testGroup2 + ";" + testGroup3 + ";" + testGroup4}, } - ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req) + ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, nil, req) expectNil(t, err) expectEquals(t, ident.UserID, testEmail) @@ -158,7 +158,7 @@ func TestStaticGroup(t *testing.T) { "X-Remote-Group": {testGroup1 + ", " + testGroup2 + ", " + testGroup3 + ", " + testGroup4}, } - ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req) + ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, nil, req) expectNil(t, err) expectEquals(t, ident.UserID, testEmail) diff --git a/connector/oauth/oauth.go b/connector/oauth/oauth.go index 413a813a..7661a9f8 100644 --- a/connector/oauth/oauth.go +++ b/connector/oauth/oauth.go @@ -116,9 +116,9 @@ func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, erro return oauthConn, err } -func (c *oauthConnector) LoginURL(scopes connector.Scopes, callbackURL, state string) (string, error) { +func (c *oauthConnector) LoginURL(scopes connector.Scopes, callbackURL, state string) (string, []byte, error) { if c.redirectURI != callbackURL { - return "", fmt.Errorf("expected callback URL %q did not match the URL in the config %q", callbackURL, c.redirectURI) + return "", nil, fmt.Errorf("expected callback URL %q did not match the URL in the config %q", callbackURL, c.redirectURI) } oauth2Config := &oauth2.Config{ @@ -129,10 +129,10 @@ func (c *oauthConnector) LoginURL(scopes connector.Scopes, callbackURL, state st Scopes: c.scopes, } - return oauth2Config.AuthCodeURL(state), nil + return oauth2Config.AuthCodeURL(state), nil, nil } -func (c *oauthConnector) HandleCallback(s connector.Scopes, r *http.Request) (identity connector.Identity, err error) { +func (c *oauthConnector) HandleCallback(s connector.Scopes, _ []byte, r *http.Request) (identity connector.Identity, err error) { q := r.URL.Query() if errType := q.Get("error"); errType != "" { return identity, errors.New(q.Get("error_description")) diff --git a/connector/oauth/oauth_test.go b/connector/oauth/oauth_test.go index 2f6b0b95..cdd2d3c6 100644 --- a/connector/oauth/oauth_test.go +++ b/connector/oauth/oauth_test.go @@ -50,7 +50,7 @@ func TestLoginURL(t *testing.T) { conn := newConnector(t, testServer.URL) - loginURL, err := conn.LoginURL(connector.Scopes{}, conn.redirectURI, "some-state") + loginURL, _, err := conn.LoginURL(connector.Scopes{}, conn.redirectURI, "some-state") assert.Equal(t, err, nil) expectedURL, err := url.Parse(testServer.URL + "/authorize") @@ -86,7 +86,7 @@ func TestHandleCallBackForGroupsInUserInfo(t *testing.T) { conn := newConnector(t, testServer.URL) req := newRequestWithAuthCode(t, testServer.URL, "TestHandleCallBackForGroupsInUserInfo") - identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, req) + identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, nil, req) assert.Equal(t, err, nil) sort.Strings(identity.Groups) @@ -122,7 +122,7 @@ func TestHandleCallBackForGroupMapsInUserInfo(t *testing.T) { conn := newConnector(t, testServer.URL) req := newRequestWithAuthCode(t, testServer.URL, "TestHandleCallBackForGroupMapsInUserInfo") - identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, req) + identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, nil, req) assert.Equal(t, err, nil) sort.Strings(identity.Groups) @@ -156,7 +156,7 @@ func TestHandleCallBackForGroupsInToken(t *testing.T) { conn := newConnector(t, testServer.URL) req := newRequestWithAuthCode(t, testServer.URL, "TestHandleCallBackForGroupsInToken") - identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, req) + identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, nil, req) assert.Equal(t, err, nil) assert.Equal(t, len(identity.Groups), 1) @@ -186,7 +186,7 @@ func TestHandleCallbackForNumericUserID(t *testing.T) { conn := newConnector(t, testServer.URL) req := newRequestWithAuthCode(t, testServer.URL, "TestHandleCallbackForNumericUserID") - identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, req) + identity, err := conn.HandleCallback(connector.Scopes{Groups: true}, nil, req) assert.Equal(t, err, nil) assert.Equal(t, identity.UserID, "1000") From eaa45e2ca59df692d2b9d7ffcd0830d8907f5e60 Mon Sep 17 00:00:00 2001 From: Michiel De Backker Date: Tue, 24 Feb 2026 13:56:32 +0100 Subject: [PATCH 2/2] fix(mysql): quote `groups` reserved word in query replacer (#4580) `groups` is a reserved word in MySQL >= 8.0.2, causing migration 13 to fail with a syntax error on `ALTER TABLE password ADD COLUMN groups`. Fixes #4579 Signed-off-by: Michiel De Backker --- storage/sql/sql.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/sql/sql.go b/storage/sql/sql.go index d671021f..1965f5ff 100644 --- a/storage/sql/sql.go +++ b/storage/sql/sql.go @@ -95,7 +95,7 @@ var ( // For compound indexes (with two keys) even less. {matchLiteral("text"), "varchar(384)"}, // Quote keywords and reserved words used as identifiers. - {regexp.MustCompile(`\b(keys)\b`), "`$1`"}, + {regexp.MustCompile(`\b(keys|groups)\b`), "`$1`"}, // Change default timestamp to fit datetime. {regexp.MustCompile(`0001-01-01 00:00:00 UTC`), "1000-01-01 00:00:00"}, },