SoftwareBrothers / adminjs

AdminJS is an admin panel for apps written in node.js
https://adminjs.co
MIT License
8.25k stars 664 forks source link

Unable to receive extra fields from the overridden login form #1434

Open eakenbor opened 1 year ago

eakenbor commented 1 year ago

Contact Details

No response

What happened?

I overrode the Login template so I can add an OTP field to the Email and Password fields. However, I am not able to get the OTP input as a callback in the "authenticate" method.

Here are my codes:

//Login.tsx
import React from 'react';
import styled, { createGlobalStyle } from 'styled-components';
import { useSelector } from 'react-redux';
import {
  Box,
  H5,
  H2,
  Label,
  Illustration,
  Input,
  FormGroup,
  Button,
  Text,
  MessageBox,
  MadeWithLove,
  themeGet,
} from '@adminjs/design-system';
import { useTranslation, ReduxState } from 'adminjs';

const GlobalStyle = createGlobalStyle`
  html, body, #app {
    width: 100%;
    height: 100%;
    margin: 0;
    padding: 0;
  }
`;

const Wrapper = styled(Box)`
  align-items: center;
  justify-content: center;
  flex-direction: column;
  height: 100%;
`;

const StyledLogo = styled.img`
  max-width: 200px;
  margin: ${themeGet('space', 'md')} 0;
`;

export type LoginProps = {
  message?: string;
  action: string;
};

export const Login: React.FC<LoginProps> = (props) => {
  const { action, message } = props;
  const { translateLabel, translateButton, translateProperty, translateMessage } = useTranslation();
  const branding = useSelector((state: ReduxState) => state.branding);

  return (
    <>
      <GlobalStyle />
      <Wrapper flex variant="grey">
        <Box bg="white" height="440px" flex boxShadow="login" width={[1, 2 / 3, 'auto']}>
          <Box
            bg="primary100"
            color="white"
            p="x3"
            width="380px"
            flexGrow={0}
            display={['none', 'none', 'block']}
            position="relative"
          >
            <H2 fontWeight="lighter">{translateLabel('loginWelcome')}</H2>
            <Text fontWeight="lighter" mt="default">
              {translateMessage('loginWelcome')}
            </Text>
            <Text textAlign="center" p="xxl">
              <Box display="inline" mr="default">
                <Illustration variant="Planet" width={82} height={91} />
              </Box>
              <Box display="inline">
                <Illustration variant="Astronaut" width={82} height={91} />
              </Box>
              <Box display="inline" position="relative" top="-20px">
                <Illustration variant="FlagInCog" width={82} height={91} />
              </Box>
            </Text>
          </Box>
          <Box as="form" action={action} method="POST" p="x3" flexGrow={1} width={['100%', '100%', '480px']}>
            <H5 marginBottom="xxl">
              {branding.logo ? <StyledLogo src={branding.logo} alt={branding.companyName} /> : branding.companyName}
            </H5>
            {message && (
              <MessageBox
                my="lg"
                message={message.split(' ').length > 1 ? message : translateMessage(message)}
                variant="danger"
              />
            )}
            <FormGroup>
              <Label required>{translateProperty('email')}</Label>
              <Input name="email" placeholder={translateProperty('email')} />
            </FormGroup>
            <FormGroup>
              <Label required>{translateProperty('password')}</Label>
              <Input
                type="password"
                name="password"
                placeholder={translateProperty('password')}
                autoComplete="new-password"
              />
            </FormGroup>
            <FormGroup>
              <Label required>OTP</Label>
              <Input
                name="otp"
                placeholder="OTP"
              />
            </FormGroup>
            <Text mt="xl" textAlign="center">
              <Button variant="primary">{translateButton('login')}</Button>
            </Text>
            <Text mt="lg" textAlign="center">
              {translateMessage('forgotPasswordQuestion')}{' '}
              <a href="/forgot-password">{translateMessage('forgotPassword')}</a>
            </Text>
          </Box>
        </Box>
        {branding.withMadeWithLove ? (
          <Box mt="xxl">
            <MadeWithLove />
          </Box>
        ) : null}
      </Wrapper>
    </>
  );
};

export default Login;

const authenticate = async (email, password, otp) => {
    console.log(otp) // OTP should appear here
    if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {
        return Promise.resolve(DEFAULT_ADMIN)
    }
    return null
}

const adminRouter = AdminJSExpress.buildAuthenticatedRouter(
    admin,
    {
        // authenticate: adminAuth(),
        authenticate,
        cookieName: 'adminjs',
        cookiePassword: 'sessionsecret',
    },
    null,
    {
        // store: sessionStore,
        resave: true,
        saveUninitialized: true,
        secret: 'sessionsecret',
        cookie: {
            httpOnly: process.env.NODE_ENV === 'production',
            secure: process.env.NODE_ENV === 'production',
        },
        name: 'adminjs',
    }
)

Company

Please can someone explain how I can pass the OTP from the login form to the code?

Bug prevalence

Every time

AdminJS dependencies version

The latest

What browsers do you see the problem on?

No response

Relevant log output

No response

Relevant code that's giving you issues

No response

dziraf commented 1 year ago

When using overrideLogin, the Login page is server-side rendered (same as the default one) which means you won't be able to use React hooks, onClicks, etc but just an HTML form with associated action.

I'll try to see if it's possible to make it non-serverside rendered though

eakenbor commented 1 year ago

@dziraf have you tried?

dziraf commented 1 year ago

@eakenbor This is ready and scheduled for v7 as it will be a breaking change because: