feat: bypass local login & block creation

This commit is contained in:
diced
2023-07-21 17:27:34 -07:00
parent 8b74b0b195
commit e82ef44b2a
5 changed files with 76 additions and 28 deletions

View File

@@ -52,6 +52,8 @@ export const PROP_TO_ENV: Record<string, string> = {
'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,

View File

@@ -4,12 +4,20 @@ import { Config } from './validate';
export type SafeConfig = Omit<Config, 'oauth' | 'datasource' | 'core'> & {
oauthEnabled: ReturnType<typeof enabled>;
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;
}

View File

@@ -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(),

View File

@@ -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');
}

View File

@@ -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<typeof get
const router = useRouter();
const { data, isLoading, mutate } = useSWR<Response['/api/user']>('/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,14 +67,30 @@ export default function Login({ config }: InferGetServerSidePropsType<typeof get
}
};
useEffect(() => {
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 && <LoadingOverlay visible />}
<Center h='100vh'>
<div>
<Title order={1} size={50} align='center'>
<b>Zipline</b>
</Title>
{showLocalLogin && (
<>
<form onSubmit={form.onSubmit(onSubmit)}>
<Stack my='sm'>
<TextInput
@@ -84,18 +112,20 @@ export default function Login({ config }: InferGetServerSidePropsType<typeof get
</form>
{eitherTrue(config.features.oauthRegistration, config.features.userRegistration) && (
<Text size='sm' align='center' color='dimmed'>
<Text size='sm' my='xs' align='center' color='dimmed'>
OR
</Text>
)}
<Stack my='xs'>
{config.features.userRegistration && (
<Button size='lg' fullWidth variant='outline' color='gray'>
Sign up
</Button>
)}
</>
)}
<Stack my='xs'>
{config.oauthEnabled.discord && (
<Button
size='lg'