test: pkce only for OpenID Connect

This commit is contained in:
oliverpool 2024-06-10 10:04:00 +02:00
parent d4af96647e
commit 985939c145
2 changed files with 65 additions and 14 deletions

View file

@ -54,8 +54,8 @@ import (
gouuid "github.com/google/uuid" gouuid "github.com/google/uuid"
"github.com/markbates/goth" "github.com/markbates/goth"
"github.com/markbates/goth/gothic" "github.com/markbates/goth/gothic"
goth_gitlab "github.com/markbates/goth/providers/github" goth_github "github.com/markbates/goth/providers/github"
goth_github "github.com/markbates/goth/providers/gitlab" goth_gitlab "github.com/markbates/goth/providers/gitlab"
"github.com/santhosh-tekuri/jsonschema/v5" "github.com/santhosh-tekuri/jsonschema/v5"
"github.com/stretchr/testify/assert" "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 { func authSourcePayloadGitLab(name string) map[string]string {
payload := authSourcePayloadOAuth2(name) payload := authSourcePayloadOAuth2(name)
payload["oauth2_provider"] = "gitlab" payload["oauth2_provider"] = "gitlab"

View file

@ -532,7 +532,8 @@ func TestSignInOAuthCallbackSignIn(t *testing.T) {
assert.Greater(t, userAfterLogin.LastLoginUnix, userGitLab.LastLoginUnix) 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)() defer tests.PrepareTestEnv(t)()
// Setup authentication source // Setup authentication source
@ -557,20 +558,12 @@ func TestSignInOAuthCallbackPKCE(t *testing.T) {
resp := session.MakeRequest(t, req, http.StatusTemporaryRedirect) resp := session.MakeRequest(t, req, http.StatusTemporaryRedirect)
dest, err := url.Parse(resp.Header().Get("Location")) dest, err := url.Parse(resp.Header().Get("Location"))
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, "S256", dest.Query().Get("code_challenge_method")) assert.Empty(t, dest.Query().Get("code_challenge_method"))
codeChallenge := dest.Query().Get("code_challenge") assert.Empty(t, dest.Query().Get("code_challenge"))
assert.NotEmpty(t, codeChallenge)
// callback (to check the initial code_challenge) // callback (to check the initial code_challenge)
defer mockCompleteUserAuth(func(res http.ResponseWriter, req *http.Request) (goth.User, error) { defer mockCompleteUserAuth(func(res http.ResponseWriter, req *http.Request) (goth.User, error) {
codeVerifier := req.URL.Query().Get("code_verifier") assert.Empty(t, 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{ return goth.User{
Provider: gitlabName, Provider: gitlabName,
UserID: userGitLabUserID, UserID: userGitLabUserID,
@ -583,6 +576,57 @@ func TestSignInOAuthCallbackPKCE(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userGitLab.ID}) 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) { func TestSignInOAuthCallbackRedirectToEscaping(t *testing.T) {
defer tests.PrepareTestEnv(t)() defer tests.PrepareTestEnv(t)()