2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.jsにLINEのapiを用いてログインを組み込む

Posted at

コンソール画面での設定

CreateでProviderを作成します。

スクリーンショット 2025-01-17 191950.png

ログインチャンネルを作成します。
スクリーンショット 2025-01-17 192236.png

以下のように必要事項を入力します。
FireShot Capture 016 - LINE Developers - developers.line.biz.png

以上でチャンネルの作成が完了しました。

ログインをする

こちらにある例を参考に進めていきます。

client_idはChannel IDを使用します。
スクリーンショット 2025-01-17 194207.png

redirect_uriはコンソールで登録したコールバックURLにします。
スクリーンショット 2025-01-17 194343.png

stateはひとまずサンプルと同じに「12345abcde」とします。
scopeは「profile%20openid%20email」にします。
そして、以下のようにLinkを作ります。
ここではNext.jsで使用します。

<Link href='https://access.line.me/oauth2/v2.1/authorize?response_type=code&client_id=${CHANNNEL_ID}&redirect_uri=${CALLBACL_URL}&state=12345abcde&scope=profile%20openid%20email'>
Lineログイン
</Link>

以下の画面にリダイレクトされるのでログインをします。
スクリーンショット 2025-01-17 200928.png

その後、redirect_uriに設定したURLにリダイレクトされます。その時URLにcodeとstateが付されます。

${redirect_uri}?code=${code}&state=12345abcde

上記URLのcode部分を取り出してlineLoginServerComponent内でレスポンスに含まれるrefresh tokenをクッキーとして格納します。
次に、lineLoginServerComponentのレスポンスに含まれるaccess_tokenを利用してgetLinProfileServerComponentを実行してユーザーのプロフィールを取得します。

リダイレクト先
'use client';
import { lineLoginServerComponent } from '@/app/actions/set-cookie';
export default function App() {
  useEffect(() => {
    lineLogin();
  }, []);
  const lineLogin = async () => {
    const params = new URLSearchParams();
    if (searchParams.get('code') != null) {
      params.append('code', searchParams.get('code')!);
      const data = await lineLoginServerComponent(searchParams.get('code')!);
      console.log(data);
      const profile = await getLinProfileServerComponent(data.access_token);
      console.log(profile);
      setUser({ ...user, uid: profile.userId, displayName: profile.displayName });
    }
  };
}

client_secretはchannelのChannel secretを使用します。
スクリーンショット 2025-01-18 163959.png

app/actions/set-cookie.tsx
export async function lineLoginServerComponent(searchParams: string) {
  try {
    const params = new URLSearchParams();
    params.append('grant_type', 'authorization_code');
    params.append('code', searchParams);
    params.append('redirect_uri', `${process.env.NEXT_PUBLIC_LINE_REDIRECT_URI}`);
    params.append('client_id', `${process.env.NEXT_PUBLIC_LINE_CLIENT_ID}`);
    params.append('client_secret', `${process.env.NEXT_PUBLIC_LINE_CLIENT_SECRET}`);

    const requestOptions = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: params.toString(),
    };

    const response = await fetch('https://api.line.me/oauth2/v2.1/token', requestOptions);

    const data = await response.json();
    if (response.ok) {
      console.log(data);

      await setCookieAction(data.refresh_token, data.expires_in);
      return data;

    } else {
      console.log('Error:', data);
    }
  } catch (error) {
    console.error('Error during LINE login:', error);
  }
}

export async function getLinProfileServerComponent(access_token: string) {
  try {
    const requestOptions = {
      method: 'GET',
      headers: {
        Authorization: `Bearer ${access_token}`,
      },
    };

    const response = await fetch('https://api.line.me/v2/profile', requestOptions);

    const data = await response.json();
    if (response.ok) {
      console.log(data);
      return data;
    } else {
      console.log('Error:', data);
    }
  } catch (error) {
    console.error('Error fetching profile:', error);
  }
}
export async function setCookieAction(token: string, expire: number): Promise<void> {
  await cookies().set({
    name: 'line_refresh_token',
    value: token,
    httpOnly: true,
    domain: `${process.env.NEXT_PUBLIC_COOKIE_DOMAIN}`,
    maxAge: 7776000,
    sameSite: 'lax',
    secure: true,
    path: '/',
  });
}

以上でログイン、プロフィールの取得が完了です。

ログインを維持する

次に、リロードなどした際にもログインを維持する処理をcontextに組み込みます。

@/context/AuthContext.tsx
'use client';
import {
  getLinProfileServerComponent,
  updateTokenServerComponent,
} from '@/app/actions/set-cookie';
  useEffect(() => {
    getProfile();
  }, []);
  const getProfile = async () => {
    const data = await updateTokenServerComponent();
    console.log(data);
    if (data) {
      const profile = await getLinProfileServerComponent(data.access_token);
      console.log(profile);
      setUser({ ...user, uid: profile.userId, displayName: profile.displayName });
    }
  };
app/actions/set-cookie.tsx
export async function updateTokenServerComponent() {
  try {
    const hasCookie = await haveCookieAction(); // Assuming haveCookieAction is defined elsewhere
    if (!hasCookie) {
      throw new Error('No cookie found');
    }

    const params = new URLSearchParams();
    params.append('grant_type', 'refresh_token');
    params.append('refresh_token', hasCookie.value);
    params.append('client_id', `${process.env.NEXT_PUBLIC_LINE_CLIENT_ID}`);
    params.append('client_secret', `${process.env.NEXT_PUBLIC_LINE_CLIENT_SECRET}`);

    const requestOptions = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
      },
      body: params.toString(),
    };

    const response = await fetch('https://api.line.me/oauth2/v2.1/token', requestOptions);

    const data = await response.json();
    if (response.ok) {
      console.log(data);
      await getLinProfileServerComponent(data.access_token);
      await setCookieAction(data.refresh_token, data.expires_in);
      return data;
    } else {
      console.log('Error:', data);
    }
  } catch (error) {
    console.error('Error updating token:', error);
  }
}

参考

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?