Next.js SSR将未经身份验证的用户重定向到登录页面

时间:2020-11-06 18:18:26

标签: reactjs next.js

我想在当前的身份验证实现中重定向未经身份验证的用户。例如profile.js页面仅适用于经过身份验证的用户。当前未经身份验证的用户可以访问页面profile.js。如果用户未通过身份验证,则应将其重定向到登录页面。

userContext.js

import React, { useEffect } from 'react';
import cookie from 'js-cookie';
import {firebase} from './firebase-client';
import Router from 'next/router';


export const UserContext = React.createContext();
const tokenName = 'firebaseToken';
const UserProvider = ({ children }) => {
  const logOut = async () => {
    await firebase
      .auth()
      .signOut()
      .then(() => {
        console.log('User logged out.');
        return null
      })
      .catch((e) => {
        console.error(e)
      })
  }
  const emailLogin = async (email, password) => {
    await firebase
      .auth()
      .signInWithEmailAndPassword(email, password)
      .then(() => {
        console.log('User logged in.');
        Router.push('/')
      })
      .catch((err) => {
        console.log(err);
      });
  };
  const onAuthStateChange = () => {
    return firebase.auth().onAuthStateChanged(async (user) => {
      if (user) {
        const token = await user.getIdToken();
        cookie.set(tokenName, token, { expires: 14 });
      } else {
        cookie.remove(tokenName);
        Router.push('/auth')
      }
    });
  };
  useEffect(() => {
    const unsubscribe = onAuthStateChange();
    return () => {
      unsubscribe();
    };
  }, []);
  return <UserContext.Provider value={{ emailLogin, logOut }}>{children}</UserContext.Provider>;
};

export default UserProvider;

api/validate.js

import admin  from '../../lib/firebase-admin';

const validate = async (token) => {
  const decodedToken = await admin.auth().verifyIdToken(token, true);
  let userData;
  const user = await admin.auth().getUser(decodedToken.uid);
  admin
  .database()
  .ref(`projects`)
  .on('value', snapshot => {
    userData = { ...snapshot.val() }
  })
  const result = {
    user: {
      uid: user.uid,
      email: user.email,
      emailVerified: user.emailVerified,
      userData: userData
    },
  };
  return result;
};

export default async (req, res) => {
  try {
    const { token } = JSON.parse(req.headers.authorization || '{}');
    if (!token) {
      return res.status(403).send({
        errorCode: 403,
        message: 'Auth token missing.',
      });
    }
    // Call the validate function above that gets the user data.
    const result = await validate(token);
    return res.status(200).send(result);
  } catch (err) {
    // Return undefined if there is no user. You may also send a different status or handle the error in any way that you wish.
    console.log(err);
    const result = undefined;
    return res.status(200).send(result);
  }
};

_app.js

import React from 'react';
import Head from 'next/head';
import App from 'next/app'
import cookies from 'next-cookies'
import UserProvider from '../lib/userContext'
import { ThemeProvider } from '@material-ui/core/styles';
import CssBaseline from '@material-ui/core/CssBaseline';
import theme from '../theme';

export default function MyApp(props) {
  const { Component, pageProps, user } = props;

  React.useEffect(() => {
    const jssStyles = document.querySelector('#jss-server-side');
    if (jssStyles) {
      jssStyles.parentElement.removeChild(jssStyles);
    }
  }, []);

  return (
    <React.Fragment>
      <Head>
        <title>My app</title>
        <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
      </Head>
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <UserProvider>
          <Component {...{...pageProps, ...user}} />
        </UserProvider>
      </ThemeProvider>
    </React.Fragment>
  );
}
MyApp.getInitialProps = async (userContext) => {
  const { ctx } = userContext;
  //const { res } = ctx;
  const appProps = await App.getInitialProps(userContext);
  const { firebaseToken } = cookies(ctx);
  if (firebaseToken) {
    try {
      const headers = {
        'Context-Type': 'application/json',
        Authorization: JSON.stringify({ token: firebaseToken }),
      };
      const dev = process.env.NODE_ENV === 'development';
      const server = dev ? 'http://localhost:3000' : 'https://firebase-app-taupe.vercel.app/';
      const result = await fetch(`${server}/api/validate`, { headers }).then((res) => res.json());
      return { ...result, ...appProps };
    } catch (e) {
      console.log(e);
    }
  }
  return { ...appProps };
};

从仅用户页面重定向未经身份验证的用户的最佳方法是什么?

0 个答案:

没有答案