Skip to content

Commit

Permalink
feat: Add option to disable local auth form (miniflux#2649)
Browse files Browse the repository at this point in the history
  • Loading branch information
thefinn93 committed Jul 18, 2024
1 parent 8d4d092 commit 223a71d
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 9 deletions.
5 changes: 5 additions & 0 deletions internal/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package cli // import "miniflux.app/v2/internal/cli"

import (
"errors"
"flag"
"fmt"
"io"
Expand Down Expand Up @@ -225,6 +226,10 @@ func Parse() {
return
}

if config.Opts.DisableLocalAuth() && config.Opts.OAuth2Provider() == "" {
printErrorAndExit(errors.New("DISABLE_LOCAL_AUTH is enabled but OAUTH2_PROVIDER is not set. Please enable at least one authentication source"))
}

startDaemon(store)
}

Expand Down
9 changes: 9 additions & 0 deletions internal/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const (
defaultOAuth2RedirectURL = ""
defaultOAuth2OidcDiscoveryEndpoint = ""
defaultOAuth2Provider = ""
defaultDisableLocalAuth = false
defaultPocketConsumerKey = ""
defaultHTTPClientTimeout = 20
defaultHTTPClientMaxBodySize = 15
Expand Down Expand Up @@ -152,6 +153,7 @@ type Options struct {
oauth2RedirectURL string
oidcDiscoveryEndpoint string
oauth2Provider string
disableLocalAuth bool
pocketConsumerKey string
httpClientTimeout int
httpClientMaxBodySize int64
Expand Down Expand Up @@ -228,6 +230,7 @@ func NewOptions() *Options {
oauth2RedirectURL: defaultOAuth2RedirectURL,
oidcDiscoveryEndpoint: defaultOAuth2OidcDiscoveryEndpoint,
oauth2Provider: defaultOAuth2Provider,
disableLocalAuth: defaultDisableLocalAuth,
pocketConsumerKey: defaultPocketConsumerKey,
httpClientTimeout: defaultHTTPClientTimeout,
httpClientMaxBodySize: defaultHTTPClientMaxBodySize * 1024 * 1024,
Expand Down Expand Up @@ -453,6 +456,11 @@ func (o *Options) OAuth2Provider() string {
return o.oauth2Provider
}

// DisableLocalAUth returns true if the local user database should not be used to authenticate users
func (o *Options) DisableLocalAuth() bool {
return o.disableLocalAuth
}

// HasHSTS returns true if HTTP Strict Transport Security is enabled.
func (o *Options) HasHSTS() bool {
return o.hsts
Expand Down Expand Up @@ -685,6 +693,7 @@ func (o *Options) SortedOptions(redactSecret bool) []*Option {
"OAUTH2_PROVIDER": o.oauth2Provider,
"OAUTH2_REDIRECT_URL": o.oauth2RedirectURL,
"OAUTH2_USER_CREATION": o.oauth2UserCreationAllowed,
"DISABLE_LOCAL_AUTH": o.disableLocalAuth,
"POCKET_CONSUMER_KEY": redactSecretValue(o.pocketConsumerKey, redactSecret),
"POLLING_FREQUENCY": o.pollingFrequency,
"FORCE_REFRESH_INTERVAL": o.forceRefreshInterval,
Expand Down
2 changes: 2 additions & 0 deletions internal/config/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,8 @@ func (p *Parser) parseLines(lines []string) (err error) {
p.opts.oidcDiscoveryEndpoint = parseString(value, defaultOAuth2OidcDiscoveryEndpoint)
case "OAUTH2_PROVIDER":
p.opts.oauth2Provider = parseString(value, defaultOAuth2Provider)
case "DISABLE_LOCAL_AUTH":
p.opts.disableLocalAuth = parseBool(value, defaultDisableLocalAuth)
case "HTTP_CLIENT_TIMEOUT":
p.opts.httpClientTimeout = parseInt(value, defaultHTTPClientTimeout)
case "HTTP_CLIENT_MAX_BODY_SIZE":
Expand Down
15 changes: 8 additions & 7 deletions internal/template/functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,14 @@ type funcMap struct {
// Map returns a map of template functions that are compiled during template parsing.
func (f *funcMap) Map() template.FuncMap {
return template.FuncMap{
"formatFileSize": formatFileSize,
"dict": dict,
"hasKey": hasKey,
"truncate": truncate,
"isEmail": isEmail,
"baseURL": config.Opts.BaseURL,
"rootURL": config.Opts.RootURL,
"formatFileSize": formatFileSize,
"dict": dict,
"hasKey": hasKey,
"truncate": truncate,
"isEmail": isEmail,
"baseURL": config.Opts.BaseURL,
"rootURL": config.Opts.RootURL,
"disableLocalAuth": config.Opts.DisableLocalAuth,
"hasOAuth2Provider": func(provider string) bool {
return config.Opts.OAuth2Provider() == provider
},
Expand Down
2 changes: 2 additions & 0 deletions internal/template/templates/views/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

{{ define "content"}}
<section class="login-form">
{{ if not disableLocalAuth }}
<form action="{{ route "checkLogin" }}" method="post">
<input type="hidden" name="csrf" value="{{ .csrf }}">

Expand All @@ -22,6 +23,7 @@
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.loading" }}">{{ t "action.login" }}</button>
</div>
</form>
{{ end }}
{{ if .webAuthnEnabled }}
<div class="webauthn">
<div role="alert" class="alert alert-error hidden" id="webauthn-error">
Expand Down
2 changes: 2 additions & 0 deletions internal/template/templates/views/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ <h1 id="page-header-title">{{ t "page.settings.title" }}</h1>
<div role="alert" class="alert alert-error">{{ .errorMessage }}</div>
{{ end }}

{{ if not disableLocalAuth }}
<fieldset>
<legend>{{ t "form.prefs.fieldset.authentication_settings" }}</legend>

Expand Down Expand Up @@ -49,6 +50,7 @@ <h1 id="page-header-title">{{ t "page.settings.title" }}</h1>
<button type="submit" class="button button-primary" data-label-loading="{{ t "form.submit.saving" }}">{{ t "action.update" }}</button>
</div>
</fieldset>
{{ end }}

{{ if .webAuthnEnabled }}
<fieldset>
Expand Down
13 changes: 11 additions & 2 deletions internal/ui/login_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,18 @@ import (
func (h *handler) checkLogin(w http.ResponseWriter, r *http.Request) {
clientIP := request.ClientIP(r)
sess := session.New(h.store, request.SessionID(r))
authForm := form.NewAuthForm(r)

view := view.New(h.tpl, r, sess)

if config.Opts.DisableLocalAuth() {
slog.Warn("blocking local auth login attempt, local auth is disabled",
slog.String("client_ip", clientIP),
slog.String("user_agent", r.UserAgent()),
)
html.OK(w, r, view.Render("login"))
return
}

authForm := form.NewAuthForm(r)
view.Set("errorMessage", locale.NewLocalizedError("error.bad_credentials").Translate(request.UserLanguage(r)))
view.Set("form", authForm)

Expand Down
9 changes: 9 additions & 0 deletions internal/ui/oauth2_unlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"log/slog"
"net/http"

"miniflux.app/v2/internal/config"
"miniflux.app/v2/internal/http/request"
"miniflux.app/v2/internal/http/response/html"
"miniflux.app/v2/internal/http/route"
Expand All @@ -15,6 +16,14 @@ import (
)

func (h *handler) oauth2Unlink(w http.ResponseWriter, r *http.Request) {
if config.Opts.DisableLocalAuth() {
slog.Warn("blocking oauth2 unlink attempt, local auth is disabled",
slog.String("user_agent", r.UserAgent()),
)
html.Redirect(w, r, route.Path(h.router, "login"))
return
}

printer := locale.NewPrinter(request.UserLanguage(r))
provider := request.RouteStringParam(r, "provider")
if provider == "" {
Expand Down

0 comments on commit 223a71d

Please sign in to comment.