🐛 Fix issues with login page

This commit is contained in:
Meier Lukas
2023-07-28 20:45:54 +02:00
parent d7f6bdf417
commit 326395730e
8 changed files with 1104 additions and 997 deletions

View File

@@ -39,7 +39,7 @@
"@mantine/modals": "^6.0.0",
"@mantine/next": "^6.0.0",
"@mantine/notifications": "^6.0.0",
"@next-auth/prisma-adapter": "^1.0.7",
"@next-auth/prisma-adapter": "^1.0.5",
"@nivo/core": "^0.83.0",
"@nivo/line": "^0.83.0",
"@prisma/client": "^5.0.0",
@@ -70,7 +70,7 @@
"i18next": "^22.5.1",
"js-file-download": "^0.4.12",
"next": "13.4.10",
"next-auth": "^4.22.3",
"next-auth": "^4.21.0",
"next-i18next": "^13.0.0",
"nzbget-api": "^0.0.3",
"prismjs": "^1.29.0",

Binary file not shown.

View File

@@ -1,27 +1,18 @@
{
"title": "Welcome back!",
"text": "Please enter your password",
"text": "Please enter your credentials",
"form": {
"fields": {
"username": {
"label": "Username"
},
"password": {
"label": "Password",
"placeholder": "Your password"
"label": "Password"
}
},
"buttons": {
"submit": "Sign in"
}
},
"notifications": {
"checking": {
"title": "Checking your password",
"message": "Your password is being checked..."
},
"correct": {
"title": "Sign in successful, redirecting..."
},
"wrong": {
"title": "The password you entered is incorrect, please try again."
}
}
"alert": "Your credentials are incorrect or this account doesn't exist. Please try again."
}

View File

@@ -1,116 +1,88 @@
import { Button, Container, Paper, PasswordInput, Text, Title } from '@mantine/core';
import { useForm } from '@mantine/form';
import {
Alert,
Button,
Card,
Flex,
PasswordInput,
Stack,
Text,
TextInput,
Title,
} from '@mantine/core';
import { useForm, zodResolver } from '@mantine/form';
import { showNotification, updateNotification } from '@mantine/notifications';
import { IconCheck, IconX } from '@tabler/icons-react';
import { IconAlertCircle, IconAlertTriangle, IconCheck, IconX } from '@tabler/icons-react';
import axios from 'axios';
import { setCookie } from 'cookies-next';
import { signIn } from 'next-auth/react';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useRouter } from 'next/router';
import React from 'react';
import { z } from 'zod';
import { signInSchema } from '~/validations/user';
import { loginNamespaces } from '../tools/server/translation-namespaces';
// TODO: Add links to the wiki articles about the login process.
export default function AuthenticationTitle() {
const router = useRouter();
export default function LoginPage() {
const { t } = useTranslation('authentication/login');
const queryParams = useRouter().query as { error?: 'CredentialsSignin' | (string & {}) };
const form = useForm({
initialValues: {
password: '',
},
const form = useForm<z.infer<typeof signInSchema>>({
validateInputOnChange: true,
validateInputOnBlur: true,
validate: zodResolver(signInSchema),
});
const handleSubmit = (values: z.infer<typeof signInSchema>) => {
signIn('credentials', {
redirect: true,
name: values.name,
password: values.password,
callbackUrl: '/',
});
};
return (
<Container
size="lg"
style={{
height: '100vh',
display: 'flex',
width: '100%',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
<Paper
withBorder
shadow="md"
p={30}
mt={30}
radius="md"
style={{ width: '100%', maxWidth: 420 }}
>
<Title
align="center"
sx={(theme) => ({ fontFamily: `Greycliff CF, ${theme.fontFamily}`, fontWeight: 900 })}
>
<Flex h="100dvh" display="flex" w="100%" direction="column" align="center" justify="center">
<Card withBorder shadow="md" p="xl" radius="md" w="90%" maw={420}>
<Title align="center" weight={900}>
{t('title')}
</Title>
<Text color="dimmed" size="sm" align="center" mt={5}>
<Text color="dimmed" size="sm" align="center" mt={5} mb="md">
{t('text')}
</Text>
<form
onSubmit={form.onSubmit((values) => {
setCookie('password', values.password, {
maxAge: 60 * 60 * 24 * 30,
sameSite: 'lax',
});
showNotification({
id: 'load-data',
loading: true,
title: t('notifications.checking.title'),
message: t('notifications.checking.message'),
autoClose: false,
withCloseButton: false,
});
axios
.post('/api/configs/tryPassword', {
tried: values.password,
})
.then((res) => {
setTimeout(() => {
if (res.data.success === true) {
router.push('/');
updateNotification({
id: 'load-data',
color: 'teal',
title: t('notifications.correct.title'),
message: undefined,
icon: <IconCheck />,
autoClose: 1000,
});
}
if (res.data.success === false) {
updateNotification({
id: 'load-data',
color: 'red',
title: t('notifications.wrong.title'),
message: undefined,
icon: <IconX />,
autoClose: 2000,
});
}
}, 500);
});
})}
>
<PasswordInput
id="password"
label={t('form.fields.password.label')}
placeholder={t('form.fields.password.placeholder') ?? undefined}
required
autoFocus
mt="md"
{...form.getInputProps('password')}
/>
<Button fullWidth type="submit" mt="xl">
{t('form.buttons.submit')}
</Button>
<form onSubmit={form.onSubmit(handleSubmit)}>
<Stack>
<TextInput
variant="filled"
label={t('form.fields.username.label')}
withAsterisk
{...form.getInputProps('name')}
/>
<PasswordInput
id="password"
variant="filled"
label={t('form.fields.password.label')}
withAsterisk
{...form.getInputProps('password')}
/>
<Button fullWidth type="submit">
{t('form.buttons.submit')}
</Button>
{queryParams.error === 'CredentialsSignin' && (
<Alert icon={<IconAlertTriangle size="1rem" />} color="red">
{t('alert')}
</Alert>
)}
</Stack>
</form>
</Paper>
</Container>
</Card>
</Flex>
);
}

View File

@@ -6,6 +6,7 @@ import { type DefaultSession, type NextAuthOptions, getServerSession } from 'nex
import { decode, encode } from 'next-auth/jwt';
import Credentials from 'next-auth/providers/credentials';
import { prisma } from '~/server/db';
import EmptyNextAuthProvider from '~/utils/empty-provider';
import { fromDate, generateSessionToken } from '~/utils/session';
import { signInSchema } from '~/validations/user';
@@ -63,9 +64,6 @@ export const constructAuthOptions = (
where: {
id: user.id,
},
include: {
roles: true,
},
});
session.user.isAdmin = userFromDatabase.isAdmin;
@@ -102,6 +100,10 @@ export const constructAuthOptions = (
strategy: 'database',
maxAge: sessionMaxAgeInSeconds,
},
pages: {
signIn: '/login',
error: '/login',
},
adapter: PrismaAdapter(prisma),
providers: [
Credentials({
@@ -117,9 +119,6 @@ export const constructAuthOptions = (
const data = await signInSchema.parseAsync(credentials);
const user = await prisma.user.findFirst({
include: {
roles: true,
},
where: {
name: data.name,
},
@@ -146,6 +145,7 @@ export const constructAuthOptions = (
};
},
}),
EmptyNextAuthProvider(),
],
jwt: {
async encode(params) {

View File

@@ -0,0 +1,14 @@
import { OAuthConfig } from 'next-auth/providers';
export default function EmptyNextAuthProvider(): OAuthConfig<any> {
return {
id: 'empty',
name: 'Empty',
type: 'oauth',
profile: () => {
throw new Error(
'EmptyNextAuthProvider can not be used and is only a placeholder because credentials authentication can not be used as session authentication without additional providers.'
);
},
};
}

1888
yarn.lock

File diff suppressed because it is too large Load Diff