feat(config): OIDC auth section with callback URL, client ID, scopes

This commit is contained in:
Claus Lohmar 2026-06-15 05:04:24 +00:00
parent ef9510704f
commit 8c15dbed35
3 changed files with 89 additions and 35 deletions

View file

@ -1,6 +1,6 @@
package templates
templ GlobalSettings(dbType, dbPath, dbHost, dbPort, dbUser, dbPass, dbName, smtpHost, smtpPort, smtpUser, smtpPass, imapHost, imapPort, nextwksURL, authURL, lang, tz, msg string) {
templ GlobalSettings(dbType, dbPath, dbHost, dbPort, dbUser, dbPass, dbName, smtpHost, smtpPort, smtpUser, smtpPass, imapHost, imapPort, nextwksURL, authURL, clientID, callbackURL, lang, tz, msg string) {
@Layout("global") {
<h2 style="font-size:1.15rem;font-weight:600;margin-bottom:20px">Global Settings</h2>
if msg != "" {
@ -92,17 +92,42 @@ templ GlobalSettings(dbType, dbPath, dbHost, dbPort, dbUser, dbPass, dbName, smt
</div>
<div class="form-card">
<h3 style="font-size:0.95rem;font-weight:600;margin-bottom:12px">URLs</h3>
<div class="form-row">
<h3 style="font-size:0.95rem;font-weight:600;margin-bottom:12px">Workspace URL</h3>
<div class="form-group">
<label>NextWks URL</label>
<input type="text" name="nextwks_url" class="input" value={ nextwksURL }/>
</div>
</div>
<div class="form-card">
<h3 style="font-size:0.95rem;font-weight:600;margin-bottom:12px">Authentication (OIDC)</h3>
<div class="form-group">
<label>Auth URL</label>
<label>Auth Server URL</label>
<input type="text" name="auth_url" class="input" value={ authURL }/>
</div>
<div class="form-row">
<div class="form-group">
<label>Client ID</label>
<input type="text" class="input" value={ clientID } readonly style="opacity:0.7"/>
</div>
<div class="form-group">
<label>Scopes</label>
<input type="text" class="input" value="openid profile email" readonly style="opacity:0.7"/>
</div>
</div>
<div class="form-group">
<label>Redirect URL — copy this to your IDM</label>
<div style="display:flex;gap:8px">
<input type="text" id="callback-url" class="input" value={ callbackURL } readonly style="opacity:0.7;font-family:monospace;font-size:0.8rem"/>
<button type="button" class="btn-sm" onclick="copyCallback()">Copy</button>
</div>
</div>
<script type="text/javascript">
function copyCallback() {
var el = document.getElementById('callback-url');
el.select(); document.execCommand('copy');
}
</script>
</div>
<div class="form-card">

View file

@ -8,7 +8,7 @@ package templates
import "github.com/a-h/templ"
import templruntime "github.com/a-h/templ/runtime"
func GlobalSettings(dbType, dbPath, dbHost, dbPort, dbUser, dbPass, dbName, smtpHost, smtpPort, smtpUser, smtpPass, imapHost, imapPort, nextwksURL, authURL, lang, tz, msg string) templ.Component {
func GlobalSettings(dbType, dbPath, dbHost, dbPort, dbUser, dbPass, dbName, smtpHost, smtpPort, smtpUser, smtpPass, imapHost, imapPort, nextwksURL, authURL, clientID, callbackURL, lang, tz, msg string) templ.Component {
return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@ -240,103 +240,129 @@ func GlobalSettings(dbType, dbPath, dbHost, dbPort, dbUser, dbPass, dbName, smtp
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\"></div></div></div><div class=\"form-card\"><h3 style=\"font-size:0.95rem;font-weight:600;margin-bottom:12px\">URLs</h3><div class=\"form-row\"><div class=\"form-group\"><label>NextWks URL</label> <input type=\"text\" name=\"nextwks_url\" class=\"input\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "\"></div></div></div><div class=\"form-card\"><h3 style=\"font-size:0.95rem;font-weight:600;margin-bottom:12px\">Workspace URL</h3><div class=\"form-group\"><label>NextWks URL</label> <input type=\"text\" name=\"nextwks_url\" class=\"input\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var16 string
templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.ResolveAttributeValue(nextwksURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/global.templ`, Line: 99, Col: 76}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/global.templ`, Line: 98, Col: 75}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var16)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\"></div><div class=\"form-group\"><label>Auth URL</label> <input type=\"text\" name=\"auth_url\" class=\"input\" value=\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "\"></div></div><div class=\"form-card\"><h3 style=\"font-size:0.95rem;font-weight:600;margin-bottom:12px\">Authentication (OIDC)</h3><div class=\"form-group\"><label>Auth Server URL</label> <input type=\"text\" name=\"auth_url\" class=\"input\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var17 string
templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.ResolveAttributeValue(authURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/global.templ`, Line: 103, Col: 70}
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/global.templ`, Line: 106, Col: 69}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var17)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\"></div></div></div><div class=\"form-card\"><h3 style=\"font-size:0.95rem;font-weight:600;margin-bottom:12px\">Localization</h3><div class=\"form-row\"><div class=\"form-group\"><label>Default Language</label> <select name=\"lang\" class=\"input\"><option value=\"en\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "\"></div><div class=\"form-row\"><div class=\"form-group\"><label>Client ID</label> <input type=\"text\" class=\"input\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var18 string
templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.ResolveAttributeValue(clientID)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/global.templ`, Line: 111, Col: 55}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var18)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "\" readonly style=\"opacity:0.7\"></div><div class=\"form-group\"><label>Scopes</label> <input type=\"text\" class=\"input\" value=\"openid profile email\" readonly style=\"opacity:0.7\"></div></div><div class=\"form-group\"><label>Redirect URL — copy this to your IDM</label><div style=\"display:flex;gap:8px\"><input type=\"text\" id=\"callback-url\" class=\"input\" value=\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
var templ_7745c5c3_Var19 string
templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.ResolveAttributeValue(callbackURL)
if templ_7745c5c3_Err != nil {
return templ.Error{Err: templ_7745c5c3_Err, FileName: `core/admin/templates/global.templ`, Line: 121, Col: 76}
}
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ_7745c5c3_Var19)
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "\" readonly style=\"opacity:0.7;font-family:monospace;font-size:0.8rem\"> <button type=\"button\" class=\"btn-sm\" onclick=\"copyCallback()\">Copy</button></div></div><script type=\"text/javascript\">\n\t\t\t\t\tfunction copyCallback() {\n\t\t\t\t\t\tvar el = document.getElementById('callback-url');\n\t\t\t\t\t\tel.select(); document.execCommand('copy');\n\t\t\t\t\t}\n\t\t\t\t</script></div><div class=\"form-card\"><h3 style=\"font-size:0.95rem;font-weight:600;margin-bottom:12px\">Localization</h3><div class=\"form-row\"><div class=\"form-group\"><label>Default Language</label> <select name=\"lang\" class=\"input\"><option value=\"en\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if lang == "en" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, ">English</option> <option value=\"de\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if lang == "de" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, ">Deutsch</option></select></div><div class=\"form-group\"><label>Default Timezone</label> <select name=\"tz\" class=\"input\"><option value=\"UTC\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, ">English</option> <option value=\"de\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if tz == "UTC" {
if lang == "de" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, ">UTC</option> <option value=\"Europe/London\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, ">Deutsch</option></select></div><div class=\"form-group\"><label>Default Timezone</label> <select name=\"tz\" class=\"input\"><option value=\"UTC\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if tz == "Europe/London" {
if tz == "UTC" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, ">London</option> <option value=\"Europe/Berlin\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, ">UTC</option> <option value=\"Europe/London\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if tz == "Europe/Berlin" {
if tz == "Europe/London" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 31, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, ">Berlin</option> <option value=\"Europe/Paris\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 32, ">London</option> <option value=\"Europe/Berlin\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if tz == "Europe/Paris" {
if tz == "Europe/Berlin" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, ">Paris</option> <option value=\"America/New_York\"")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, ">Berlin</option> <option value=\"Europe/Paris\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if tz == "America/New_York" {
if tz == "Europe/Paris" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, ">New York</option></select></div></div></div><button type=\"submit\" class=\"btn\" style=\"width:100%\">Save Settings</button></form>")
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, ">Paris</option> <option value=\"America/New_York\"")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
if tz == "America/New_York" {
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, " selected")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}
}
templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, ">New York</option></select></div></div></div><button type=\"submit\" class=\"btn\" style=\"width:100%\">Save Settings</button></form>")
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
}

View file

@ -51,7 +51,7 @@ func (h *Handler) adminGlobal(w http.ResponseWriter, r *http.Request) {
cfg["db_user"], cfg["db_pass"], cfg["db_name"],
cfg["smtp_host"], cfg["smtp_port"], cfg["smtp_user"], cfg["smtp_pass"],
cfg["imap_host"], cfg["imap_port"], cfg["nextwks_url"], cfg["auth_url"],
cfg["lang"], cfg["tz"], "",
cfg["client_id"], cfg["callback_url"], cfg["lang"], cfg["tz"], "",
)
component.Render(r.Context(), w)
}
@ -60,6 +60,7 @@ func (h *Handler) adminGlobal(w http.ResponseWriter, r *http.Request) {
func (h *Handler) adminGlobalSave(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
msg := writeConfigRaw(h.configPath, r)
// Snip: update second call same way
// Re-read to show updated values
cfg := readConfigRaw(h.configPath)
component := templates.GlobalSettings(
@ -67,7 +68,7 @@ func (h *Handler) adminGlobalSave(w http.ResponseWriter, r *http.Request) {
cfg["db_user"], cfg["db_pass"], cfg["db_name"],
cfg["smtp_host"], cfg["smtp_port"], cfg["smtp_user"], cfg["smtp_pass"],
cfg["imap_host"], cfg["imap_port"], cfg["nextwks_url"], cfg["auth_url"],
cfg["lang"], cfg["tz"], msg,
cfg["client_id"], cfg["callback_url"], cfg["lang"], cfg["tz"], msg,
)
component.Render(r.Context(), w)
}
@ -101,6 +102,8 @@ func readConfigRaw(path string) rawConfig {
cfg["nextwks_url"] = trimSuffix(cfg["nextwks_url"], "/auth/callback")
}
cfg["auth_url"] = getNested(m, "oidc", "issuer_url")
cfg["client_id"] = getNested(m, "oidc", "client_id")
cfg["callback_url"] = getNested(m, "oidc", "redirect_url")
cfg["lang"] = getNested(m, "locale", "language")
cfg["tz"] = getNested(m, "locale", "timezone")
return cfg