mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 16:40:24 +08:00 
			
		
		
		
	Allow specifying SECRET_KEY_URI, similar to INTERNAL_TOKEN_URI (#19663)
Only load SECRET_KEY and INTERNAL_TOKEN if they exist. Never write the config file if the keys do not exist, which was only a fallback for Gitea upgraded from < 1.5 Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		@@ -203,7 +203,7 @@ func setPort(port string) error {
 | 
				
			|||||||
		defaultLocalURL += ":" + setting.HTTPPort + "/"
 | 
							defaultLocalURL += ":" + setting.HTTPPort + "/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Save LOCAL_ROOT_URL if port changed
 | 
							// Save LOCAL_ROOT_URL if port changed
 | 
				
			||||||
		setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
 | 
							setting.CreateOrAppendToCustomConf("server.LOCAL_ROOT_URL", func(cfg *ini.File) {
 | 
				
			||||||
			cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
 | 
								cfg.Section("server").Key("LOCAL_ROOT_URL").SetValue(defaultLocalURL)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -379,14 +379,19 @@ LOG_SQL = false ; if unset defaults to true
 | 
				
			|||||||
;; Whether the installer is disabled (set to true to disable the installer)
 | 
					;; Whether the installer is disabled (set to true to disable the installer)
 | 
				
			||||||
INSTALL_LOCK = false
 | 
					INSTALL_LOCK = false
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Global secret key that will be used - if blank will be regenerated.
 | 
					;; Global secret key that will be used
 | 
				
			||||||
 | 
					;; This key is VERY IMPORTANT. If you lose it, the data encrypted by it (like 2FA secret) can't be decrypted anymore.
 | 
				
			||||||
SECRET_KEY =
 | 
					SECRET_KEY =
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
 | 
					;; Alternative location to specify secret key, instead of this file; you cannot specify both this and SECRET_KEY, and must pick one
 | 
				
			||||||
 | 
					;; This key is VERY IMPORTANT. If you lose it, the data encrypted by it (like 2FA secret) can't be decrypted anymore.
 | 
				
			||||||
 | 
					;SECRET_KEY_URI = file:/etc/gitea/secret_key
 | 
				
			||||||
 | 
					;;
 | 
				
			||||||
;; Secret used to validate communication within Gitea binary.
 | 
					;; Secret used to validate communication within Gitea binary.
 | 
				
			||||||
INTERNAL_TOKEN=
 | 
					INTERNAL_TOKEN=
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: file:/etc/gitea/internal_token)
 | 
					;; Alternative location to specify internal token, instead of this file; you cannot specify both this and INTERNAL_TOKEN, and must pick one
 | 
				
			||||||
;INTERNAL_TOKEN_URI = ;e.g. /etc/gitea/internal_token
 | 
					;INTERNAL_TOKEN_URI = file:/etc/gitea/internal_token
 | 
				
			||||||
;;
 | 
					;;
 | 
				
			||||||
;; How long to remember that a user is logged in before requiring relogin (in days)
 | 
					;; How long to remember that a user is logged in before requiring relogin (in days)
 | 
				
			||||||
;LOGIN_REMEMBER_DAYS = 7
 | 
					;LOGIN_REMEMBER_DAYS = 7
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -494,7 +494,8 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o
 | 
				
			|||||||
## Security (`security`)
 | 
					## Security (`security`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- `INSTALL_LOCK`: **false**: Controls access to the installation page. When set to "true", the installation page is not accessible.
 | 
					- `INSTALL_LOCK`: **false**: Controls access to the installation page. When set to "true", the installation page is not accessible.
 | 
				
			||||||
- `SECRET_KEY`: **\<random at every install\>**: Global secret key. This should be changed.
 | 
					- `SECRET_KEY`: **\<random at every install\>**: Global secret key. This key is VERY IMPORTANT, if you lost it, the data encrypted by it (like 2FA secret) can't be decrypted anymore.
 | 
				
			||||||
 | 
					- `SECRET_KEY_URI`: **<empty>**: Instead of defining SECRET_KEY, this option can be used to use the key stored in a file (example value: `file:/etc/gitea/secret_key`). It shouldn't be lost like SECRET_KEY.
 | 
				
			||||||
- `LOGIN_REMEMBER_DAYS`: **7**: Cookie lifetime, in days.
 | 
					- `LOGIN_REMEMBER_DAYS`: **7**: Cookie lifetime, in days.
 | 
				
			||||||
- `COOKIE_USERNAME`: **gitea\_awesome**: Name of the cookie used to store the current username.
 | 
					- `COOKIE_USERNAME`: **gitea\_awesome**: Name of the cookie used to store the current username.
 | 
				
			||||||
- `COOKIE_REMEMBER_NAME`: **gitea\_incredible**: Name of cookie used to store authentication
 | 
					- `COOKIE_REMEMBER_NAME`: **gitea\_incredible**: Name of cookie used to store authentication
 | 
				
			||||||
@@ -520,7 +521,7 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o
 | 
				
			|||||||
- `ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET`: **true**: Set to `false` to allow local users to push to gitea-repositories without setting up the Gitea environment. This is not recommended and if you want local users to push to Gitea repositories you should set the environment appropriately.
 | 
					- `ONLY_ALLOW_PUSH_IF_GITEA_ENVIRONMENT_SET`: **true**: Set to `false` to allow local users to push to gitea-repositories without setting up the Gitea environment. This is not recommended and if you want local users to push to Gitea repositories you should set the environment appropriately.
 | 
				
			||||||
- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.
 | 
					- `IMPORT_LOCAL_PATHS`: **false**: Set to `false` to prevent all users (including admin) from importing local path on server.
 | 
				
			||||||
- `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary.
 | 
					- `INTERNAL_TOKEN`: **\<random at every install if no uri set\>**: Secret used to validate communication within Gitea binary.
 | 
				
			||||||
- `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
 | 
					- `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining INTERNAL_TOKEN in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
 | 
				
			||||||
- `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[argon2, pbkdf2, scrypt, bcrypt\], argon2 will spend more memory than others.
 | 
					- `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[argon2, pbkdf2, scrypt, bcrypt\], argon2 will spend more memory than others.
 | 
				
			||||||
- `CSRF_COOKIE_HTTP_ONLY`: **true**: Set false to allow JavaScript to read CSRF cookie.
 | 
					- `CSRF_COOKIE_HTTP_ONLY`: **true**: Set false to allow JavaScript to read CSRF cookie.
 | 
				
			||||||
- `MIN_PASSWORD_LENGTH`: **6**: Minimum password length for new users.
 | 
					- `MIN_PASSWORD_LENGTH`: **6**: Minimum password length for new users.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -62,7 +62,7 @@ func newLFSService() {
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Save secret
 | 
								// Save secret
 | 
				
			||||||
			CreateOrAppendToCustomConf(func(cfg *ini.File) {
 | 
								CreateOrAppendToCustomConf("server.LFS_JWT_SECRET", func(cfg *ini.File) {
 | 
				
			||||||
				cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
 | 
									cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,6 @@ import (
 | 
				
			|||||||
	"text/template"
 | 
						"text/template"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/generate"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/user"
 | 
						"code.gitea.io/gitea/modules/user"
 | 
				
			||||||
@@ -923,9 +922,15 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	sec = Cfg.Section("security")
 | 
						sec = Cfg.Section("security")
 | 
				
			||||||
	InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
 | 
						InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
 | 
				
			||||||
	SecretKey = sec.Key("SECRET_KEY").MustString("!#@FDEWREWR&*(")
 | 
					 | 
				
			||||||
	LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
 | 
						LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
 | 
				
			||||||
	CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
 | 
						CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
 | 
				
			||||||
 | 
						SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY")
 | 
				
			||||||
 | 
						if SecretKey == "" {
 | 
				
			||||||
 | 
							// FIXME: https://github.com/go-gitea/gitea/issues/16832
 | 
				
			||||||
 | 
							// Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value
 | 
				
			||||||
 | 
							SecretKey = "!#@FDEWREWR&*(" // nolint:gosec
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
 | 
						CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
 | 
						ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
 | 
				
			||||||
@@ -948,11 +953,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
 | 
				
			|||||||
	PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
 | 
						PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
 | 
				
			||||||
	SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
 | 
						SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	InternalToken = loadInternalToken(sec)
 | 
						InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN")
 | 
				
			||||||
	if InstallLock && InternalToken == "" {
 | 
					 | 
				
			||||||
		// if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
 | 
					 | 
				
			||||||
		generateSaveInternalToken()
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
 | 
						cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
 | 
				
			||||||
	if len(cfgdata) == 0 {
 | 
						if len(cfgdata) == 0 {
 | 
				
			||||||
@@ -1141,51 +1142,36 @@ func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) {
 | 
				
			|||||||
	return authorizedPrincipalsAllow, true
 | 
						return authorizedPrincipalsAllow, true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func loadInternalToken(sec *ini.Section) string {
 | 
					func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string {
 | 
				
			||||||
	uri := sec.Key("INTERNAL_TOKEN_URI").String()
 | 
						// don't allow setting both URI and verbatim string
 | 
				
			||||||
	if uri == "" {
 | 
						uri := sec.Key(uriKey).String()
 | 
				
			||||||
		return sec.Key("INTERNAL_TOKEN").String()
 | 
						verbatim := sec.Key(verbatimKey).String()
 | 
				
			||||||
 | 
						if uri != "" && verbatim != "" {
 | 
				
			||||||
 | 
							log.Fatal("Cannot specify both %s and %s", uriKey, verbatimKey)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// if we have no URI, use verbatim
 | 
				
			||||||
 | 
						if uri == "" {
 | 
				
			||||||
 | 
							return verbatim
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tempURI, err := url.Parse(uri)
 | 
						tempURI, err := url.Parse(uri)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal("Failed to parse INTERNAL_TOKEN_URI (%s): %v", uri, err)
 | 
							log.Fatal("Failed to parse %s (%s): %v", uriKey, uri, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	switch tempURI.Scheme {
 | 
						switch tempURI.Scheme {
 | 
				
			||||||
	case "file":
 | 
						case "file":
 | 
				
			||||||
		buf, err := os.ReadFile(tempURI.RequestURI())
 | 
							buf, err := os.ReadFile(tempURI.RequestURI())
 | 
				
			||||||
		if err != nil && !os.IsNotExist(err) {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err)
 | 
								log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err)
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		// No token in the file, generate one and store it.
 | 
					 | 
				
			||||||
		if len(buf) == 0 {
 | 
					 | 
				
			||||||
			token, err := generate.NewInternalToken()
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				log.Fatal("Error generate internal token: %v", err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			err = os.WriteFile(tempURI.RequestURI(), []byte(token), 0o600)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return token
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return strings.TrimSpace(string(buf))
 | 
							return strings.TrimSpace(string(buf))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// only file URIs are allowed
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
 | 
							log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
 | 
				
			||||||
 | 
							return ""
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ""
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// generateSaveInternalToken generates and saves the internal token to app.ini
 | 
					 | 
				
			||||||
func generateSaveInternalToken() {
 | 
					 | 
				
			||||||
	token, err := generate.NewInternalToken()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		log.Fatal("Error generate internal token: %v", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	InternalToken = token
 | 
					 | 
				
			||||||
	CreateOrAppendToCustomConf(func(cfg *ini.File) {
 | 
					 | 
				
			||||||
		cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
 | 
					// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
 | 
				
			||||||
@@ -1249,7 +1235,12 @@ func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// CreateOrAppendToCustomConf creates or updates the custom config.
 | 
					// CreateOrAppendToCustomConf creates or updates the custom config.
 | 
				
			||||||
// Use the callback to set individual values.
 | 
					// Use the callback to set individual values.
 | 
				
			||||||
func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) {
 | 
					func CreateOrAppendToCustomConf(purpose string, callback func(cfg *ini.File)) {
 | 
				
			||||||
 | 
						if CustomConf == "" {
 | 
				
			||||||
 | 
							log.Error("Custom config path must not be empty")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfg := ini.Empty()
 | 
						cfg := ini.Empty()
 | 
				
			||||||
	isFile, err := util.IsFile(CustomConf)
 | 
						isFile, err := util.IsFile(CustomConf)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -1264,8 +1255,6 @@ func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	callback(cfg)
 | 
						callback(cfg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	log.Info("Settings saved to: %q", CustomConf)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
 | 
						if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
 | 
				
			||||||
		log.Fatal("failed to create '%s': %v", CustomConf, err)
 | 
							log.Fatal("failed to create '%s': %v", CustomConf, err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -1273,6 +1262,7 @@ func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) {
 | 
				
			|||||||
	if err := cfg.SaveTo(CustomConf); err != nil {
 | 
						if err := cfg.SaveTo(CustomConf); err != nil {
 | 
				
			||||||
		log.Fatal("error saving to custom config: %v", err)
 | 
							log.Fatal("error saving to custom config: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						log.Info("Settings for %s saved to: %q", purpose, CustomConf)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Change permissions to be more restrictive
 | 
						// Change permissions to be more restrictive
 | 
				
			||||||
	fi, err := os.Stat(CustomConf)
 | 
						fi, err := os.Stat(CustomConf)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,11 @@ func CheckInternalToken(next http.Handler) http.Handler {
 | 
				
			|||||||
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
						return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
		tokens := req.Header.Get("Authorization")
 | 
							tokens := req.Header.Get("Authorization")
 | 
				
			||||||
		fields := strings.SplitN(tokens, " ", 2)
 | 
							fields := strings.SplitN(tokens, " ", 2)
 | 
				
			||||||
 | 
							if setting.InternalToken == "" {
 | 
				
			||||||
 | 
								log.Warn(`The INTERNAL_TOKEN setting is missing from the configuration file: %q, internal API can't work.`, setting.CustomConf)
 | 
				
			||||||
 | 
								http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken {
 | 
							if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken {
 | 
				
			||||||
			log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens)
 | 
								log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens)
 | 
				
			||||||
			http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
 | 
								http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -364,7 +364,7 @@ func loadOrCreateSymmetricKey() (interface{}, error) {
 | 
				
			|||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
 | 
							setting.CreateOrAppendToCustomConf("oauth2.JWT_SECRET", func(cfg *ini.File) {
 | 
				
			||||||
			secretBase64 := base64.RawURLEncoding.EncodeToString(key)
 | 
								secretBase64 := base64.RawURLEncoding.EncodeToString(key)
 | 
				
			||||||
			cfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
 | 
								cfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user