🐔

Auth.js に Cognito + 外部IdP を繋ぎこむ際に TypeError: immutable になる

2024/11/03に公開

はじめに

はじめまして。しんりうです。

今回は、Next.js App Router + Auth.js (NextAuth.js v5) に AWS Cognito + 外部 IdP (Google) を繋ぎ込む際に遭遇したエラーとその解決方法を共有します。
特に、OAuth ではなく OpenID Connect(OIDC)を使用した認証を実装しようとした際に発生した問題について説明します。

NextAuth.js v4 までは起きていなかったのですが、v5 へのアップグレードに伴いこのエラーが生じました。
Auth.js の公式 docs では v5 では 既存の OAuth プロバイダーが壊れる可能性がある と述べられており、この影響によるものだと考えられます。

動作環境

Node.js バージョン: 21.2.0

"next": "14.2.16",
"next-auth": "5.0.0-beta.25",

前提条件

AWS Cognito と Google Cloud での OAuth クライアントの設定

AWS Cognito 側ではユーザープールを作成し、コールバック URL などアプリケーションクライアントを適切に設定していました。
また、Google Cloud 側では OAuth クライアントの設定で承認済み URL などを適切に設定していました。

※ ここで誤りがある場合、redirect url miss match などのエラーが起きます。

auth.config.ts の設定

auth.config.ts の元々の設定は以下のようになっていました。

auth.config.ts
import type { NextAuthConfig } from "next-auth";
import Cognito from "next-auth/providers/cognito";

const authConfig: NextAuthConfig = {
  providers: [
    Cognito,
  ],
};

export default authConfig;

エラーと解決方法

エラーその 1

TypeError: immutable が起きていました。
また、URL 及び サーバーサイドのログを確認すると、invalid scope であることがわかりました。

そこで Cognito プロバイダの設定として authorization.params.scopeopenid を明示的に宣言しました。

auth.config.ts
import type { NextAuthConfig } from "next-auth";
import Cognito from "next-auth/providers/cognito";

const authConfig: NextAuthConfig = {
  providers: [
    Cognito({
+     authorization: {
+       params: {
+         scope: "openid", // cognito に OIDC フローを要求 (これがないと OAuth フローのみを実行し、IDトークンを返さなくなる)
+       },
+     },
    }),
  ],
};

export default authConfig;

エラーその 2

invalid scope は解決できましたが、次のエラーが起きました。
nonce チェックに失敗しているとのことです。

[auth][error] CallbackRouteError: Read more at https://errors.authjs.dev#callbackrouteerror
[auth][cause]: OperationProcessingError: unexpected ID Token "nonce" claim value
[auth][error] InvalidCheck: nonce value could not be parsed. Read more at https://errors.authjs.dev#invalidcheck

そこで nonce チェックを行うよう、明示的に宣言しました。

auth.config.ts
import type { NextAuthConfig } from "next-auth";
import Cognito from "next-auth/providers/cognito";

const authConfig: NextAuthConfig = {
  providers: [
    Cognito({
      authorization: {
        params: {
          scope: "openid", // cognito に OIDC フローを要求 (これがないと OAuth フローのみを実行し、IDトークンを返さなくなる)
        },
      },
+     checks: ["nonce"], // cognito から返されるIDトークン検証時に nonce チェックを行うように設定 (OIDCフローでは nonce チェックが必須)
    }),
  ],
};

export default authConfig;

解決方法まとめ

以上2点の対応により、無事に動作するようになりました。
改めて整理しておきます。

auth.config.ts
import type { NextAuthConfig } from "next-auth";
import Cognito from "next-auth/providers/cognito";

const authConfig: NextAuthConfig = {
  providers: [
    Cognito({
+     authorization: {
+       params: {
+         scope: "openid", // cognito に OIDC フローを要求 (これがないと OAuth フローのみを実行し、IDトークンを返さなくなる)
+       },
+     },
+     checks: ["nonce"], // cognito から返されるIDトークン検証時に nonce チェックを行うように設定 (OIDCフローでは nonce チェックが必須)
    }),
  ],
};

export default authConfig;

考察

NextAuth.js は、v5 へのアップデートに伴う破壊的変更の一つに、OAuth/OIDC の仕様準拠がより厳密になったというものがあります。

そのため、今回の対応のように OIDC フローを明示的に要求し、OIDC に準拠するために必要な nonce 検証を明示的に有効化する必要があったと考えられます。

おわりに

今回は、Auth.js (NextAuth.js v5) 、AWS Cognito + 外部 IdP (Google) を繋ぎ込もうとした時に遭遇した問題の解決方法を共有しました。

もしどなたかのお役に立てば幸いです。
ここまで読んでいただきありがとうございました。

Discussion