diff --git a/auth/.env.example b/auth/.env.example
index 581d1bad..73452a74 100644
--- a/auth/.env.example
+++ b/auth/.env.example
@@ -6,6 +6,9 @@ RSA_PRIVATE_KEY_PATH=""
PROFILE_PICTURE_PATH="/profile_pictures"
+# If true, POST /users registration is disabled and returns 403.
+DISABLE_REGISTRATION=false
+
# json object with the claims to add to every jwt (this is read when creating a new user)
EXTRA_CLAIMS='{}'
# json object with the claims to add to every jwt of the FIRST user (this can be used to mark the first user as admin).
diff --git a/auth/config.go b/auth/config.go
index 473bdb4d..e610dea8 100644
--- a/auth/config.go
+++ b/auth/config.go
@@ -14,6 +14,7 @@ import (
"maps"
"os"
"slices"
+ "strconv"
"strings"
"time"
@@ -24,18 +25,19 @@ import (
)
type Configuration struct {
- JwtPrivateKey *rsa.PrivateKey
- JwtPublicKey *rsa.PublicKey
- JwtKid string
- PublicUrl string
- OidcProviders map[string]OidcProviderConfig
- DefaultClaims jwt.MapClaims
- FirstUserClaims jwt.MapClaims
- GuestClaims jwt.MapClaims
- ProtectedClaims []string
- ExpirationDelay time.Duration
- EnvApiKeys []ApiKeyWToken
- ProfilePicturePath string
+ JwtPrivateKey *rsa.PrivateKey
+ JwtPublicKey *rsa.PublicKey
+ JwtKid string
+ PublicUrl string
+ OidcProviders map[string]OidcProviderConfig
+ DefaultClaims jwt.MapClaims
+ FirstUserClaims jwt.MapClaims
+ GuestClaims jwt.MapClaims
+ ProtectedClaims []string
+ ExpirationDelay time.Duration
+ EnvApiKeys []ApiKeyWToken
+ ProfilePicturePath string
+ DisableRegistration bool
}
type OidcAuthMethod string
@@ -76,6 +78,12 @@ func LoadConfiguration(ctx context.Context, db *dbc.Queries) (*Configuration, er
"/profile_pictures",
)
+ disableRegistration, err := strconv.ParseBool(cmp.Or(os.Getenv("DISABLE_REGISTRATION"), "false"))
+ if err != nil {
+ return nil, fmt.Errorf("invalid DISABLE_REGISTRATION value: %w", err)
+ }
+ ret.DisableRegistration = disableRegistration
+
claims := os.Getenv("EXTRA_CLAIMS")
if claims != "" {
err := json.Unmarshal([]byte(claims), &ret.DefaultClaims)
diff --git a/auth/oidc.go b/auth/oidc.go
index c22a3c4e..619cb2b9 100644
--- a/auth/oidc.go
+++ b/auth/oidc.go
@@ -530,8 +530,9 @@ func (h *Handler) OidcUnlink(c *echo.Context) error {
}
type ServerInfo struct {
- PublicUrl string `json:"publicUrl"`
- Oidc map[string]OidcInfo `json:"oidc"`
+ PublicUrl string `json:"publicUrl"`
+ AllowRegister bool `json:"allowRegister"`
+ Oidc map[string]OidcInfo `json:"oidc"`
}
type OidcInfo struct {
@@ -547,8 +548,9 @@ type OidcInfo struct {
// @Router /info [get]
func (h *Handler) Info(c *echo.Context) error {
ret := ServerInfo{
- PublicUrl: h.config.PublicUrl,
- Oidc: make(map[string]OidcInfo),
+ PublicUrl: h.config.PublicUrl,
+ AllowRegister: !h.config.DisableRegistration,
+ Oidc: make(map[string]OidcInfo),
}
for _, provider := range h.config.OidcProviders {
ret.Oidc[provider.Id] = OidcInfo{
diff --git a/auth/users.go b/auth/users.go
index b59f2457..194dd38f 100644
--- a/auth/users.go
+++ b/auth/users.go
@@ -159,9 +159,14 @@ func (h *Handler) GetMe(c *echo.Context) error {
// @Param user body RegisterDto false "Registration informations"
// @Success 201 {object} SessionWToken
// @Success 409 {object} KError "Duplicated email or username"
+// @Failure 403 {object} KError "Registrations are disabled"
// @Failure 422 {object} KError "Invalid register body"
// @Router /users [post]
func (h *Handler) Register(c *echo.Context) error {
+ if h.config.DisableRegistration {
+ return echo.NewHTTPError(http.StatusForbidden, "Registrations are disabled")
+ }
+
ctx := c.Request().Context()
var req RegisterDto
err := c.Bind(&req)
diff --git a/front/public/translations/en.json b/front/public/translations/en.json
index 6b9c40ba..dd344ee4 100644
--- a/front/public/translations/en.json
+++ b/front/public/translations/en.json
@@ -250,7 +250,9 @@
"or-login": "Have an account already? <1>Log in1>.",
"password-no-match": "Passwords do not match.",
"delete": "Delete your account",
- "delete-confirmation": "This action can't be reverted. Are you sure?"
+ "delete-confirmation": "This action can't be reverted. Are you sure?",
+ "register-disabled": "Registrations are disabled.",
+ "register-disabled-oidc": "Password registration is disabled. Use OIDC."
},
"downloads": {
"empty": "Nothing downloaded yet, start browsing for something you like",
diff --git a/front/src/ui/login/login.tsx b/front/src/ui/login/login.tsx
index 8dfcbfd3..a6ef3bf4 100644
--- a/front/src/ui/login/login.tsx
+++ b/front/src/ui/login/login.tsx
@@ -4,6 +4,7 @@ import { Trans, useTranslation } from "react-i18next";
import { Platform } from "react-native";
import { A, Button, H1, Input, P } from "~/primitives";
import { defaultApiUrl } from "~/providers/account-provider";
+import { useFetch } from "~/query";
import { useQueryState } from "~/utils";
import { FormPage } from "./form";
import { login } from "./logic";
@@ -20,46 +21,48 @@ export const LoginPage = () => {
const { t } = useTranslation();
const router = useRouter();
+ const { data: info } = useFetch(OidcLogin.query(apiUrl));
if (Platform.OS !== "web" && !apiUrl) return {t("login.username")} {t("login.password")} {error}{t("login.login")}
-
{t("misc.or")}
{error}
- )} + {or} > )} Loader={() => ( diff --git a/front/src/ui/login/register.tsx b/front/src/ui/login/register.tsx index 5da31a75..a14c1448 100644 --- a/front/src/ui/login/register.tsx +++ b/front/src/ui/login/register.tsx @@ -4,6 +4,7 @@ import { Trans, useTranslation } from "react-i18next"; import { Platform } from "react-native"; import { A, Button, H1, Input, P } from "~/primitives"; import { defaultApiUrl } from "~/providers/account-provider"; +import { useFetch } from "~/query"; import { useQueryState } from "~/utils"; import { FormPage } from "./form"; import { login } from "./logic"; @@ -21,63 +22,84 @@ export const RegisterPage = () => { const router = useRouter(); const { t } = useTranslation(); + const { data: info } = useFetch(OidcLogin.query(apiUrl)); if (Platform.OS !== "web" && !apiUrl) return{t("login.username")}
- setUsername(value)} - /> - -{t("login.email")}
- setEmail(value)} /> - -{t("login.password")}
-{t("login.confirm")}
-- {t("login.password-no-match")} -
- )} - {error &&{error}
} -+ {t( + Object.values(info.oidc).length > 0 + ? "login.register-disabled-oidc" + : "login.register-disabled", + )} +
{t("login.username")}
+ setUsername(value)} + /> + +{t("login.email")}
+ setEmail(value)} /> + +{t("login.password")}
+{t("login.confirm")}
++ {t("login.password-no-match")} +
+ )} + {error &&{error}
} +
+