Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
98 views
in Technique[技术] by (71.8m points)

javascript - 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'..

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.
question from:https://stackoverflow.com/questions/65933166/how-to-process-data-before-sending-to-server-in-next-js-environment

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...