authentication

22672 ワード

import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import client from "../../client";

export default {
  Mutation: {
    editProfile: async (
      _,
      { firstName, lastName, username, email, password: newPassword, token }
    ) => {
      const { id } = await jwt.verify(token, process.env.SECRET_KEY);

      let uglyPassword;
      if (newPassword) {
        uglyPassword = await bcrypt.hash(newPassword, 10);
      }
      const updatedUser = await client.user.update({
        where: {
          id,
        },
        data: {
          firstName,
          lastName,
          username,
          email,
          password: uglyPassword,
        },
      });
      if (updatedUser.id) {
        return {
          ok: true,
        };
      } else {
        return {
          ok: false,
          error: "Could not update profile.",
        };
      }
    },
  },
};
しかし、すべてのMutationにtokenを提供するのはあまりにも効率的ではありません.また,verifyを用いてtokenを導入するのも面倒な作業である.
ソリューション
すべてのtokenをMutationからインポートし、ヘッダとして受け取ったtokenにアクセスする必要があります.したがって、HTTP Headersを使用し、HTTP Headersはrequest、responseの一部であり、任意の値を入れることができます.
だからHTTP Headersにtokenを入れる
export default {
  Mutation: {
    editProfile: async (
      _,
      { firstName, lastName, username, email, password: newPassword },
      { token }
    ) => {
      const { id } = await jwt.verify(token, process.env.SECRET_KEY);

      let uglyPassword;
      if (newPassword) {
        uglyPassword = await bcrypt.hash(newPassword, 10);
      }
      const updatedUser = await client.user.update({
        where: {
          id,
        },
        data: {
          firstName,
          lastName,
          username,
          email,
          password: uglyPassword,
        },
      });
      if (updatedUser.id) {
        return {
          ok: true,
        };
      } else {
        return {
          ok: false,
          error: "Could not update profile.",
        };
      }
    },
  },
};
editProfile resolverには4つの論点があり、そのうちの1つはcontextである.contextobjectであり、すべての解析器にアクセス可能な情報を入れることができる.
したがってcontextにトークンを追加すると、そのトークンはすべての解析器からアクセスできます.
しかし、もう一つの問題はcontextが関数になる可能性があり、トークン自体を入れると危険になる可能性があるということです.
サーバ側がcontextからのreqをチェックするとgraphqlのタグを表示できます.
  context: ({ req }) => {
    return {
      token: req.headers.token,
    };
  },
このように使うことができますが、これは最善の方法ではありません.
editProfile.resolvers.jsで
const { id } = await jwt.verify(token, process.env.SECRET_KEY);
上記のコードがアップロードやコメントなどすべての場所で作成されているとすれば、効率は非常に低い.
でもサーバーにUserを送ったらどうですか?
//users.utils.js

export const getUser = async (token) => {
  try {
    if (!token) {
      return null;
    }
    const { id } = await jwt.verify(token, process.env.SECRET_KEY);
    const user = await client.user.findUnique({ where: { id } });
    if (user) {
      return user;
    } else {
      return null;
    }
  } catch {
    return null;
  }
};
//server.js

const server = new ApolloServer({
  schema,
  context: async ({ req }) => {
    return {
      loggedInUser: await getUser(req.headers.token),
    };
  },
});
//editProfile.resolvers.js

export default {
  Mutation: {
    editProfile: async (
      _,
      { firstName, lastName, username, email, password: newPassword },
      { loggedInUser } //token 대신에 loggedInUser 보냄
    ) => {
      let uglyPassword = null;
      if (newPassword) {
        uglyPassword = await bcrypt.hash(newPassword, 10);
      }
      const updatedUser = await client.user.update({
        where: {
          id: loggedInUser.id,
        },
        data: {
          firstName,
          lastName,
          username,
          email,
          ...(uglyPassword && { password: uglyPassword }),
        },
      });
      if (updatedUser.id) {
        return {
          ok: true,
        };
      } else {
        return {
          ok: false,
          error: "Could not update profile.",
        };
      }
    },
  },
};