diff --git a/cmd/web/handlers:login.go b/cmd/web/handlers:login.go index e49ee5a..ed4d009 100644 --- a/cmd/web/handlers:login.go +++ b/cmd/web/handlers:login.go @@ -45,10 +45,23 @@ func (app *application) redirectAfterLogin(ctx context.Context, w http.ResponseW } } +type signInData struct { + OIDCProviders []string + EnableEmailPasswordSignup bool +} + func (app *application) getSignIn(w http.ResponseWriter, r *http.Request) *httperr.Error { + names := make([]string, 0, len(app.oidcProviders)) + for name := range app.oidcProviders { + names = append(names, name) + } + tmplData := app.html.TemplateData(r) tmplData.Form = accounts.NewSignInForm() - tmplData.Data = app.loginData() + tmplData.Data = signInData{ + EnableEmailPasswordSignup: app.options.EnableEmailPasswordSignup, + OIDCProviders: names, + } return app.html.Render(w, r, http.StatusOK, pages.SignIn, tmplData) } @@ -56,9 +69,17 @@ func (app *application) postSignIn(w http.ResponseWriter, r *http.Request) *http ctx := r.Context() fail := func(formWithErrors *accounts.SignInForm) *httperr.Error { + names := make([]string, 0, len(app.oidcProviders)) + for name := range app.oidcProviders { + names = append(names, name) + } + data := app.html.TemplateData(r) data.Form = formWithErrors - data.Data = app.loginData() + data.Data = signInData{ + EnableEmailPasswordSignup: app.options.EnableEmailPasswordSignup, + OIDCProviders: names, + } return app.html.Render(w, r, http.StatusUnprocessableEntity, pages.SignIn, data) } @@ -263,20 +284,6 @@ func (app *application) postSignOut(w http.ResponseWriter, r *http.Request) *htt return nil } -// LoginData carries the set of enabled OIDC provider names to the sign-in and -// sign-up templates so they can render the appropriate provider buttons. -type LoginData struct { - OIDCProviders []string -} - -func (app *application) loginData() LoginData { - names := make([]string, 0, len(app.oidcProviders)) - for name := range app.oidcProviders { - names = append(names, name) - } - return LoginData{OIDCProviders: names} -} - // getAuthOIDC initiates the OIDC login flow for the named provider. It generates // a random state token, stores it in the session for CSRF verification, then // redirects the browser to the provider's authorization endpoint. diff --git a/cmd/web/options.go b/cmd/web/options.go index aacd308..a43df2f 100644 --- a/cmd/web/options.go +++ b/cmd/web/options.go @@ -34,6 +34,11 @@ type options struct { SMTP SMTP // has SECRET OIDCProviders OIDCProviderMap // has SECRET Limits Limits + + // EnableEmailPasswordSignup controls whether users can sign up with email and password. + // Selfhosters can signup once to create an account and then disable email password signup + // to prevent further unwanted signups. + EnableEmailPasswordSignup bool } func parseOptions(args []string) (*options, error) { @@ -60,6 +65,8 @@ func parseOptions(args []string) (*options, error) { fs.StringVar(&cfg.SMTP.Password, "smtp-password", envOr("SMTP_PASSWORD", ""), "SMTP password (prefer SMTP_PASSWORD env var over flag)") fs.StringVar(&cfg.SMTP.From, "smtp-from", "", "from address for outgoing mail") + fs.BoolVar(&cfg.EnableEmailPasswordSignup, "enable-email-password-signup", true, "enable email and password signup (default true)") + if err := fs.Parse(args); err != nil { return nil, fmt.Errorf("parseServeOptions: %w", err) } diff --git a/cmd/web/routes.go b/cmd/web/routes.go index cc52a72..f3a0591 100644 --- a/cmd/web/routes.go +++ b/cmd/web/routes.go @@ -50,8 +50,10 @@ func (app *application) routes() (http.Handler, error) { // Login related routes that are only accessible if the user is not logged in. mux.Handle("GET /signin", app.withGuest(app.html.Handle(app.getSignIn))) mux.Handle("POST /signin", app.withGuest(app.html.Handle(app.postSignIn))) - mux.Handle("GET /signup", app.withGuest(app.html.Handle(app.getSignUp))) - mux.Handle("POST /signup", app.withGuest(app.html.Handle(app.postSignUp))) + if app.options.EnableEmailPasswordSignup { + mux.Handle("GET /signup", app.withGuest(app.html.Handle(app.getSignUp))) + mux.Handle("POST /signup", app.withGuest(app.html.Handle(app.postSignUp))) + } mux.Handle("GET /forgot-password", app.withGuest(app.html.Handle(app.getForgotPassword))) mux.Handle("POST /forgot-password", app.withGuest(app.html.Handle(app.postForgotPassword))) mux.Handle("GET /forgot-password/success", app.withGuest(app.html.Handle(app.getForgotPasswordSuccess))) diff --git a/internal/templates/pages/docs.tmpl b/internal/templates/pages/docs.tmpl index add9fea..772d0c9 100644 --- a/internal/templates/pages/docs.tmpl +++ b/internal/templates/pages/docs.tmpl @@ -289,6 +289,12 @@
errordebug, info, warn, error-enable-email-password-signuptrueDon't have an account? Sign up.
+ {{ end }}