Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove the need for auth provider env #127

Merged
merged 1 commit into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pnpm i
### Setting up the environment

- Copy the env.example file into .env
- Setup google oauth required for auth https://next-auth.js.org/providers/google
- Setup google oauth required for auth https://next-auth.js.org/providers/google or Email provider by setting SMTP details
- Login to minio console using `splitpro` user and password `password` and [create access keys](http://localhost:9001/access-keys/new-account) and the R2 related env variables

### Run the app
Expand Down
9 changes: 3 additions & 6 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,6 @@ import pwa from 'next-pwa';
// @ts-ignore
import nextra from 'nextra';

import { fileURLToPath } from 'node:url';
import createJiti from 'jiti';
const jiti = createJiti(fileURLToPath(import.meta.url));

jiti('./src/env.js');

// eslint-disable-next-line @typescript-eslint/no-unsafe-call
const withPwa = pwa({
dest: 'public',
Expand All @@ -28,6 +22,9 @@ const withPwa = pwa({
const config = {
reactStrictMode: true,
output: process.env.DOCKER_OUTPUT ? 'standalone' : undefined,
experimental: {
instrumentationHook: true,
},
/**
* If you are using `appDir` then you must comment the below `i18n` config out.
*
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"framer-motion": "^11.0.3",
"geist": "^1.2.1",
"input-otp": "^1.2.3",
"jiti": "^1.21.6",
"lucide-react": "^0.312.0",
"nanoid": "^5.0.6",
"next": "^14.0.4",
Expand Down Expand Up @@ -106,4 +105,4 @@
"seed": "tsx prisma/seed.ts"
},
"packageManager": "pnpm@8.9.2"
}
}
3 changes: 0 additions & 3 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions src/env.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ export const env = createEnv({
// VERCEL_URL doesn't include `https` so it cant be validated as a URL
process.env.VERCEL ? z.string() : z.string().url(),
),
AUTH_PROVIDERS: z.string(),
ENABLE_SENDING_INVITES: z.boolean(),
FROM_EMAIL: z.string().optional(),
EMAIL_SERVER_HOST: z.string().optional(),
Expand Down Expand Up @@ -62,7 +61,6 @@ export const env = createEnv({
NODE_ENV: process.env.NODE_ENV,
NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
NEXTAUTH_URL: process.env.NEXTAUTH_URL,
AUTH_PROVIDERS: process.env.AUTH_PROVIDERS,
ENABLE_SENDING_INVITES: process.env.ENABLE_SENDING_INVITES === 'true',
FROM_EMAIL: process.env.FROM_EMAIL,
EMAIL_SERVER_HOST: process.env.EMAIL_SERVER_HOST,
Expand Down
12 changes: 12 additions & 0 deletions src/instrumentation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Add things here to be executed during server startup.
*
* more details here: https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation
*/
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
console.log('Registering instrumentation');
const { validateAuthEnv } = await import('./server/auth');
validateAuthEnv();
}
}
70 changes: 43 additions & 27 deletions src/pages/auth/signin.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use client';
import { signIn } from 'next-auth/react';
import { type ClientSafeProvider, getProviders, signIn } from 'next-auth/react';
import Head from 'next/head';
import { Button } from '~/components/ui/button';
import Image from 'next/image';
Expand Down Expand Up @@ -39,15 +39,33 @@ const otpSchema = z.object({
otp: z.string({ required_error: 'OTP is required' }).length(5, { message: 'Invalid OTP' }),
});

const providerSvgs = {
github: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 496 512"
className="h-4 w-4 fill-primary-foreground "
>
<path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3 .3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5 .3-6.2 2.3zm44.2-1.7c-2.9 .7-4.9 2.6-4.6 4.9 .3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3 .7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3 .3 2.9 2.3 3.9 1.6 1 3.6 .7 4.3-.7 .7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3 .7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3 .7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z" />
</svg>
),
google: (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 488 512"
className="h-4 w-4 fill-primary-foreground"
>
<path d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z" />
</svg>
),
};

const Home: NextPage<{ authProviders: string; feedbackEmail: string }> = ({
authProviders,
const Home: NextPage<{ feedbackEmail: string; providers: ClientSafeProvider[] }> = ({
providers,
feedbackEmail,
}) => {
const [emailStatus, setEmailStatus] = useState<'idle' | 'sending' | 'success'>('idle');

const providers = authProviders.split(',').map((provider) => provider.trim().toUpperCase());

const emailForm = useForm<z.infer<typeof emailSchema>>({
resolver: zodResolver(emailSchema),
});
Expand Down Expand Up @@ -85,30 +103,26 @@ const Home: NextPage<{ authProviders: string; feedbackEmail: string }> = ({
<div className="mb-10 flex items-center gap-4">
<p className="text-3xl text-primary">SplitPro</p>
</div>
{providers?.includes('GOOGLE') && (
<Button
className="mx-auto flex w-[300px] items-center gap-2 bg-white hover:bg-gray-100 focus:bg-gray-100"
onClick={() => signIn('google')}
>
<Image
alt="Google logo"
loading="lazy"
height="15"
width="15"
id="provider-logo-dark"
src="https://authjs.dev/img/providers/google.svg"
/>
Continue with Google
</Button>
)}
{providers
.filter((provider) => provider.id !== 'email')
.map((provider) => (
<Button
className="mx-auto flex w-[300px] items-center gap-3 bg-white hover:bg-gray-100 focus:bg-gray-100"
onClick={() => signIn(provider.id)}
key={provider.id}
>
{providerSvgs[provider.id as keyof typeof providerSvgs]}
Continue with {provider.name}
</Button>
))}
{providers && providers.length === 2 && (
<div className="mt-6 flex w-[300px] items-center justify-between gap-2">
<p className=" z-10 ml-[150px] -translate-x-1/2 bg-background px-4 text-sm">or</p>
<div className="absolute h-[1px] w-[300px] bg-gradient-to-r from-zinc-800 via-zinc-300 to-zinc-800"></div>
</div>
)}
{providers?.includes('EMAIL') &&
(emailStatus === 'success' ? (
{providers.find((provider) => provider.id === 'email') ? (
emailStatus === 'success' ? (
<>
<p className="mt-6 w-[300px] text-center text-sm">
We have sent an email with the OTP. Please check your inbox
Expand Down Expand Up @@ -181,10 +195,10 @@ const Home: NextPage<{ authProviders: string; feedbackEmail: string }> = ({
</form>
</Form>
</>
))}
)
) : null}
<p className="mt-6 w-[300px] text-center text-sm text-muted-foreground">
Trouble logging in? contact

<br />
<a className="underline" href={'mailto:' + feedbackEmail}>
{feedbackEmail ?? ''}
Expand All @@ -200,7 +214,9 @@ export default Home;

export const getServerSideProps: GetServerSideProps = async (context) => {
const session = await getServerAuthSession(context);
const feedbackEmail = process.env.FEEDBACK_EMAIL;
const providers = await getProviders();

console.log('providers', providers);

if (session) {
return {
Expand All @@ -213,8 +229,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => {

return {
props: {
authProviders: env.AUTH_PROVIDERS,
feedbackEmail: env.FEEDBACK_EMAIL ?? '',
providers: Object.values(providers ?? {}),
},
};
};
25 changes: 20 additions & 5 deletions src/server/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,8 @@ export const getServerAuthSessionForSSG = async (context: GetServerSidePropsCont
*/
function getProviders() {
const providersList = [];
const envProviders = env.AUTH_PROVIDERS?.split(',').map((provider) =>
provider.trim().toUpperCase(),
);

if (envProviders?.includes('GOOGLE') && env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
providersList.push(
GoogleProvider({
clientId: env.GOOGLE_CLIENT_ID,
Expand All @@ -122,7 +119,7 @@ function getProviders() {
);
}

if (envProviders?.includes('EMAIL')) {
if (env.EMAIL_SERVER_HOST) {
providersList.push(
EmailProvider({
from: env.FROM_EMAIL,
Expand All @@ -149,3 +146,21 @@ function getProviders() {

return providersList;
}

/**
* Validates the environment variables that are related to authentication.
* this will check if atleat one provider is set properly.
*
* this function should be updated if new providers are added.
*/
export function validateAuthEnv() {
console.log('Validating auth env');
if (!process.env.SKIP_ENV_VALIDATION) {
const providers = getProviders();
if (providers.length === 0) {
throw new Error(
'No authentication providers are configured, at least one is required. Learn more here: https://github.com/oss-apps/split-pro?tab=readme-ov-file#setting-up-the-environment',
);
}
}
}