diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index f6daf0d146..3877a53811 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -54,8 +54,8 @@ import ( gouuid "github.com/google/uuid" "github.com/markbates/goth" "github.com/markbates/goth/gothic" - goth_gitlab "github.com/markbates/goth/providers/github" - goth_github "github.com/markbates/goth/providers/gitlab" + goth_github "github.com/markbates/goth/providers/github" + goth_gitlab "github.com/markbates/goth/providers/gitlab" "github.com/santhosh-tekuri/jsonschema/v5" "github.com/stretchr/testify/assert" ) @@ -325,6 +325,13 @@ func authSourcePayloadOAuth2(name string) map[string]string { } } +func authSourcePayloadOpenIDConnect(name, appURL string) map[string]string { + payload := authSourcePayloadOAuth2(name) + payload["oauth2_provider"] = "openidConnect" + payload["open_id_connect_auto_discovery_url"] = appURL + ".well-known/openid-configuration" + return payload +} + func authSourcePayloadGitLab(name string) map[string]string { payload := authSourcePayloadOAuth2(name) payload["oauth2_provider"] = "gitlab" diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go index 5d5ea40e8b..83a92be83a 100644 --- a/tests/integration/oauth_test.go +++ b/tests/integration/oauth_test.go @@ -532,7 +532,8 @@ func TestSignInOAuthCallbackSignIn(t *testing.T) { assert.Greater(t, userAfterLogin.LastLoginUnix, userGitLab.LastLoginUnix) } -func TestSignInOAuthCallbackPKCE(t *testing.T) { +func TestSignInOAuthCallbackWithoutPKCEWhenUnsupported(t *testing.T) { + // https://codeberg.org/forgejo/forgejo/issues/4033 defer tests.PrepareTestEnv(t)() // Setup authentication source @@ -557,20 +558,12 @@ func TestSignInOAuthCallbackPKCE(t *testing.T) { resp := session.MakeRequest(t, req, http.StatusTemporaryRedirect) dest, err := url.Parse(resp.Header().Get("Location")) assert.NoError(t, err) - assert.Equal(t, "S256", dest.Query().Get("code_challenge_method")) - codeChallenge := dest.Query().Get("code_challenge") - assert.NotEmpty(t, codeChallenge) + assert.Empty(t, dest.Query().Get("code_challenge_method")) + assert.Empty(t, dest.Query().Get("code_challenge")) // callback (to check the initial code_challenge) defer mockCompleteUserAuth(func(res http.ResponseWriter, req *http.Request) (goth.User, error) { - codeVerifier := req.URL.Query().Get("code_verifier") - assert.NotEmpty(t, codeVerifier) - assert.Greater(t, len(codeVerifier), 40, codeVerifier) - - sha2 := sha256.New() - io.WriteString(sha2, codeVerifier) - assert.Equal(t, codeChallenge, base64.RawURLEncoding.EncodeToString(sha2.Sum(nil))) - + assert.Empty(t, req.URL.Query().Get("code_verifier")) return goth.User{ Provider: gitlabName, UserID: userGitLabUserID, @@ -583,6 +576,57 @@ func TestSignInOAuthCallbackPKCE(t *testing.T) { unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userGitLab.ID}) } +func TestSignInOAuthCallbackPKCE(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + // Setup authentication source + sourceName := "oidc" + authSource := addAuthSource(t, authSourcePayloadOpenIDConnect(sourceName, u.String())) + // Create a user as if it had been previously been created by the authentication source. + userID := "5678" + user := &user_model.User{ + Name: "oidc.user", + Email: "oidc.user@example.com", + Passwd: "oidc.userpassword", + Type: user_model.UserTypeIndividual, + LoginType: auth_model.OAuth2, + LoginSource: authSource.ID, + LoginName: userID, + } + defer createUser(context.Background(), t, user)() + + // initial redirection (to generate the code_challenge) + session := emptyTestSession(t) + req := NewRequest(t, "GET", fmt.Sprintf("/user/oauth2/%s", sourceName)) + resp := session.MakeRequest(t, req, http.StatusTemporaryRedirect) + dest, err := url.Parse(resp.Header().Get("Location")) + assert.NoError(t, err) + assert.Equal(t, "S256", dest.Query().Get("code_challenge_method")) + codeChallenge := dest.Query().Get("code_challenge") + assert.NotEmpty(t, codeChallenge) + + // callback (to check the initial code_challenge) + defer mockCompleteUserAuth(func(res http.ResponseWriter, req *http.Request) (goth.User, error) { + codeVerifier := req.URL.Query().Get("code_verifier") + assert.NotEmpty(t, codeVerifier) + assert.Greater(t, len(codeVerifier), 40, codeVerifier) + + sha2 := sha256.New() + io.WriteString(sha2, codeVerifier) + assert.Equal(t, codeChallenge, base64.RawURLEncoding.EncodeToString(sha2.Sum(nil))) + + return goth.User{ + Provider: sourceName, + UserID: userID, + Email: user.Email, + }, nil + })() + req = NewRequest(t, "GET", fmt.Sprintf("/user/oauth2/%s/callback?code=XYZ&state=XYZ", sourceName)) + resp = session.MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, "/", test.RedirectURL(resp)) + unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: user.ID}) + }) +} + func TestSignInOAuthCallbackRedirectToEscaping(t *testing.T) { defer tests.PrepareTestEnv(t)()