From 081bb6839b245d8ee5c3402aba0bd62acc849406 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Thu, 4 Jul 2024 21:49:56 +0000 Subject: [PATCH 1/2] allow `token` parameter from the POST body As per https://github.com/go-gitea/gitea/pull/28390 this will be disallowed as a URL parameter for security reasons, but in the POST body it is secure and required for some flows. --- services/auth/oauth2.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index 46d8510143675..109ee5088b2c8 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -63,6 +63,14 @@ func (o *OAuth2) Name() string { // representing whether the token exists or not func parseToken(req *http.Request) (string, bool) { _ = req.ParseForm() + + // attempt to read the token from the POST body, which does not pose a security issue + // and is mandated for certain flows like OIDC introspection: + // https://datatracker.ietf.org/doc/html/rfc7662#section-2.1 + if token := req.PostForm.Get("token"); token != "" { + return token, true + } + if !setting.DisableQueryAuthToken { // Check token. if token := req.Form.Get("token"); token != "" { From 64c1beeaa2b48c552c40da7e3071ed6a1af81317 Mon Sep 17 00:00:00 2001 From: Shivaram Lingamneni Date: Mon, 15 Jul 2024 02:21:55 +0000 Subject: [PATCH 2/2] add an integration test for OIDC introspection --- tests/integration/oauth_test.go | 37 +++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/integration/oauth_test.go b/tests/integration/oauth_test.go index e9b69f5f149b3..c20bd01016d8d 100644 --- a/tests/integration/oauth_test.go +++ b/tests/integration/oauth_test.go @@ -419,3 +419,40 @@ func TestRefreshTokenInvalidation(t *testing.T) { assert.Equal(t, "unauthorized_client", string(parsedError.ErrorCode)) assert.Equal(t, "token was already used", parsedError.ErrorDescription) } + +func TestOAuthIntrospection(t *testing.T) { + defer tests.PrepareTestEnv(t)() + req := NewRequestWithValues(t, "POST", "/login/oauth/access_token", map[string]string{ + "grant_type": "authorization_code", + "client_id": "da7da3ba-9a13-4167-856f-3899de0b0138", + "client_secret": "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=", + "redirect_uri": "a", + "code": "authcode", + "code_verifier": "N1Zo9-8Rfwhkt68r1r29ty8YwIraXR8eh_1Qwxg7yQXsonBt", + }) + resp := MakeRequest(t, req, http.StatusOK) + type response struct { + AccessToken string `json:"access_token"` + TokenType string `json:"token_type"` + ExpiresIn int64 `json:"expires_in"` + RefreshToken string `json:"refresh_token"` + } + parsed := new(response) + + assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), parsed)) + assert.True(t, len(parsed.AccessToken) > 10) + assert.True(t, len(parsed.RefreshToken) > 10) + + req = NewRequestWithValues(t, "POST", "/login/oauth/introspect", map[string]string{ + "token": parsed.AccessToken, + }) + req.Header.Add("Authorization", "Basic ZGE3ZGEzYmEtOWExMy00MTY3LTg1NmYtMzg5OWRlMGIwMTM4OjRNSzhOYTZSNTVzbWRDWTBXdUNDdW1aNmhqUlBuR1k1c2FXVlJISGpKaUE9") + resp = MakeRequest(t, req, http.StatusOK) + type introspectResponse struct { + Active bool `json:"active"` + Scope string `json:"scope,omitempty"` + } + introspectParsed := new(introspectResponse) + assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), introspectParsed)) + assert.True(t, introspectParsed.Active) +}