简体   繁体   中英

How to process data before sending to server in next js environment?

I'm currently making some web site which need authentication. I use Next(React) and typescript for web dev.

My goal is to make most of pages as ServerSideRendered or StaticHTML. But I faced problem at the very beginning of development.

I'm currently making login page and overall work flow is below.

  1. Server renders LoginForm include RSA-public-key (for encrypting email and password)
  2. User writes email address and password and click 'Submit Button' in form.
  3. In the web browser, it will be encrypted with RSA public key then sent to server via http request.
  4. The server accept it then create session and tokens then send it into client. (Maybe, I guess it can be achieved by receiving token and routing to next page -- Can you correct it too?)

The problem I faced with is on 3 -- encrypt before fetch. Since it is server side rendered and I didn't put any of react CSR code. It should be encrypted with certain ways. And I ends up with use external script using script tag --put some js files into /public/script/ then import it and run it from browser.

  1. /pages/auth/login.tsx
import React, { Component } from 'react';
import Head from 'next/head';
import { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult  } from 'next';
import { RSAHash } from '../../types/alias';    // RSAHash euquivalent with dtype 'string' just alias
import rsa from '../../lib/server-rsa';         // Server-side RSA encryption module

interface LogInProps {
    encryptKey: RSAHash
}

export default class LogIn extends Component<LogInProps> {
    render() {
        return (
            <>
                <Head>
                    <script type='module' src='/script/user/submitLoginForm.js' async={false}></script>
                </Head>
                <form>
                    <input id='encryptKey' type='hidden' defaultValue={this.props.encryptKey}/> {/* RSA public key */}
                    <label>Email Address :
                        <input id='email' type='email'/>
                    </label>
                    <label>Password :
                        <input id='password' type='password'/>
                    </label>
                    <button type='submit'>Login</button> {/* !!HERE!! */}
                </form>
            </>
        );
    }
}

export const getServerSideProps: GetServerSideProps = async (ctx: GetServerSidePropsContext): Promise<GetServerSidePropsResult<LogInProps>> => {
    // Put RSA public key to props for encryption on browser
    return {
        props: {
            encryptKey: rsa.getPublicKey(),
        },
    };
}
  1. /public/script/user/submitLoginForm.js
import OpenCrypto from "../library/opencrypto.js";

function submitLoginForm() {
    const email = document.getElementById('email').value;
    const password = document.getElementById('password').value;
    const key = document.getElementById('encryptKey').textContent;

    const crypt = new OpenCrypto();
    const emailCipher = crypt.rsaEncrypt(credit, email);
    const passwordCipher = crypt.rsaEncrypt(credit, sha256(password));
    // Also there are function sha256(_) which encrypt to sha256 hash -- not implemented yet-- it is implemented like opencrypto did
    console.log(`Receive data ${email} / ${password}`);
    console.log(`Got cipher`)
    console.log(emailCipher);
    console.log(passwordCipher);
    // Send POST request for login, then receive session. then proceed with web site maybe?
}
  1. /public/script/library/opencrypto.js
// Some external lightweight RSA encryption library

What I want to know is how can I make browser run function submitLoginForm() in tag.

I think I can run it by adding onSubmit="submitLoginForm()" to submit type input . (Position I marked as '!!HERE!!') But it is typescript and it deny to accept string as prop of onSubmit . It shows error Type 'string' is not assignable to type '((event: FormEvent<HTMLButtonElement>) => void) | undefined'. Type 'string' is not assignable to type '((event: FormEvent<HTMLButtonElement>) => void) | undefined'. .

First, Is this right way to run script? Second, If so, how can I put run command in that HTML? Third, Ultimately, is it right approach for the feature? I try to avoid using libraries like next-auth and so on... (But I'm gonna use JWTToken for session)

Least of codes can be wrong since I'm quite new to Web Dev. Appreciate if check that too.

Thank you.

Appendix. I tested rendered page with inspector and checked below.

  1. Login page renders well with getServerSideProps -- means RSA pub token is inserted well.
  2. When checking network panel, I can see external script and it's crypto library is received well with status 200 and I can see the code from browser.

I think you should read through the React docs some more time, because it seems like you haven't fully grasped the concepts. You can save the values of the input fields in the state. See here how forms work in React . No need for document.getElementById , also you can execute your function directly in the component:

export default class LogIn extends Component<LogInProps> {
  state = {
    email: "",
    password: ""
  };

  submitLoginForm(e) {
    e.preventDefault();
    const { email, password } = this.state;
    const crypt = new OpenCrypto();
    const emailCipher = crypt.rsaEncrypt(credit, email);
    const passwordCipher = crypt.rsaEncrypt(credit, sha256(password));
    // Send POST request for login, then receive session. then proceed with web site maybe?
  }

  handleChange(e) {
    const name = e.target.name;
    const value = e.target.value;

    this.setState({ [name]: value });
  }

  render() {
    return (
      <>
        <Head>
          <script type="module" src="/script/user/submitLoginForm.js" async={false}></script>
        </Head>

        <form onSubmit={this.submitLoginForm}>
          <input id="encryptKey" type="hidden" defaultValue={this.props.encryptKey} />{" "}
          {/* RSA public key */}
          <label>
            Email Address :
            <input
              name="email"
              type="email"
              value={this.state.email}
              onChange={this.handleChange}
            />
          </label>
          <label>
            Password :
            <input
              name="password"
              type="password"
              value={this.state.password}
              onChange={this.handleChange}
            />
          </label>
          <button type="submit">Login</button> {/* !!HERE!! */}
        </form>
      </>
    );
  }
}

All you would have to do now is use fetch or axios to send your data to the server.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM