From edf9c23d3a9d4d86f1a1393f2eb48259aee8489d Mon Sep 17 00:00:00 2001
From: Earl Warren <contact@earl-warren.org>
Date: Wed, 5 Jun 2024 17:50:15 +0200
Subject: [PATCH] test(oauth): RFC 6749 Section 10.2 conformance

See:

1b088fade6c69e63843d1bdf402454c363b22ce2 Prevent automatic OAuth grants for public clients
07fe5a8b1347bf62b3507c87d28b95ef58d6a162 use existing oauth grant for public client

(cherry picked from commit 592469464b74d3cd97d4c9ec0ee9dd392bb5b426)
---
 models/fixtures/oauth2_application.yml |  2 +-
 tests/integration/oauth_test.go        | 61 +++++++++++++++++++++++++-
 2 files changed, 61 insertions(+), 2 deletions(-)

diff --git a/models/fixtures/oauth2_application.yml b/models/fixtures/oauth2_application.yml
index 2f38cb58b6..beae9137ad 100644
--- a/models/fixtures/oauth2_application.yml
+++ b/models/fixtures/oauth2_application.yml
@@ -14,7 +14,7 @@
   name: "Test native app"
   client_id: "ce5a1322-42a7-11ed-b878-0242ac120002"
   client_secret: "$2a$10$UYRgUSgekzBp6hYe8pAdc.cgB4Gn06QRKsORUnIYTYQADs.YR/uvi" # bcrypt of "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=
-  redirect_uris: '["http://127.0.0.1"]'
+  redirect_uris: '["b", "http://127.0.0.1"]'
   created_unix: 1546869730
   updated_unix: 1546869730
   confidential_client: false
diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go
index 1da1c6f9c0..b198e6479b 100644
--- a/tests/integration/oauth_test.go
+++ b/tests/integration/oauth_test.go
@@ -79,6 +79,65 @@ func TestAuthorizeShow(t *testing.T) {
 	htmlDoc.GetCSRF()
 }
 
+func TestOAuth_AuthorizeConfidentialTwice(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	// da7da3ba-9a13-4167-856f-3899de0b0138 a confidential client in models/fixtures/oauth2_application.yml
+
+	// request authorization for the first time shows the grant page ...
+	authorizeURL := "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=a&response_type=code&state=thestate"
+	req := NewRequest(t, "GET", authorizeURL)
+	ctx := loginUser(t, "user4")
+	resp := ctx.MakeRequest(t, req, http.StatusOK)
+
+	htmlDoc := NewHTMLParser(t, resp.Body)
+	htmlDoc.AssertElement(t, "#authorize-app", true)
+
+	// ... and the user grants the authorization
+	req = NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{
+		"_csrf":        htmlDoc.GetCSRF(),
+		"client_id":    "da7da3ba-9a13-4167-856f-3899de0b0138",
+		"redirect_uri": "a",
+		"state":        "thestate",
+		"granted":      "true",
+	})
+	resp = ctx.MakeRequest(t, req, http.StatusSeeOther)
+	assert.Contains(t, test.RedirectURL(resp), "code=")
+
+	// request authorization the second time and the grant page is not shown again, redirection happens immediately
+	req = NewRequest(t, "GET", authorizeURL)
+	resp = ctx.MakeRequest(t, req, http.StatusSeeOther)
+	assert.Contains(t, test.RedirectURL(resp), "code=")
+}
+
+func TestOAuth_AuthorizePublicTwice(t *testing.T) {
+	defer tests.PrepareTestEnv(t)()
+
+	// ce5a1322-42a7-11ed-b878-0242ac120002 is a public client in models/fixtures/oauth2_application.yml
+	authorizeURL := "/login/oauth/authorize?client_id=ce5a1322-42a7-11ed-b878-0242ac120002&redirect_uri=b&response_type=code&code_challenge_method=plain&code_challenge=CODE&state=thestate"
+	ctx := loginUser(t, "user4")
+	// a public client must be authorized every time
+	for _, name := range []string{"First", "Second"} {
+		t.Run(name, func(t *testing.T) {
+			req := NewRequest(t, "GET", authorizeURL)
+			resp := ctx.MakeRequest(t, req, http.StatusOK)
+
+			htmlDoc := NewHTMLParser(t, resp.Body)
+			htmlDoc.AssertElement(t, "#authorize-app", true)
+
+			req = NewRequestWithValues(t, "POST", "/login/oauth/grant", map[string]string{
+				"_csrf":        htmlDoc.GetCSRF(),
+				"client_id":    "ce5a1322-42a7-11ed-b878-0242ac120002",
+				"redirect_uri": "b",
+				"state":        "thestate",
+				"granted":      "true",
+			})
+			resp = ctx.MakeRequest(t, req, http.StatusSeeOther)
+			assert.Contains(t, test.RedirectURL(resp), "code=")
+		})
+	}
+}
+
 func TestAuthorizeRedirectWithExistingGrant(t *testing.T) {
 	defer tests.PrepareTestEnv(t)()
 	req := NewRequest(t, "GET", "/login/oauth/authorize?client_id=da7da3ba-9a13-4167-856f-3899de0b0138&redirect_uri=https%3A%2F%2Fexample.com%2Fxyzzy&response_type=code&state=thestate")
@@ -480,7 +539,7 @@ func TestSignInOAuthCallbackRedirectToEscaping(t *testing.T) {
 	gitlab := addAuthSource(t, authSourcePayloadGitLabCustom(gitlabName))
 
 	//
-	// Create a user as if it had been previously been created by the GitLab
+	// Create a user as if it had been previously created by the GitLab
 	// authentication source.
 	//
 	userGitLabUserID := "5678"