23
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Next.jsのSSRでJWTをHeaderに埋め込む方法

Last updated at Posted at 2022-02-01

##概要
Next.jsではCSR, SSR, SSG, ISRといった色々なレンダリング方法を使い分けることが可能です。

SSGやISRはビルド時にhtmlが生成されるためパフォーマンスが最も高く、Next.jsの開発元であるVercelも推奨しています。
image.png

ただビルド時だとユーザーのリクエストを受け取れないため、例えばログイン状態でAPIを叩いてレンダリングさせたい場合などはSSGでなくSSRを使ってPre-renderingするのがいいかと思います。
image.png

今回はSSRで実際どのように認証情報(JWT)を乗せてリクエストを送るのか、Apollo Clientとaxiosの2パターンを紹介しようと思います

参考
SSG と SSR で理解する Next.js のページレンダリング

##前提
JWTをサーバー側に渡すにはcookieを使います
今回はnookiesというライブラリを使います
ログイン処理にて session という名前でトークンを保存しておきます

page/login.tsx
const emailLogin = async () => {
    const token = await user?.getIdToken();

    const options = {
      maxAage: 60 * 60,
      secure: true,
      path: '/',
    }

    if (token) setCookie({ res }, 'session', token, options);    
};

##Apollo Client

page/index.tsx
export default function Home({
  postData
}) {
  /* 省略 */
}

export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => {
    const cookies = nookies.get(context);
    const accessToken = cookies.session;
    const apolloClient = initializeApollo(null, context); //apolloClient初期化

    // トークンがない場合ログイン画面にリダイレクト
    if (!accessToken) {
      return {
        redirect: {
          permanent: false,
          destination: '/login',
        },

        props: {} as never,
      };
    }

    const { data: postData } = await apolloClient.query({
      query: GetPostDataDocument,
    });

    return {
      props: {
        postData
      },
    };
}
apolloClient.ts
let apolloClient: ApolloClient<NormalizedCacheObject>;

function createApolloClient(ctx: { req: any }) {
  const httpLink = createHttpLink({
    uri: 'https://api.service.com/',
  });

  const authLink = setContext((req, { headers }) => {
    const accessToken = nookies.get(ctx).session; // JWT取り出し
    return {
      headers: {
        ...headers,
        someoneToken: accessToken || '', // Headerに埋め込み
      },
    };
  });

  return new ApolloClient({
    ssrMode: typeof window === 'undefined',
    link: authLink.concat(httpLink),
    cache: new InMemoryCache(),
    credentials: 'include',
  });
}

export function initializeApollo(initialState = null, ctx) {
  const _apolloClient = apolloClient ?? createApolloClient(ctx); //このctxにcookie情報が含まれる

  if (initialState) {
    _apolloClient.cache.restore(initialState);
  }

  return _apolloClient;
}

###ポイント
getServerSideProps は、SSRのときにサーバーサイドでのみ実行されるAPIとなります。

そちらで受け取る context パラメータには Node HTTPServer の req、 res などが内包しており、クライアント側の cookie 情報を取得できます。

なので context を apollo Client の初期化時に引数で渡して、Header に格納するという流れになります。

##Axios

page/index.tsx

/* クライアント側はApollo Clientと同様 */

export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext) => {
  const cookies = context.req.headers.cookie; /* session=AAAAAAAAAA... */

  // トークンがない場合ログイン画面にリダイレクト
  if (!cookies) {
    return {
      redirect: {
        destination: "/login",
        permanent: false,
      },
    };
  }

  const token = cookies.replace('session=', ''); // JWTのみを取り出す

  const res = await api.get('/posts', {
    headers: {
      "Authorization": `Bearer ${token}`
    }
  })
  const postData = res.data;

  return {
    props: {
      postData,
    }
  };
};
api.ts
/* Axios初期化 */

const api = Axios.create({
  baseURL: 'https://api.service.com/',
  headers: {
    Accept: "application/json",
    "Content-Type": "application/json",
  },
});

###ポイント
ApolloClientと同様の流れでcontext → cookie → session(JWT) を取り出します。

今回はAPI呼び出し時にheadersを定義しているのでわかりやすいかと思います。

##あとがき
自分が詰まった箇所だったのでメモがてらまとめてみました。
参考になりましたら幸いです。

23
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?