From 8649d61e3a6b18f379007e89c1959dac63c7315b Mon Sep 17 00:00:00 2001 From: Bernd Konrad Date: Fri, 14 Aug 2026 09:44:12 +0200 Subject: [PATCH 1/5] merged origin master --- config/config.go | 3 +++ config/config_test.go | 2 ++ config/keys.go | 1 + docs/spec.json | 11 +++++++++-- gotify-server.env.example | 7 +++++++ model/gotifyinfo.go | 5 +++++ router/router.go | 10 ++++++++-- ui/serve.go | 20 +++++++++++--------- ui/src/common/ElevationForm.tsx | 3 ++- ui/src/config.ts | 2 ++ ui/src/user/Login.tsx | 4 +++- 11 files changed, 53 insertions(+), 15 deletions(-) diff --git a/config/config.go b/config/config.go index bc49bcf5..85ece6bb 100644 --- a/config/config.go +++ b/config/config.go @@ -68,6 +68,7 @@ type OIDC struct { AutoRegister bool LinkByUsername bool Scopes []string + IDPName string } type Configuration struct { @@ -117,6 +118,7 @@ func Get() (*Configuration, []FutureLog) { UsernameClaim: "preferred_username", AutoRegister: true, Scopes: []string{"openid", "profile", "email"}, + IDPName: "OIDC", }, } @@ -180,6 +182,7 @@ func Get() (*Configuration, []FutureLog) { add(parseBool(&c.OIDC.AutoRegister, EnvOIDCAutoRegister)) add(parseBool(&c.OIDC.LinkByUsername, EnvOIDCLinkByUsername)) add(parseList(&c.OIDC.Scopes, EnvOIDCScopes)) + add(parseString(&c.OIDC.IDPName, EnvOIDCIDPName)) add(parseString(&c.NoColor, EnvNoColor)) diff --git a/config/config_test.go b/config/config_test.go index 5ca4c966..548157fe 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -22,6 +22,7 @@ func TestConfigEnv(t *testing.T) { t.Setenv("GOTIFY_SERVER_CORS_ALLOWMETHODS", "GET,POST") t.Setenv("GOTIFY_SERVER_CORS_ALLOWHEADERS", "Authorization,content-type") t.Setenv("GOTIFY_SERVER_STREAM_ALLOWEDORIGINS", ".+.example.com,otherdomain.com") + t.Setenv("GOTIFY_OIDC_IDP_NAME", "Company XYZ SSO") conf, _ := Get() assert.Equal(t, 80, conf.Server.Port, "should use defaults") @@ -33,6 +34,7 @@ func TestConfigEnv(t *testing.T) { assert.Equal(t, []string{"GET", "POST"}, conf.Server.Cors.AllowMethods) assert.Equal(t, []string{"Authorization", "content-type"}, conf.Server.Cors.AllowHeaders) assert.Equal(t, []string{".+.example.com", "otherdomain.com"}, conf.Server.Stream.AllowedOrigins) + assert.Equal(t, "Company XYZ SSO", conf.OIDC.IDPName) } func TestLocalAuthDisabled(t *testing.T) { diff --git a/config/keys.go b/config/keys.go index e64aba96..c6bf7167 100644 --- a/config/keys.go +++ b/config/keys.go @@ -42,5 +42,6 @@ const ( EnvOIDCLinkByUsername = "GOTIFY_OIDC_LINK_BY_USERNAME" EnvLocalAuthEnabled = "GOTIFY_LOCALAUTH_ENABLED" EnvOIDCScopes = "GOTIFY_OIDC_SCOPES" + EnvOIDCIDPName = "GOTIFY_OIDC_IDP_NAME" EnvNoColor = "NOCOLOR" ) diff --git a/docs/spec.json b/docs/spec.json index 67de0871..e1d5bbe8 100644 --- a/docs/spec.json +++ b/docs/spec.json @@ -2947,7 +2947,8 @@ "version", "register", "localAuth", - "oidc" + "oidc", + "oidcIdpName" ], "properties": { "localAuth": { @@ -2962,6 +2963,12 @@ "x-go-name": "Oidc", "example": true }, + "oidcIdpName": { + "description": "Name of the OIDC identity provider.", + "type": "string", + "x-go-name": "OIDCIDPName", + "example": "OIDC" + }, "register": { "description": "If registration is enabled.", "type": "boolean", @@ -3545,4 +3552,4 @@ "in": "query" } } -} \ No newline at end of file +} diff --git a/gotify-server.env.example b/gotify-server.env.example index f205bf05..14c13e3f 100644 --- a/gotify-server.env.example +++ b/gotify-server.env.example @@ -228,6 +228,13 @@ # Type: boolean # GOTIFY_LOCALAUTH_ENABLED=true +# Name of the OIDC identity provider displayed in the login and elevation UI. +# Only used if GOTIFY_OIDC_ENABLED is true. Defaults to OIDC. +# +# Type: text +# Example: Company XYZ SSO +# GOTIFY_OIDC_IDP_NAME=OIDC + # Database driver to use. For mysql and postgres the target database must # already exist and the configured user must have sufficient permissions. # diff --git a/model/gotifyinfo.go b/model/gotifyinfo.go index 38692adc..526276ef 100644 --- a/model/gotifyinfo.go +++ b/model/gotifyinfo.go @@ -24,4 +24,9 @@ type GotifyInfo struct { // required: true // example: true Oidc bool `json:"oidc"` + // Name of the OIDC identity provider. + // + // required: true + // example: OIDC + OIDCIDPName string `json:"oidcIdpName"` } diff --git a/router/router.go b/router/router.go index 89505471..e65842c0 100644 --- a/router/router.go +++ b/router/router.go @@ -120,7 +120,7 @@ func Create(db *database.GormDatabase, vInfo *model.VersionInfo, conf *config.Co userChangeNotifier.OnUserDeleted(pluginManager.RemoveUser) userChangeNotifier.OnUserAdded(pluginManager.InitializeForUserID) - ui.Register(g, *vInfo, conf.Registration, conf.LocalAuthEnabled, conf.OIDC.Enabled) + ui.Register(g, *vInfo, conf.Registration, conf.LocalAuthEnabled, conf.OIDC.Enabled, conf.OIDC.IDPName) if conf.OIDC.Enabled { oidcHandler := api.NewOIDC(conf, db, userChangeNotifier) @@ -191,7 +191,13 @@ func Create(db *database.GormDatabase, vInfo *model.VersionInfo, conf *config.Co // schema: // $ref: "#/definitions/GotifyInfo" g.GET("gotifyinfo", func(ctx *gin.Context) { - ctx.JSON(200, &model.GotifyInfo{Version: vInfo.Version, Oidc: conf.OIDC.Enabled, Register: conf.Registration, LocalAuth: conf.LocalAuthEnabled}) + ctx.JSON(200, &model.GotifyInfo{ + Version: vInfo.Version, + Oidc: conf.OIDC.Enabled, + Register: conf.Registration, + LocalAuth: conf.LocalAuthEnabled, + OIDCIDPName: conf.OIDC.IDPName, + }) }) g.Group("/").Use(authentication.RequireApplicationOrClient).POST("/message", messageHandler.CreateMessage) diff --git a/ui/serve.go b/ui/serve.go index 12e32590..433191bd 100644 --- a/ui/serve.go +++ b/ui/serve.go @@ -16,19 +16,21 @@ import ( var box embed.FS type uiConfig struct { - Register bool `json:"register"` - Version model.VersionInfo `json:"version"` - LocalAuth bool `json:"localAuth"` - OIDC bool `json:"oidc"` + Register bool `json:"register"` + Version model.VersionInfo `json:"version"` + LocalAuth bool `json:"localAuth"` + OIDC bool `json:"oidc"` + OIDCIDPName string `json:"oidcIdpName"` } // Register registers the ui on the root path. -func Register(r *gin.Engine, version model.VersionInfo, register, localAuthEnabled, oidcEnabled bool) { +func Register(r *gin.Engine, version model.VersionInfo, register bool, oidcEnabled bool, localAuthEnabled bool, oidcIDPName string) { uiConfigBytes, err := json.Marshal(uiConfig{ - Version: version, - Register: register, - LocalAuth: localAuthEnabled, - OIDC: oidcEnabled, + Version: version, + Register: register, + LocalAuth: localAuthEnabled, + OIDC: oidcEnabled, + OIDCIDPName: oidcIDPName, }) if err != nil { panic(err) diff --git a/ui/src/common/ElevationForm.tsx b/ui/src/common/ElevationForm.tsx index bed7c620..cb5db113 100644 --- a/ui/src/common/ElevationForm.tsx +++ b/ui/src/common/ElevationForm.tsx @@ -18,6 +18,7 @@ const ElevationForm = observer(() => { const localAuthEnabled = config.get('localAuth'); const oidcEnabled = config.get('oidc'); const oidcPending = elevateStore.oidcElevatePending; + const oidcIdpName = config.get('oidcIdpName'); const handleLocalElevate = async () => { try { @@ -91,7 +92,7 @@ const ElevationForm = observer(() => { color="primary" fullWidth onClick={() => elevateStore.oidcElevate(ElevateDuration)}> - Elevate via OIDC + {`Elevate via ${oidcIdpName}`} )} diff --git a/ui/src/config.ts b/ui/src/config.ts index cfe75d52..be57bd21 100644 --- a/ui/src/config.ts +++ b/ui/src/config.ts @@ -6,6 +6,7 @@ export interface IConfig { version: IVersion; oidc: boolean; localAuth: boolean; + oidcIdpName: string; } declare global { @@ -20,6 +21,7 @@ const config: IConfig = { version: {commit: 'unknown', buildDate: 'unknown', version: 'unknown'}, oidc: false, localAuth: true, + oidcIdpName: 'OIDC', ...window.config, }; diff --git a/ui/src/user/Login.tsx b/ui/src/user/Login.tsx index 5f1ac4b1..5dc819cc 100644 --- a/ui/src/user/Login.tsx +++ b/ui/src/user/Login.tsx @@ -19,6 +19,8 @@ const Login = observer(() => { const navigate = useNavigate(); const localAuthEnabled = config.get('localAuth'); const oidcEnabled = config.get('oidc'); + const oidcIdpName = config.get('oidcIdpName'); + React.useEffect(() => { if (currentUser.loggedIn) { navigate('/'); @@ -103,7 +105,7 @@ const Login = observer(() => { size="large" color="primary" style={{marginBottom: 5}}> - Login with OIDC + {`Login with ${oidcIdpName}`} )} From 4b2fa6ace429315cbcf4d9b207dd3afbe9313e5e Mon Sep 17 00:00:00 2001 From: Bernd Konrad Date: Fri, 14 Aug 2026 09:44:31 +0200 Subject: [PATCH 2/5] fixed bug: wrong order of arguments in ui.Register --- router/router.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/router/router.go b/router/router.go index e65842c0..de4e94f9 100644 --- a/router/router.go +++ b/router/router.go @@ -120,7 +120,7 @@ func Create(db *database.GormDatabase, vInfo *model.VersionInfo, conf *config.Co userChangeNotifier.OnUserDeleted(pluginManager.RemoveUser) userChangeNotifier.OnUserAdded(pluginManager.InitializeForUserID) - ui.Register(g, *vInfo, conf.Registration, conf.LocalAuthEnabled, conf.OIDC.Enabled, conf.OIDC.IDPName) + ui.Register(g, *vInfo, conf.Registration, conf.OIDC.Enabled, conf.LocalAuthEnabled, conf.OIDC.IDPName) if conf.OIDC.Enabled { oidcHandler := api.NewOIDC(conf, db, userChangeNotifier) From bebd948075612985e2bbb662ee946f732b68a7d5 Mon Sep 17 00:00:00 2001 From: Bernd Konrad Date: Fri, 14 Aug 2026 10:06:23 +0200 Subject: [PATCH 3/5] restored original parameter ordering. used project formatting tools to (hopefully) resolve automated checks --- router/router.go | 2 +- ui/serve.go | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/router/router.go b/router/router.go index de4e94f9..e65842c0 100644 --- a/router/router.go +++ b/router/router.go @@ -120,7 +120,7 @@ func Create(db *database.GormDatabase, vInfo *model.VersionInfo, conf *config.Co userChangeNotifier.OnUserDeleted(pluginManager.RemoveUser) userChangeNotifier.OnUserAdded(pluginManager.InitializeForUserID) - ui.Register(g, *vInfo, conf.Registration, conf.OIDC.Enabled, conf.LocalAuthEnabled, conf.OIDC.IDPName) + ui.Register(g, *vInfo, conf.Registration, conf.LocalAuthEnabled, conf.OIDC.Enabled, conf.OIDC.IDPName) if conf.OIDC.Enabled { oidcHandler := api.NewOIDC(conf, db, userChangeNotifier) diff --git a/ui/serve.go b/ui/serve.go index 433191bd..afe2e10d 100644 --- a/ui/serve.go +++ b/ui/serve.go @@ -24,7 +24,14 @@ type uiConfig struct { } // Register registers the ui on the root path. -func Register(r *gin.Engine, version model.VersionInfo, register bool, oidcEnabled bool, localAuthEnabled bool, oidcIDPName string) { +func Register( + r *gin.Engine, + version model.VersionInfo, + register bool, + localAuthEnabled bool, + oidcEnabled bool, + oidcIDPName string, +) { uiConfigBytes, err := json.Marshal(uiConfig{ Version: version, Register: register, From 24070e0af4f885275dcf34060f6166cff9f9ca1e Mon Sep 17 00:00:00 2001 From: Bernd Konrad Date: Fri, 14 Aug 2026 10:25:27 +0200 Subject: [PATCH 4/5] removed newline at the end of swagger doc. i dont know which tool added that, but now the CI seems to fail becuase of it. seems a bit odd that i have to commit stuff before the CI spits out reproduceable results. --- docs/spec.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec.json b/docs/spec.json index e1d5bbe8..5692a37b 100644 --- a/docs/spec.json +++ b/docs/spec.json @@ -3552,4 +3552,4 @@ "in": "query" } } -} +} \ No newline at end of file From 2636b0953290cc1c76d1cdb7072bf054e3094818 Mon Sep 17 00:00:00 2001 From: Bernd Konrad Date: Fri, 14 Aug 2026 10:58:50 +0200 Subject: [PATCH 5/5] seems like the formatting rules that caused the length function defintiion to have line breaks is what causes the line by line coverage rate to drop below the threshold. added new router test for /gotifyinfo to rectify that. --- router/router_test.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/router/router_test.go b/router/router_test.go index 58daed95..cd6f7864 100644 --- a/router/router_test.go +++ b/router/router_test.go @@ -42,7 +42,11 @@ func (s *IntegrationSuite) BeforeTest(string, string) { g, closable := Create( s.db.GormDatabase, &model.VersionInfo{Version: "1.0.0", BuildDate: "2018-02-20-17:30:47", Commit: "asdasds"}, - &config.Configuration{PassStrength: 5, LocalAuthEnabled: true}, + &config.Configuration{ + PassStrength: 5, + LocalAuthEnabled: true, + OIDC: config.OIDC{IDPName: "Company XYZ SSO"}, + }, ) s.closable = closable s.server = httptest.NewServer(g) @@ -60,6 +64,12 @@ func (s *IntegrationSuite) TestVersionInfo() { doRequestAndExpect(s.T(), req, 200, `{"version":"1.0.0", "commit":"asdasds", "buildDate":"2018-02-20-17:30:47"}`) } +func (s *IntegrationSuite) TestGotifyInfo() { + req := s.newRequest("GET", "gotifyinfo", "") + + doRequestAndExpect(s.T(), req, 200, `{"version":"1.0.0", "oidc":false, "register":false, "localAuth":true, "oidcIdpName":"Company XYZ SSO"}`) +} + func (s *IntegrationSuite) TestHeaderInProd() { mode.Set(mode.Prod) req := s.newRequest("GET", "version", "")