adminjs icon indicating copy to clipboard operation
adminjs copied to clipboard

Unable to receive extra fields from the overridden login form

Open eakenbor opened this issue 2 years ago • 3 comments

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

eakenbor avatar Feb 25 '23 00:02 eakenbor

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

dziraf avatar Mar 01 '23 10:03 dziraf

@dziraf have you tried?

eakenbor avatar Mar 25 '23 14:03 eakenbor

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

  • I removed overrideLogin, you will be able to override login page by doing: componentLoader.override(<path>, 'Login').
  • This change requires an update of @adminjs/express or whatever plugin you're using to allow components.bundle.js to be served publicly in your local development environment. In production it has always been treated as an asset and should've worked there without any issues but locally it was handled by a route which bundled the components dynamically and all internal admin routes required authorization. This one specific route will be served before auth logic.

dziraf avatar Mar 28 '23 10:03 dziraf