Skip to content

Commit effb405

Browse files
authored
Always load or generate oauth2 jwt secret (#30942)
Fix #30923
1 parent f4f4e18 commit effb405

File tree

3 files changed

+44
-12
lines changed

3 files changed

+44
-12
lines changed

modules/setting/oauth2.go

+6-11
Original file line numberDiff line numberDiff line change
@@ -126,16 +126,15 @@ func loadOAuth2From(rootCfg ConfigProvider) {
126126
OAuth2.Enabled = sec.Key("ENABLE").MustBool(OAuth2.Enabled)
127127
}
128128

129-
if !OAuth2.Enabled {
130-
return
131-
}
132-
133-
jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET")
134-
135129
if !filepath.IsAbs(OAuth2.JWTSigningPrivateKeyFile) {
136130
OAuth2.JWTSigningPrivateKeyFile = filepath.Join(AppDataPath, OAuth2.JWTSigningPrivateKeyFile)
137131
}
138132

133+
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
134+
// Because this secret is also used as GeneralTokenSigningSecret (as a quick not-that-breaking fix for some legacy problems).
135+
// Including: CSRF token, account validation token, etc ...
136+
// In main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
137+
jwtSecretBase64 := loadSecret(sec, "JWT_SECRET_URI", "JWT_SECRET")
139138
if InstallLock {
140139
jwtSecretBytes, err := generate.DecodeJwtSecretBase64(jwtSecretBase64)
141140
if err != nil {
@@ -157,20 +156,16 @@ func loadOAuth2From(rootCfg ConfigProvider) {
157156
}
158157
}
159158

160-
// generalSigningSecret is used as container for a []byte value
161-
// instead of an additional mutex, we use CompareAndSwap func to change the value thread save
162159
var generalSigningSecret atomic.Pointer[[]byte]
163160

164161
func GetGeneralTokenSigningSecret() []byte {
165162
old := generalSigningSecret.Load()
166163
if old == nil || len(*old) == 0 {
167164
jwtSecret, _, err := generate.NewJwtSecretWithBase64()
168165
if err != nil {
169-
log.Fatal("Unable to generate general JWT secret: %s", err.Error())
166+
log.Fatal("Unable to generate general JWT secret: %v", err)
170167
}
171168
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
172-
// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
173-
LogStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
174169
return jwtSecret
175170
}
176171
return *generalSigningSecret.Load()

modules/setting/oauth2_test.go

+27-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package setting
55

66
import (
7+
"os"
78
"testing"
89

910
"code.gitea.io/gitea/modules/generate"
@@ -14,7 +15,7 @@ import (
1415

1516
func TestGetGeneralSigningSecret(t *testing.T) {
1617
// when there is no general signing secret, it should be generated, and keep the same value
17-
assert.Nil(t, generalSigningSecret.Load())
18+
generalSigningSecret.Store(nil)
1819
s1 := GetGeneralTokenSigningSecret()
1920
assert.NotNil(t, s1)
2021
s2 := GetGeneralTokenSigningSecret()
@@ -33,6 +34,31 @@ JWT_SECRET = BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
3334
assert.EqualValues(t, expected, actual)
3435
}
3536

37+
func TestGetGeneralSigningSecretSave(t *testing.T) {
38+
defer test.MockVariableValue(&InstallLock, true)()
39+
40+
old := GetGeneralTokenSigningSecret()
41+
assert.Len(t, old, 32)
42+
43+
tmpFile := t.TempDir() + "/app.ini"
44+
_ = os.WriteFile(tmpFile, nil, 0o644)
45+
cfg, _ := NewConfigProviderFromFile(tmpFile)
46+
loadOAuth2From(cfg)
47+
generated := GetGeneralTokenSigningSecret()
48+
assert.Len(t, generated, 32)
49+
assert.NotEqual(t, old, generated)
50+
51+
generalSigningSecret.Store(nil)
52+
cfg, _ = NewConfigProviderFromFile(tmpFile)
53+
loadOAuth2From(cfg)
54+
again := GetGeneralTokenSigningSecret()
55+
assert.Equal(t, generated, again)
56+
57+
iniContent, err := os.ReadFile(tmpFile)
58+
assert.NoError(t, err)
59+
assert.Contains(t, string(iniContent), "JWT_SECRET = ")
60+
}
61+
3662
func TestOauth2DefaultApplications(t *testing.T) {
3763
cfg, _ := NewConfigProviderFromData(``)
3864
loadOAuth2From(cfg)

routers/install/install.go

+11
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,17 @@ func SubmitInstall(ctx *context.Context) {
481481
cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(internalToken)
482482
}
483483

484+
// FIXME: at the moment, no matter oauth2 is enabled or not, it must generate a "oauth2 JWT_SECRET"
485+
// see the "loadOAuth2From" in "setting/oauth2.go"
486+
if !cfg.Section("oauth2").HasKey("JWT_SECRET") && !cfg.Section("oauth2").HasKey("JWT_SECRET_URI") {
487+
_, jwtSecretBase64, err := generate.NewJwtSecretWithBase64()
488+
if err != nil {
489+
ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
490+
return
491+
}
492+
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(jwtSecretBase64)
493+
}
494+
484495
// if there is already a SECRET_KEY, we should not overwrite it, otherwise the encrypted data will not be able to be decrypted
485496
if setting.SecretKey == "" {
486497
var secretKey string

0 commit comments

Comments
 (0)