diff --git a/apps/browser-extension/src/SignInPage.tsx b/apps/browser-extension/src/SignInPage.tsx index f1899d5a..1d849028 100644 --- a/apps/browser-extension/src/SignInPage.tsx +++ b/apps/browser-extension/src/SignInPage.tsx @@ -80,11 +80,7 @@ export default function SignInPage() { break; } if (loginError) { - if (loginError.data?.code == "UNAUTHORIZED") { - errorMessage = "Wrong username or password"; - } else { - errorMessage = loginError.message; - } + errorMessage = loginError.message || "Wrong username or password"; } return ( diff --git a/apps/web/components/signin/CredentialsForm.tsx b/apps/web/components/signin/CredentialsForm.tsx index a35b768f..313dc7c5 100644 --- a/apps/web/components/signin/CredentialsForm.tsx +++ b/apps/web/components/signin/CredentialsForm.tsx @@ -35,6 +35,8 @@ function SignIn() { const [signinError, setSigninError] = useState(""); const router = useRouter(); const searchParams = useSearchParams(); + const clientConfig = useClientConfig(); + const oAuthError = searchParams.get("error"); if (oAuthError && !signinError) { setSigninError(`${OAUTH_FAILED} ${oAuthError}`); @@ -44,6 +46,19 @@ function SignIn() { resolver: zodResolver(signInSchema), }); + if (clientConfig.auth.disablePasswordAuth) { + return ( + <> + {signinError && ( +

{signinError}

+ )} +

+ Password authentication is currently disabled. +

+ + ); + } + return (
{clientConfig.auth.disableSignups || - clientConfig.auth.disablePasswordSignups ? ( + clientConfig.auth.disablePasswordAuth ? (

Signups are currently disabled.

) : ( diff --git a/apps/web/lib/clientConfig.tsx b/apps/web/lib/clientConfig.tsx index 90e6d35c..c5d206e3 100644 --- a/apps/web/lib/clientConfig.tsx +++ b/apps/web/lib/clientConfig.tsx @@ -6,7 +6,7 @@ export const ClientConfigCtx = createContext({ demoMode: undefined, auth: { disableSignups: false, - disablePasswordSignups: false, + disablePasswordAuth: false, }, inference: { inferredTagLang: "english", diff --git a/docs/docs/03-configuration.md b/docs/docs/03-configuration.md index d1b587ad..3d674d63 100644 --- a/docs/docs/03-configuration.md +++ b/docs/docs/03-configuration.md @@ -26,9 +26,9 @@ When setting up OAuth, the allowed redirect URLs configured at the provider shou ::: | Name | Required | Default | Description | -| ------------------------------------------- | -------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| ------------------------------------------- | -------- | ---------------------- |---------------------------------------------------------------------------------------------------------------------------------------------------------------------| | DISABLE_SIGNUPS | No | false | If enabled, no new signups will be allowed and the signup button will be disabled in the UI | -| DISABLE_PASSWORD_SIGNUPS | No | false | If enabled, only signups using OAuth are allowed and the signup button for a local account will be disabled in the UI | +| DISABLE_PASSWORD_AUTH | No | false | If enabled, only signups and logins using OAuth are allowed and the signup button and login form for local accounts will be disabled in the UI | | OAUTH_WELLKNOWN_URL | No | Not set | The "wellknown Url" for openid-configuration as provided by the OAuth provider | | OAUTH_CLIENT_SECRET | No | Not set | The "Client Secret" as provided by the OAuth provider | | OAUTH_CLIENT_ID | No | Not set | The "Client ID" as provided by the OAuth provider | diff --git a/packages/shared/config.ts b/packages/shared/config.ts index 288becab..44b7e26d 100644 --- a/packages/shared/config.ts +++ b/packages/shared/config.ts @@ -10,7 +10,7 @@ const stringBool = (defaultValue: string) => const allEnv = z.object({ API_URL: z.string().url().default("http://localhost:3000"), DISABLE_SIGNUPS: stringBool("false"), - DISABLE_PASSWORD_SIGNUPS: stringBool("false"), + DISABLE_PASSWORD_AUTH: stringBool("false"), OAUTH_ALLOW_DANGEROUS_EMAIL_ACCOUNT_LINKING: stringBool("false"), OAUTH_WELLKNOWN_URL: z.string().url().optional(), OAUTH_CLIENT_SECRET: z.string().optional(), @@ -54,7 +54,7 @@ const serverConfigSchema = allEnv.transform((val) => { apiUrl: val.API_URL, auth: { disableSignups: val.DISABLE_SIGNUPS, - disablePasswordSignups: val.DISABLE_PASSWORD_SIGNUPS, + disablePasswordAuth: val.DISABLE_PASSWORD_AUTH, oauth: { allowDangerousEmailAccountLinking: val.OAUTH_ALLOW_DANGEROUS_EMAIL_ACCOUNT_LINKING, @@ -114,7 +114,7 @@ export const clientConfig = { demoMode: serverConfig.demoMode, auth: { disableSignups: serverConfig.auth.disableSignups, - disablePasswordSignups: serverConfig.auth.disablePasswordSignups, + disablePasswordAuth: serverConfig.auth.disablePasswordAuth, }, inference: { inferredTagLang: serverConfig.inference.inferredTagLang, diff --git a/packages/trpc/auth.ts b/packages/trpc/auth.ts index 846c07b6..39aebd3b 100644 --- a/packages/trpc/auth.ts +++ b/packages/trpc/auth.ts @@ -3,6 +3,7 @@ import * as bcrypt from "bcryptjs"; import { db } from "@hoarder/db"; import { apiKeys } from "@hoarder/db/schema"; +import serverConfig from "@hoarder/shared/config"; // API Keys @@ -79,6 +80,9 @@ export async function hashPassword(password: string) { } export async function validatePassword(email: string, password: string) { + if (serverConfig.auth.disablePasswordAuth) { + throw new Error("Password authentication is currently disabled"); + } const user = await db.query.users.findFirst({ where: (u, { eq }) => eq(u.email, email), }); diff --git a/packages/trpc/routers/apiKeys.ts b/packages/trpc/routers/apiKeys.ts index 81e3bb2b..b7468dd2 100644 --- a/packages/trpc/routers/apiKeys.ts +++ b/packages/trpc/routers/apiKeys.ts @@ -3,6 +3,7 @@ import { and, eq } from "drizzle-orm"; import { z } from "zod"; import { apiKeys } from "@hoarder/db/schema"; +import serverConfig from "@hoarder/shared/config"; import { authenticateApiKey, generateApiKey, validatePassword } from "../auth"; import { authedProcedure, publicProcedure, router } from "../index"; @@ -74,6 +75,13 @@ export const apiKeysAppRouter = router({ .output(zApiKeySchema) .mutation(async ({ input }) => { let user; + // Special handling as otherwise the extension would show "username or password is wrong" + if (serverConfig.auth.disablePasswordAuth) { + throw new TRPCError({ + message: "Password authentication is currently disabled", + code: "FORBIDDEN", + }); + } try { user = await validatePassword(input.email, input.password); } catch (e) { diff --git a/packages/trpc/routers/users.ts b/packages/trpc/routers/users.ts index 736e7e2f..87d0fa2d 100644 --- a/packages/trpc/routers/users.ts +++ b/packages/trpc/routers/users.ts @@ -31,9 +31,9 @@ export const usersAppRouter = router({ .mutation(async ({ input, ctx }) => { if ( serverConfig.auth.disableSignups || - serverConfig.auth.disablePasswordSignups + serverConfig.auth.disablePasswordAuth ) { - const errorMessage = serverConfig.auth.disablePasswordSignups + const errorMessage = serverConfig.auth.disablePasswordAuth ? "Local Signups are disabled in the server config. Use OAuth instead!" : "Signups are disabled in server config"; throw new TRPCError({