Nest.jsでGoogle認証(OAuth2.0)を使う【フロントエンド編】

に公開

こちらは前回の記事のフロント側の実装になります。

ポイントとしては、
①Googleログイン後にid_tokenとrefresh_tokenを取得する
②id_tokenが有効期限(1時間)を過ぎていた場合はrefresh_tokenを使用し、id_tokenを再取得する
※ 今回はauth-optionsのみの説明になります

auth-options

import GoogleProvider from 'next-auth/providers/google';
import type { NextAuthOptions, Session } from 'next-auth';

export const authOptions: NextAuthOptions = {
  debug: true,
  session: { strategy: 'jwt' },
  providers: [
    // 1) providers内でGoogleProviderを指定する
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      authorization: { params: { access_type: 'offline', prompt: 'consent' } },
    }),
  ],
  callbacks: {
    async jwt({ token, account }) {
      if (account) {
        /*
           2) 初回ログイン後はこちらの処理が走ります。accountにGoogleの認証サーバーから取得した情報が入ってきます。
           id_tokenとrefresh_tokenをproviderに持たせます
        */
        return {
          access_token: account.access_token,
          expires_at: account.expires_at,
          refresh_token: account.refresh_token,
          user: token,
          id_token: account.id_token,
        };
      } else if (Date.now() < (token.expires_at as number) * 1000) {
        // 3) id_tokneの有効期限(1時間)内の場合
        return {
          ...token,
        };
      } else {
        if (!token.refresh_token) throw new Error('Missing refresh token');
        
        // 4) id_tokenの期限切れの場合
        try {
          const urlSerchParamsArgs: Record<string, string> = {
            client_id: process.env.GOOGLE_CLIENT_ID!,
            client_secret: process.env.GOOGLE_CLIENT_SECRET!,
            grant_type: 'refresh_token',
            refresh_token: token.refresh_token as string,
          };
         
         // 5) id_tokenを再取得しに行きます
          const response = await fetch('https://oauth2.googleapis.com/token', {
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: new URLSearchParams(urlSerchParamsArgs),
            method: 'POST',
          });

          const tokens = await response.json();
       
          if (!response.ok) throw tokens;

          return {
            ...token, 
            access_token: tokens.access_token,
            expires_at: Math.floor(Date.now() / 1000 + tokens.expires_in),
            refresh_token: tokens.refresh_token ?? token.refresh_token,
            id_token: tokens.id_token,
          };
        } catch (error) {
          console.error('Error refreshing access token', error);
          return { ...token, error: 'RefreshAccessTokenError' as const };
        }
      }
    },
    session: ({ session, token }: { session: Session; token: any }) => {
      return {
        ...session,
        user: {
          name: token?.user?.name,
          email: token.user.email,
          image: token.user.picture,
          role: token.role,
          id_token: token.id_token,
        },
      };
    },
  },
};


このようにすることで1時間以内のid_tokenを保持することができます