Skip to content

Commit e31f224

Browse files
authored
Make OAuth2 issuer configurable (#35915)
The new (correct) behavior breaks the old (incorrect) logins. Add a config option to support legacy "issuer". Fix #35830
1 parent 1c8c565 commit e31f224

File tree

4 files changed

+35
-13
lines changed

4 files changed

+35
-13
lines changed

custom/conf/app.example.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,11 @@ ENABLED = true
567567
;; Alternative location to specify OAuth2 authentication secret. You cannot specify both this and JWT_SECRET, and must pick one
568568
;JWT_SECRET_URI = file:/etc/gitea/oauth2_jwt_secret
569569
;;
570+
;; The "issuer" claim identifies the principal that issued the JWT.
571+
;; Gitea 1.25 makes it default to "ROOT_URL without the last slash" to follow the standard.
572+
;; If you have old logins from before 1.25, you may want to set it to the old (non-standard) value "ROOT_URL with the last slash".
573+
;JWT_CLAIM_ISSUER =
574+
;;
570575
;; Lifetime of an OAuth2 access token in seconds
571576
;ACCESS_TOKEN_EXPIRATION_TIME = 3600
572577
;;

modules/setting/oauth2.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ var OAuth2 = struct {
9696
InvalidateRefreshTokens bool
9797
JWTSigningAlgorithm string `ini:"JWT_SIGNING_ALGORITHM"`
9898
JWTSigningPrivateKeyFile string `ini:"JWT_SIGNING_PRIVATE_KEY_FILE"`
99+
JWTClaimIssuer string `ini:"JWT_CLAIM_ISSUER"`
99100
MaxTokenLength int
100101
DefaultApplications []string
101102
}{

services/oauth2_provider/access_token.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,12 @@ func NewJwtRegisteredClaimsFromUser(clientID string, grantUserID int64, exp *jwt
112112
// to retrieve the configuration information. This MUST also be identical to the "iss" Claim value in ID Tokens issued from this Issuer.
113113
// * https://accounts.google.com/.well-known/openid-configuration
114114
// * https://github.com/login/oauth/.well-known/openid-configuration
115+
issuer := setting.OAuth2.JWTClaimIssuer
116+
if issuer == "" {
117+
issuer = strings.TrimSuffix(setting.AppURL, "/")
118+
}
115119
return jwt.RegisteredClaims{
116-
Issuer: strings.TrimSuffix(setting.AppURL, "/"),
120+
Issuer: issuer,
117121
Audience: []string{clientID},
118122
Subject: strconv.FormatInt(grantUserID, 10),
119123
ExpiresAt: exp,

tests/integration/oauth_test.go

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -919,20 +919,32 @@ func TestOAuth_GrantScopesClaimAllGroups(t *testing.T) {
919919
}
920920

921921
func testOAuth2WellKnown(t *testing.T) {
922+
defer test.MockVariableValue(&setting.AppURL, "https://try.gitea.io/")()
922923
urlOpenidConfiguration := "/.well-known/openid-configuration"
923924

924-
defer test.MockVariableValue(&setting.AppURL, "https://try.gitea.io/")()
925-
req := NewRequest(t, "GET", urlOpenidConfiguration)
926-
resp := MakeRequest(t, req, http.StatusOK)
927-
var respMap map[string]any
928-
DecodeJSON(t, resp, &respMap)
929-
assert.Equal(t, "https://try.gitea.io", respMap["issuer"])
930-
assert.Equal(t, "https://try.gitea.io/login/oauth/authorize", respMap["authorization_endpoint"])
931-
assert.Equal(t, "https://try.gitea.io/login/oauth/access_token", respMap["token_endpoint"])
932-
assert.Equal(t, "https://try.gitea.io/login/oauth/keys", respMap["jwks_uri"])
933-
assert.Equal(t, "https://try.gitea.io/login/oauth/userinfo", respMap["userinfo_endpoint"])
934-
assert.Equal(t, "https://try.gitea.io/login/oauth/introspect", respMap["introspection_endpoint"])
935-
assert.Equal(t, []any{"RS256"}, respMap["id_token_signing_alg_values_supported"])
925+
t.Run("WellKnown", func(t *testing.T) {
926+
req := NewRequest(t, "GET", urlOpenidConfiguration)
927+
resp := MakeRequest(t, req, http.StatusOK)
928+
var respMap map[string]any
929+
DecodeJSON(t, resp, &respMap)
930+
assert.Equal(t, "https://try.gitea.io", respMap["issuer"])
931+
assert.Equal(t, "https://try.gitea.io/login/oauth/authorize", respMap["authorization_endpoint"])
932+
assert.Equal(t, "https://try.gitea.io/login/oauth/access_token", respMap["token_endpoint"])
933+
assert.Equal(t, "https://try.gitea.io/login/oauth/keys", respMap["jwks_uri"])
934+
assert.Equal(t, "https://try.gitea.io/login/oauth/userinfo", respMap["userinfo_endpoint"])
935+
assert.Equal(t, "https://try.gitea.io/login/oauth/introspect", respMap["introspection_endpoint"])
936+
assert.Equal(t, []any{"RS256"}, respMap["id_token_signing_alg_values_supported"])
937+
})
938+
939+
t.Run("WellKnownWithIssuer", func(t *testing.T) {
940+
defer test.MockVariableValue(&setting.OAuth2.JWTClaimIssuer, "https://try.gitea.io/")()
941+
req := NewRequest(t, "GET", urlOpenidConfiguration)
942+
resp := MakeRequest(t, req, http.StatusOK)
943+
var respMap map[string]any
944+
DecodeJSON(t, resp, &respMap)
945+
assert.Equal(t, "https://try.gitea.io/", respMap["issuer"]) // has trailing by JWTClaimIssuer
946+
assert.Equal(t, "https://try.gitea.io/login/oauth/authorize", respMap["authorization_endpoint"])
947+
})
936948

937949
defer test.MockVariableValue(&setting.OAuth2.Enabled, false)()
938950
MakeRequest(t, NewRequest(t, "GET", urlOpenidConfiguration), http.StatusNotFound)

0 commit comments

Comments
 (0)