Lang x Lang

CORS と Authentication

CORS(Cross-Origin Resource Sharing) と API の認証を行う。CORS header を設定しないと、POSTMAN などでテストしてると気づかないが、ないと API がうまく機能しないし、認証がないと誰でも API が叩けちゃうので危ない。

src
├── lib
│   └── BearerAuth.ts
└── middleware.ts

共通処理は、middleware で行う。API の認証は、前述の通りライブラリ化したいので、別ファイルにする。

CORS

src/middleware.ts
import { NextResponse } from 'next/server';

export async function middleware(request: Request) {
  newHeaders.set('Access-Control-Allow-Origin', '*');
  newHeaders.set(
    'Access-Control-Allow-Headers',
    'GET, POST, OPTIONS, DELETE, PUT'
  );
  newHeaders.set('X-FRAME-OPTIONS', 'DENY');
  newHeaders.set('Cache-Control', 'no-cache, no-store, max-age=0');

  return NextResponse.next({
    headers: newHeaders,
    request: {
      headers: newHeaders,
    },
  });
}

export const config = {
  matcher: '/api/:path*',
};

今回は、スマートフォンアプリなどのクライアントからのリクエストを想定して、Access-Control-Allow-Origin: * にしているが、アクセス元を限定する場合は、middleware: CORS にあるように、Origin を指定する。

middleware は全てに適用されてしまうので、matcher を使って、適用範囲をいったん /api/ 配下に限定する。CORS は、ブラウザが API をリクエストする際に、preflight リクエストを OPTION メソッドで送って来るので、204 レスポンスを返す必要があるのだが、この処理は、Next.js がよしなにやってくれるようになったので、以前は自分で書いていたが、いまは書く必要はない。X-FRAME-OPTIONS はクリックジャッキング対策で、CORS とは関係ないが、セキュリティ関連、その他の共通で必要な headers は、全て middleware で設定してしまおう。

Authentication

今回は、Bearer Auth にした。API 利用の流れとしては、

  • access_token を取得
  • request headerAuthorization: Bearer {access_token}を入れて API を叩く

になる。access_token 取得の API は /api/auth/access_token にする。このエンドポイントと、後で作る health check の /api/health には認証をかけないように処理する。

src/middleware.ts
import { NextResponse } from 'next/server';
import { getBearerAuthStatusCode } from './lib/BearerAuth';
import { ERROR_MAP } from './lib/ErrorMessages';

export async function middleware(request: Request) {
  ...
  const regexAuth = new RegExp('/api/(auth|health)/*');
  if (!regexAuth.test(request.url)) {
    const bearerAuthStatusCode = await getBearerAuthStatusCode(request);
    if (bearerAuthStatusCode !== 200) {
      return NextResponse.json(
        { error: ERROR_MAP[bearerAuthStatusCode] },
        { status: bearerAuthStatusCode }
      );
    }
  }
  ...
}

export const config = {
  matcher: '/api/:path*',
}

'/api/(auth|health)/*' にマッチしない場合だけ、認証処理を行う。

以下は、token の生成・検証の例になる。必要とされるセキュリティレベルに合わせてロジックを組むと良い。

/api/auth/access_token

/api/auth/access_token
  • POST method で受ける
  • secret を突き合わせる
  • DB から user 情報を取得する
  • 有効期限や必要なものを含めて token を生成する

ということをやっている。refresh token なども必要に応じて追加すると良い。

Next.js 14 では、セキュリティの都合上、jsonwebtoken がうまく動かないので、jose を利用している。JWT を使うのが良いかどうかは議論の余地があるが、API の用途によって token の生成方法は変えれば良い。参照のコードでは、先走って Zod を使っているが、ナンノコッチャ?という場合は、一旦先へ進み、後で戻ってくれば良い。

app/lib/BearerAuth.ts

src/lib/BearerAuth.ts

これは、Authorization header から token を取り出し、検証して、その結果(ステータスコード)を返却するだけだ。

当社サイトでは、Cookie を使用しています。各規約をご確認の上ご利用ください:
Cookie Policy, Privacy Policy および Terms of Use