From e82ef44b2abaae039de37b86c4bc46e607fa389d Mon Sep 17 00:00:00 2001 From: diced Date: Fri, 21 Jul 2023 17:27:34 -0700 Subject: [PATCH] feat: bypass local login & block creation --- src/lib/config/read.ts | 6 +++ src/lib/config/safe.ts | 8 ++++ src/lib/config/validate.ts | 2 + src/lib/oauth/withOAuth.ts | 2 + src/pages/auth/login.tsx | 86 +++++++++++++++++++++++++------------- 5 files changed, 76 insertions(+), 28 deletions(-) diff --git a/src/lib/config/read.ts b/src/lib/config/read.ts index d05e489f..54f24618 100644 --- a/src/lib/config/read.ts +++ b/src/lib/config/read.ts @@ -52,6 +52,8 @@ export const PROP_TO_ENV: Record = { 'website.defaultAvatar': 'WEBSITE_DEFAULT_AVATAR', 'website.disableMediaPreview': 'WEBSITE_DISABLE_MEDIA_PREVIEW', + 'oauth.bypassLocalLogin': 'OAUTH_BYPASS_LOCAL_LOGIN', + 'oauth.loginOnly': 'OAUTH_LOGIN_ONLY', 'oauth.discord.clientId': 'OAUTH_DISCORD_CLIENT_ID', 'oauth.discord.clientSecret': 'OAUTH_DISCORD_CLIENT_SECRET', 'oauth.github.clientId': 'OAUTH_GITHUB_CLIENT_ID', @@ -107,6 +109,8 @@ export function readEnv() { env(PROP_TO_ENV['website.defaultAvatar'], 'website.defaultAvatar', 'string'), env(PROP_TO_ENV['website.disableMediaPreview'], 'website.disableMediaPreview', 'boolean'), + env(PROP_TO_ENV['oauth.bypassLocalLogin'], 'oauth.bypassLocalLogin', 'boolean'), + env(PROP_TO_ENV['oauth.loginOnly'], 'oauth.loginOnly', 'boolean'), env(PROP_TO_ENV['oauth.discord.clientId'], 'oauth.discord.clientId', 'string'), env(PROP_TO_ENV['oauth.discord.clientSecret'], 'oauth.discord.clientSecret', 'string'), env(PROP_TO_ENV['oauth.github.clientId'], 'oauth.github.clientId', 'string'), @@ -162,6 +166,8 @@ export function readEnv() { disableMediaPreview: undefined, }, oauth: { + bypassLocalLogin: undefined, + loginOnly: undefined, discord: { clientId: undefined, clientSecret: undefined, diff --git a/src/lib/config/safe.ts b/src/lib/config/safe.ts index 17eaa3e8..dfd8e1ad 100644 --- a/src/lib/config/safe.ts +++ b/src/lib/config/safe.ts @@ -4,12 +4,20 @@ import { Config } from './validate'; export type SafeConfig = Omit & { oauthEnabled: ReturnType; + oauth: { + bypassLocalLogin: boolean; + loginOnly: boolean; + } }; export function safeConfig(): SafeConfig { const { datasource, core, oauth, ...rest } = config; (rest as SafeConfig).oauthEnabled = enabled(config); + (rest as SafeConfig).oauth = { + bypassLocalLogin: oauth.bypassLocalLogin, + loginOnly: oauth.loginOnly, + }; return rest as SafeConfig; } diff --git a/src/lib/config/validate.ts b/src/lib/config/validate.ts index 50a9a595..8556fb46 100644 --- a/src/lib/config/validate.ts +++ b/src/lib/config/validate.ts @@ -116,6 +116,8 @@ export const schema = z.object({ disableMediaPreview: z.boolean().default(false), }), oauth: z.object({ + bypassLocalLogin: z.boolean().default(false), + loginOnly: z.boolean().default(false), discord: z .object({ clientId: z.string(), diff --git a/src/lib/oauth/withOAuth.ts b/src/lib/oauth/withOAuth.ts index 950c517a..3b159ecf 100644 --- a/src/lib/oauth/withOAuth.ts +++ b/src/lib/oauth/withOAuth.ts @@ -175,6 +175,8 @@ export const withOAuth = }); return res.redirect('/dashboard'); + } else if (config.oauth.loginOnly) { + return res.badRequest('Can\'t create users through oauth.'); } else if (existingUser) { return res.badRequest('This username is already taken'); } diff --git a/src/pages/auth/login.tsx b/src/pages/auth/login.tsx index 4f3e607b..cce423a7 100644 --- a/src/pages/auth/login.tsx +++ b/src/pages/auth/login.tsx @@ -1,10 +1,11 @@ import { Response } from '@/lib/api/response'; import { config } from '@/lib/config'; +import { SafeConfig } from '@/lib/config/safe'; import { getZipline } from '@/lib/db/models/zipline'; import { fetchApi } from '@/lib/fetchApi'; import { withSafeConfig } from '@/lib/middleware/next/withSafeConfig'; import { eitherTrue, isTruthy } from '@/lib/primitive'; -import { Button, Center, PasswordInput, Stack, Text, TextInput, Title } from '@mantine/core'; +import { Button, Center, LoadingOverlay, PasswordInput, Stack, Text, TextInput, Title } from '@mantine/core'; import { hasLength, useForm } from '@mantine/form'; import { IconBrandDiscordFilled, @@ -22,6 +23,17 @@ export default function Login({ config }: InferGetServerSidePropsType('/api/user'); + const showLocalLogin = + router.query.local === 'true' || + !( + config.oauth.bypassLocalLogin && Object.values(config.oauthEnabled).filter((x) => x === true).length > 0 + ); + + const willRedirect = + config.oauth.bypassLocalLogin && + Object.values(config.oauthEnabled).filter((x) => x === true).length === 1 && + router.query.local !== 'true'; + useEffect(() => { if (data?.user) { router.push('/dashboard'); @@ -55,47 +67,65 @@ export default function Login({ config }: InferGetServerSidePropsType { + if (willRedirect) { + const provider = Object.keys(config.oauthEnabled).find( + (x) => config.oauthEnabled[x as keyof SafeConfig['oauthEnabled']] === true + ); + + if (provider) { + router.push(`/api/auth/oauth/${provider}`); + } + } + }, []); + return ( <> + {willRedirect && !showLocalLogin && } +
<b>Zipline</b> -
- - + {showLocalLogin && ( + <> + + + - + - - - + +
+ - {eitherTrue(config.features.oauthRegistration, config.features.userRegistration) && ( - - OR - + {eitherTrue(config.features.oauthRegistration, config.features.userRegistration) && ( + + OR + + )} + + {config.features.userRegistration && ( + + )} + )} - {config.features.userRegistration && ( - - )} - {config.oauthEnabled.discord && (