From 6219d885c1713c5f47f79398d72e8f79be13f250 Mon Sep 17 00:00:00 2001 From: Carl Furrow Date: Tue, 11 Nov 2025 11:47:59 -0500 Subject: [PATCH 1/4] chore: do not require google env vars make them optional for those that do not want or need them. --- .env.example | 3 ++- src/env.ts | 4 ++-- src/features/auth/lib/better-auth.ts | 34 +++++++++++++++------------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index 9df7f427..bf94aab1 100644 --- a/.env.example +++ b/.env.example @@ -13,9 +13,10 @@ BETTER_AUTH_URL=http://localhost:3000 # Generate a secure random string using: openssl rand -base64 32 BETTER_AUTH_SECRET=your-secret-key-here -# Google OAuth +# Google OAuth (Optional) # Get these from Google Cloud Console: https://console.cloud.google.com # Required scopes: email, profile +# Leave empty or remove these lines to disable Google OAuth GOOGLE_CLIENT_ID="your-google-client-id.apps.googleusercontent.com" GOOGLE_CLIENT_SECRET="your-google-client-secret" diff --git a/src/env.ts b/src/env.ts index d79ad759..90685f72 100644 --- a/src/env.ts +++ b/src/env.ts @@ -12,8 +12,8 @@ export const env = createEnv({ server: { BETTER_AUTH_URL: z.string().url(), DATABASE_URL: z.string().url(), - GOOGLE_CLIENT_ID: z.string().min(1), - GOOGLE_CLIENT_SECRET: z.string().min(1), + GOOGLE_CLIENT_ID: z.string().min(1).optional(), + GOOGLE_CLIENT_SECRET: z.string().min(1).optional(), NODE_ENV: z.enum(["development", "production", "test"]), BETTER_AUTH_SECRET: z.string().min(1), OPENPANEL_SECRET_KEY: z.string().optional(), diff --git a/src/features/auth/lib/better-auth.ts b/src/features/auth/lib/better-auth.ts index d91e7be1..2e78d529 100644 --- a/src/features/auth/lib/better-auth.ts +++ b/src/features/auth/lib/better-auth.ts @@ -123,20 +123,22 @@ export const auth = betterAuth({ }, enabled: true, }, - socialProviders: { - google: { - enabled: true, - clientId: env.GOOGLE_CLIENT_ID, - clientSecret: env.GOOGLE_CLIENT_SECRET, - mapProfileToUser: async (profile) => { - return { - ...profile, - email: profile.email, - firstName: profile.given_name, - lastName: profile.family_name, - role: UserRole.user, - }; - }, - }, - }, + socialProviders: env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET + ? { + google: { + enabled: true, + clientId: env.GOOGLE_CLIENT_ID, + clientSecret: env.GOOGLE_CLIENT_SECRET, + mapProfileToUser: async (profile) => { + return { + ...profile, + email: profile.email, + firstName: profile.given_name, + lastName: profile.family_name, + role: UserRole.user, + }; + }, + }, + } + : {}, }); From 2f76bba7de2926a7a71c90414b260b55fe8cfc4b Mon Sep 17 00:00:00 2001 From: Carl Furrow Date: Tue, 11 Nov 2025 14:53:20 -0500 Subject: [PATCH 2/4] doc: update SELF-HOSTING.md to indicate that the google env vars are optional --- docs/SELF-HOSTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/SELF-HOSTING.md b/docs/SELF-HOSTING.md index a90cffd4..2eb34f96 100644 --- a/docs/SELF-HOSTING.md +++ b/docs/SELF-HOSTING.md @@ -76,6 +76,10 @@ BETTER_AUTH_SECRET=your-secret-key-here # Optional: Seed sample data on first run SEED_SAMPLE_DATA=true + +# Optional: Google OAuth (can be omitted if not using Google sign-in) +# GOOGLE_CLIENT_ID="your-google-client-id.apps.googleusercontent.com" +# GOOGLE_CLIENT_SECRET="your-google-client-secret" ``` #### 4. Customize Sample Data (Optional) From 31a8a384825d269ae305095cc909879beae85d95 Mon Sep 17 00:00:00 2001 From: Carl Furrow Date: Tue, 11 Nov 2025 14:58:10 -0500 Subject: [PATCH 3/4] feat: create is-google-oauth-enabled helper function --- src/features/auth/lib/is-google-oauth-enabled.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/features/auth/lib/is-google-oauth-enabled.ts diff --git a/src/features/auth/lib/is-google-oauth-enabled.ts b/src/features/auth/lib/is-google-oauth-enabled.ts new file mode 100644 index 00000000..c241e3a8 --- /dev/null +++ b/src/features/auth/lib/is-google-oauth-enabled.ts @@ -0,0 +1,9 @@ +import { env } from "@/env"; + +/** + * Check if Google OAuth is enabled by verifying both required environment variables are set + * @returns true if Google OAuth is enabled, false otherwise + */ +export function isGoogleOAuthEnabled(): boolean { + return !!(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET); +} From ceea53dcf6ecb0bda55c48582f0d691e22929d11 Mon Sep 17 00:00:00 2001 From: Carl Furrow Date: Tue, 11 Nov 2025 14:58:39 -0500 Subject: [PATCH 4/4] chore: hide google auth from login/signup forms if google oauth is not enabled --- .../(app)/auth/(auth-layout)/signin/page.tsx | 5 +++- .../(app)/auth/(auth-layout)/signup/page.tsx | 4 ++- .../auth/signin/ui/CredentialsLoginForm.tsx | 28 ++++++++++------- src/features/auth/signup/ui/signup-form.tsx | 30 ++++++++++++------- 4 files changed, 44 insertions(+), 23 deletions(-) diff --git a/app/[locale]/(app)/auth/(auth-layout)/signin/page.tsx b/app/[locale]/(app)/auth/(auth-layout)/signin/page.tsx index ae0cbb2a..e051835b 100644 --- a/app/[locale]/(app)/auth/(auth-layout)/signin/page.tsx +++ b/app/[locale]/(app)/auth/(auth-layout)/signin/page.tsx @@ -1,5 +1,8 @@ import { CredentialsLoginForm } from "@/features/auth/signin/ui/CredentialsLoginForm"; +import { isGoogleOAuthEnabled } from "@/features/auth/lib/is-google-oauth-enabled"; export default async function AuthSignInPage() { - return ; + const googleOAuthEnabled = isGoogleOAuthEnabled(); + + return ; } diff --git a/app/[locale]/(app)/auth/(auth-layout)/signup/page.tsx b/app/[locale]/(app)/auth/(auth-layout)/signup/page.tsx index 824163b3..e2279c65 100644 --- a/app/[locale]/(app)/auth/(auth-layout)/signup/page.tsx +++ b/app/[locale]/(app)/auth/(auth-layout)/signup/page.tsx @@ -3,6 +3,7 @@ import Link from "next/link"; import { getI18n } from "locales/server"; import { paths } from "@/shared/constants/paths"; import { SignUpForm } from "@/features/auth/signup/ui/signup-form"; +import { isGoogleOAuthEnabled } from "@/features/auth/lib/is-google-oauth-enabled"; export const metadata = { title: "Sign Up - Workout.cool", @@ -11,6 +12,7 @@ export const metadata = { export default async function AuthSignUpPage() { const t = await getI18n(); + const googleOAuthEnabled = isGoogleOAuthEnabled(); return (
@@ -19,7 +21,7 @@ export default async function AuthSignUpPage() {

{t("register_description")}

- +

diff --git a/src/features/auth/signin/ui/CredentialsLoginForm.tsx b/src/features/auth/signin/ui/CredentialsLoginForm.tsx index 138d792b..5d84701f 100644 --- a/src/features/auth/signin/ui/CredentialsLoginForm.tsx +++ b/src/features/auth/signin/ui/CredentialsLoginForm.tsx @@ -16,7 +16,11 @@ import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import { Alert, AlertDescription } from "@/components/ui/alert"; -export function CredentialsLoginForm({ className, ...props }: React.ComponentPropsWithoutRef<"form">) { +type CredentialsLoginFormProps = React.ComponentPropsWithoutRef<"form"> & { + googleOAuthEnabled?: boolean; +}; + +export function CredentialsLoginForm({ className, googleOAuthEnabled = false, ...props }: CredentialsLoginFormProps) { const t = useI18n(); const searchParams = useSearchParams(); const isResetSuccess = searchParams.get("reset") === "success"; @@ -66,16 +70,20 @@ export function CredentialsLoginForm({ className, ...props }: React.ComponentPro

-
-
- -
-
- {t("commons.or")} -
-
+ {googleOAuthEnabled && ( + <> +
+
+ +
+
+ {t("commons.or")} +
+
- + + + )}
{t("commons.dont_have_account")}{" "} diff --git a/src/features/auth/signup/ui/signup-form.tsx b/src/features/auth/signup/ui/signup-form.tsx index 091f5234..bbb90b7f 100644 --- a/src/features/auth/signup/ui/signup-form.tsx +++ b/src/features/auth/signup/ui/signup-form.tsx @@ -15,7 +15,11 @@ import { signUpSchema } from "../schema/signup.schema"; import type { SignUpSchema } from "../schema/signup.schema"; -export const SignUpForm = () => { +type SignUpFormProps = { + googleOAuthEnabled?: boolean; +}; + +export const SignUpForm = ({ googleOAuthEnabled = false }: SignUpFormProps) => { const t = useI18n(); const searchParams = useSearchParams(); const redirectUrl = searchParams.get("redirect"); @@ -120,18 +124,22 @@ export const SignUpForm = () => { {t("commons.submit")} -
-
- -
-
- {t("commons.or")} + {googleOAuthEnabled && ( +
+
+ +
+
+ {t("commons.or")} +
-
+ )} -
- -
+ {googleOAuthEnabled && ( +
+ +
+ )}
{t("commons.already_have_account")}{" "}