reactjs 如何使用next-auth和fetch发送包含令牌的请求

plicqrtu  于 2022-12-26  发布在  React
关注(0)|答案(1)|浏览(128)

我用下面的代码在pages/API/auth中创建了[... nextauth]文件:

import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials";
import { getToken } from "next-auth/jwt";

let userAccount = null;
let token = null;

export const authOptions = {
  session: {
    strategy: 'jwt'
  },
  providers: [
    CredentialsProvider({
      type: 'credentials',
      credentials: {},
      async authorize(credentials, req){
        
        const res = await fetch("http://localhost:3030/seller/login", {
            method: 'POST',
            body: JSON.stringify(credentials),
            headers: { "Content-Type": "application/json" }
        })

        const user = await res.json();

        token = await getToken({ req })

        if(res.ok && user) {
          userAccount = user;
            return user
        }

        return null;
      }
    })
  ],
  pages: {
    signIn: '/auth/login',
  },
  callbacks: {
    async jwt(token, account) {
      console.log("JWT Token User");
      console.log(token.account);
      if (typeof user !== typeof undefined)
      {
          token.accessToken = account.access_token;
      }
      return token;
    },
    async session(session, token, user) {
      console.log("Session token");
      console.log(token);
      if (userAccount !== null)
      {
          session.user = userAccount;
      }
      else if (typeof token !== typeof undefined)
      {
          session.accessToken = token.accessToken
      }
      console.log("session callback returning");
      console.log(session);
      return session;
  },

  },
  
 
}

export default NextAuth(authOptions)

我有一个登录页面,其中采取了我的credintials和数据库检查

import React from 'react'
import { useSession, signIn, signOut } from "next-auth/react";
import Router from 'next/router';

const Login = () => {
  const session = useSession();
  React.useEffect(() => {
    if(session.status === 'authenticated') Router.replace("/dashboard");
  }, [session.status]);
    const [userInfo, setUserInfo] = React.useState({email: '', password: ''})
    const handleSubmit = async (e) => {
      e.preventDefault();
      const res = await signIn("credentials", {
        email: userInfo.email,
        password: userInfo.password,
        redirect: false,
      });
      console.log(res);
    };
  return (
   ...
  )

export default Login

现在,我有了一个用于将新产品发布到数据库的页面,它需要随请求发送一个令牌

import React from 'react'
import { useSession } from 'next-auth/react';
import { getToken } from "next-auth/jwt"
import { getCookie } from 'cookies-next';
import Router from 'next/router';
import { forwardRef } from 'react';
import { useFormik, Formik } from 'formik';
import * as Yup from 'yup';
import { FiDollarSign } from 'react-icons/fi';
import NavbarPage from '../../components/navBar';
import Footer from '../../components/footer';

const newproduct = () => {
  const { data: session, status } = useSession({
    required: true,
  })
  console.log(session)
    
    React.useEffect(() => {
      if(status === 'unauthenticated') Router.replace("/auth/login");
    }, [status]);
    if(status === 'loading'){
      return <div className='h-screen w-screen flex justify-center items-center'><Spinner color="#f59e0b" className="h-12" /></div>
    }

    if(status === 'authenticated'){
      
    return (
        <div className=" bg-gray-700">
    
    <div className='bg-white'>
        <NavbarPage />
    </div>
        <div className='p-4 dashboardHeight' style={{minHeight: 'calc(100vh-206px)'}}>
        <h1 className='p-6 font-bold text-4xl text-slate-50'>Inbox</h1>
        <div className='flex justify-center w-full'>

            <Formik
            initialValues= {{
            title: '',
            brand: '',
            category: '',
            price: '',
            quantity: '',
            mImage: '',
            aImages: '',
            }}
            validationSchema= {validateSchema}
            onSubmit={ async(values) => {
              // window.alert(JSON.stringify(values));
              console.log(JSON.stringify(values))
              const res = await fetch("http://localhost:3030/product", {
                method: 'POST',
                body: JSON.stringify(values),
                headers: { 
                  "Content-Type": "application/json",
                  "authentication": session.user ### <<< here
              }
        })

                const user = await res.json();
            }}
            >
        {props => (
      <form onSubmit={props.handleSubmit} className="w-4/5" method='post' encType='multipart/form-data'>
        <Field
          dot={true}
          error={props.touched?.title && props.errors?.title}
          label="Title (Latin)"
          name="title"
          onChange={props.handleChange}
          type="text"
        />
        <Field
          dot={true}
          error={props.touched?.brand && props.errors?.brand}
          label="Brand (Latin)"
          name="brand"
          onChange={props.handleChange}
          type="text"
        />
        <Field
          dot={true}
          error={props.touched?.category && props.errors?.category}
          label="Category (Latin)"
          name="category"
          onChange={props.handleChange}
          type="select"
          >
            <option>-Select Product Category-</option>
            <option value="Supermarket">Supermarket</option>
            <option value="Fashion">Fashion</option>
            <option value="Health & Beauty">Health & Beauty</option>
            <option value="Baby Products">Baby Products</option>
            <option value="Phones & Tablets">Phones & Tablets</option>
            <option value="Home & Office">Home & Office</option>
            <option value="Electronics">Electronics</option>
            <option value="Computing">Computing</option>
            <option value="Sporting Goods">Sporting Goods</option>
            <option value="Gaming">Gaming</option>
            <option value="Automobile">Automobile</option>
          </Field>
        <Field
          dot={true}
          error={props.touched?.price && props.errors?.price}
          icon={<PriceIcon />}
          label="Price"
          name="price"
          onChange={props.handleChange}
          type="text"
        />
        <Field
          dot={true}
          error={props.touched?.quantity && props.errors?.quantity}
          label="Quantity"
          name="quantity"
          onChange={props.handleChange}
          type="text"
        />
        <Field
          dot={true}
          error={props.touched?.mImage && props.errors?.mImage}
          label="Main Image"
          name="mImage"
          onChange={props.handleChange}
          type="file"
        />
        <Field
          dot={false}
          error={props.touched?.aImages && props.errors?.aImages}
          label="Addtional Images"
          name="aImages"
          onChange={props.handleChange}
          type="file"
          // multiple="multiple"
        />
        <button
          className="mt-8 bg-black active:bg-gray-900 focus:outline-none text-white rounded px-4 py-1"
          type="submit"
        >
          Add Product
        </button>
      </form>)}
            </Formik>
            </div>
        </div>
        <Footer/>
        </div>
    )
    }
}

export default newproduct

// Yup validation schema
const validateSchema = Yup.object().shape({
    title: Yup.string().required('Field is required'),
    brand: Yup.string().required('Field is required'),
    category: Yup.string().required('Field is required'),
    price: Yup.string().required('Field is required'),
    quantity: Yup.string().required('Field is required'),
    mImage: Yup.string().required('Field is required'),
    aImages: Yup.string(),
  });
  
  /*  COMPONENT LOGIC */
  
  const style = {
    dot: `after:content-['*'] after:ml-0.5 after:text-red-500`,
    error: `ring-red-500 ring-1`,
    disabled: `cursor-not-allowed`,
    container: `relative mb-6 mt-3`,
    errorMessage: `text-sm text-red-500 mt-2`,
    checkboxLabel: `block overflow-hidden h-6 rounded-full bg-gray-300`,
    checkboxContainer: `relative w-10 mr-2 align-middle select-none mt-2`,
    iconContainer: `absolute flex border border-transparent left-0 top-0 h-full w-10`,
    icon: `flex items-center justify-center rounded-tl rounded-bl z-10 text-gray-400 text-lg h-full w-full`,
    checkbox: `checked:bg-blue-500 checked:right-0 focus:outline-none right-4 duration-200 ease-in absolute block w-6 h-6 rounded-full bg-white border-4 appearance-none cursor-pointer`,
    default: `text-base relative flex flex-1 w-full mt-1 rounded-md py-2 px-4 bg-white text-gray-700 placeholder-gray-400 text-base focus:outline-none focus:ring-1 focus:border-transparent border`,
  };
  
  const Field = forwardRef(
    (
      { disabled, dot, error, icon, label, name, type = 'text', ...rest },
      ref,
    ) => {
      let component;
  
      // if you won't use select, you can delete this part
      if (type === 'select') {
        component = (
          <select
            aria-required={dot}
            aria-invalid={!!error}
            className={`${style.default} ${disabled ? style.disabled : ''}
               ${error ? style.error : 'border-gray-300'}
            `}
            disabled={disabled}
            id={name}
            name={name}
            ref={ref}
            {...rest}
          />
        );
      }
  
      // if you won't use textarea, you can delete this part
      if (type === 'textarea') {
        component = (
          <textarea
            aria-required={dot}
            aria-invalid={!!error}
            className={`${style.default} ${disabled ? style.disabled : ''}
               ${error ? style.error : 'border-gray-300'}
            `}
            disabled={disabled}
            id={name}
            name={name}
            ref={ref}
            {...rest}
          />
        );
      }
  
      // if you won't use checkbox, you can delete this part and the classes checkbox, checkboxContainer and checkboxLabel
      if (type === 'checkbox') {
        component = (
          <div className={style.checkboxContainer}>
            <input
              aria-required={dot}
              aria-invalid={!!error}
              className={`${style.checkbox} ${disabled ? style.disabled : ''}`}
              disabled={disabled}
              id={name}
              name={name}
              type="checkbox"
              {...rest}
            />
            <span className={style.checkboxLabel} />
          </div>
        );
      }
  
      // if you won't use input, you can delete this part
      if (type !== 'checkbox' && type !== 'select' && type !== 'textarea') {
        component = (
          <div className="relative">
            <div className={style.iconContainer}>
              <div className={style.icon}>{icon}</div>
            </div>
            <input
              aria-required={dot}
              aria-invalid={!!error}
              className={`${style.default} ${icon ? 'pl-12' : ''}
                 ${error ? style.error : 'border-gray-300'}
                 ${disabled ? style.disabled : ''}
              `}
              disabled={disabled}
              id={name}
              name={name}
              type={type}
              ref={ref}
              {...rest}
            />
            {error && <ErrorIcon />}
          </div>
        );
      }
  
      return (
        <div className={`${style.container} ${disabled ? 'opacity-50' : ''}`}>
          <label htmlFor={name} className={`text-white ${dot && style.dot}`}>
            {label}
          </label>
          {component}
          {error && (
            <span role="alert" className={style.errorMessage}>
              {error}
            </span>
          )}
        </div>
      );
    },
  );
  
  Field.displayName = 'Field';
  
  const ErrorIcon = () => (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      width="15"
      height="15"
      fill="currentColor"
      className="absolute right-2 -mt-7 text-red-500"
      viewBox="0 0 1792 1792"
    >
      <path d="M1024 1375v-190q0-14-9.5-23.5t-22.5-9.5h-192q-13 0-22.5 9.5t-9.5 23.5v190q0 14 9.5 23.5t22.5 9.5h192q13 0 22.5-9.5t9.5-23.5zm-2-374l18-459q0-12-10-19-13-11-24-11h-220q-11 0-24 11-10 7-10 21l17 457q0 10 10 16.5t24 6.5h185q14 0 23.5-6.5t10.5-16.5zm-14-934l768 1408q35 63-2 126-17 29-46.5 46t-63.5 17h-1536q-34 0-63.5-17t-46.5-46q-37-63-2-126l768-1408q17-31 47-49t65-18 65 18 47 49z" />
    </svg>
  );
  
  const LockIcon = () => (
    <svg
      height="20"
      width="20"
      stroke="currentColor"
      fill="currentColor"
      strokeWidth="0"
      viewBox="0 0 448 512"
      xmlns="http://www.w3.org/2000/svg"
    >
      <path d="M400 224h-24v-72C376 68.2 307.8 0 224 0S72 68.2 72 152v72H48c-26.5 0-48 21.5-48 48v192c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V272c0-26.5-21.5-48-48-48zm-104 0H152v-72c0-39.7 32.3-72 72-72s72 32.3 72 72v72z" />
    </svg>
  );

  const PriceIcon = () => (
    <FiDollarSign className="text-3xl"/>
  );

  const Spinner = ({ color, className }) => (
    <svg
      fill={color}
      viewBox="0 0 1792 1792"
      className={`${className} flex-no-shrink animate-spin`}
      xmlns="http://www.w3.org/2000/svg"
    >
      <path d="M1760 896q0 176-68.5 336t-184 275.5-275.5 184-336 68.5-336-68.5-275.5-184-184-275.5-68.5-336q0-213 97-398.5t265-305.5 374-151v228q-221 45-366.5 221t-145.5 406q0 130 51 248.5t136.5 204 204 136.5 248.5 51 248.5-51 204-136.5 136.5-204 51-248.5q0-230-145.5-406t-366.5-221v-228q206 31 374 151t265 305.5 97 398.5z" />
    </svg>
  );

现在,有一个错误与session.user是不承认,虽然当我登录它,它给了我对象
注:我已经测试了后端,工作正常,如果我手动输入令牌,应用程序也能正常工作,问题是动态地将令牌放在请求的标题中。
我尝试从会话中获取令牌,但无法访问其中的任何数据。我也尝试从cookie中获取令牌,但它给我未定义的

6ss1mwsb

6ss1mwsb1#

您已经在jwtsession回调中添加了访问令牌,这很好。剩下的就是使用它了。而且标头的名称通常是Authorization

onSubmit={ async(values) => {
          // window.alert(JSON.stringify(values));
          console.log(JSON.stringify(values))
          const res = await fetch("http://localhost:3030/product", {
            method: 'POST',
            body: JSON.stringify(values),
            headers: { 
              "Content-Type": "application/json",
              "Authorization": `Bearer ${session.accessToken}`,  // <<< here
          }
    })

            const user = await res.json();
        }}

相关问题