はじめに
弊社では LLM を活用した機能開発の観測基盤として Langfuse をセルフホストで運用しています。Langfuse は LLM アプリケーションのトレーシングやプロンプト管理等に活用できるオープンソースの LLM エンジニアリングプラットフォームです。
さてこの種のサービスには認証の課題がつきまといます。Langfuse は標準でメールアドレス / パスワードによるログインが可能ですが、弊社の方針として開発組織内で利用、管理しているアプリケーションやサービス群には IAM Identity Center を IdP とする SSO を採用しています。Redash や SendGrid といったサービスで既にこの方式を採用しており、Langfuse でも同様に IAM Identity Center 経由の SSO ログインを実現したいと考えました。
ところが Langfuse は IAM Identity Center を IdP として直接サポートしていません。Langfuse の認証は NextAuth.js ベースであり、対応する認証方式は OAuth / OIDC が中心です*1。では IAM Identity Center の OIDC 機能をそのまま使えるかというと、そう単純ではありません。IAM Identity Center が OIDC を提供するにはアプリケーション側が trusted token issuer に対応している必要があり、Langfuse はこれに該当しないため直接利用できません。
Cognito と Langfuse の SSO 連携については既に優れた先例がありますが、ここに IAM Identity Center を加えた構成についての情報はあまり無いようです。ニッチな話題かもしれませんが、同様の課題を抱えている方もいるのではと考え、本稿にて事例を紹介できればと思います。
構成
前述の事情を踏まえ、間に Amazon Cognito を挟む構成としました。Cognito は SAML によって IdP からの認証情報を受け取り、これを OIDC にてアプリケーション(今回の場合は Langfuse)に提供する役目を担います。IAM Identity Center は SAML による外部アプリケーション(今回の場合は Cognito)のフェデレーションをサポートしているので、これを組み合わせると以下のような構成が実現できます。
IAM Identity Center --- (SAML) ---> Cognito --- (OIDC) ---> Langfuse
認証フローを時系列で描くと以下のようになります。
sequenceDiagram
autonumber
participant User as ユーザー (Browser)
participant LF as Langfuse
participant Cog as Cognito
participant IDC as IAM Identity Center
User->>LF: SSO ログインボタンをクリック
LF->>User: Cognito のログイン URL へリダイレクト
User->>Cog: 認証リクエスト (OIDC)
Cog->>User: IAM Identity Center へリダイレクト
User->>IDC: SAML 認証リクエスト
IDC->>User: ログイン画面の表示 / 認証の実施
IDC->>User: SAML Assertion を発行
User->>Cog: SAML Assertion をポスト (ACS URL)
Cog->>Cog: SAML Assertion の検証 / ユーザー情報の処理
Cog->>User: Langfuse へリダイレクト (認可コード)
User->>LF: 認可コードを送信
LF->>Cog: トークン交換リクエスト
Cog->>LF: ID トークン / アクセストークン (OIDC)
LF->>User: ログイン完了
要するに Cognito が SAML -- OIDC 間の橋渡し役を担う格好です。ユーザー視点では Langfuse のログイン画面で SSO ボタンを押すと IAM Identity Center のログイン画面に遷移し、認証後 Langfuse に戻ってきます。
これを実現する構成が以下のようになります。本稿の趣旨は Langfuse 利用にかかる SSO ログイン化が主軸につき、Langfuse 本体の構成はかなり端折った図である点ご容赦ください*2

なお上図における logging / security 各アカウントは以下拙稿における member / security に対応するものです。主に SSO ログイン時の監査に使われる周辺要素であり、本稿では詳細を割愛します。
設定の手順
設定は大きく4つのステップに分かれます。
- IAM Identity Center に SAML アプリケーションを登録する(admin アカウント)
- Cognito で SAML IdP と OIDC クライアントを構成する(langfuse アカウント)
- Langfuse に OIDC 関連の環境変数を設定する(langfuse アカウント)
- SSO ログインへの一本化(langfuse アカウント)
以下、各ステップの内容を解説します。
Step 1: IAM Identity Center への SAML アプリケーション登録
admin アカウントの IAM Identity Center に、Langfuse 用のカスタム SAML アプリケーション(customer-managed application)を Terraform で登録します*3。aws_ssoadmin_application リソースで application_provider_arn に custom-saml を指定し、IAM Identity Center のポータル上での可視性を有効にします。
あわせて aws_ssoadmin_application_assignment で SSO ログインを許可する対象を紐付けます。グループ単位での割り当ても可能ですが、弊社では職責や担当職務に応じて柔軟にアクセス範囲を制御したかったため、ユーザー単位での割り当てとしました*4。
続いてマネジメントコンソールから Application metadata を設定します。これは Cognito が SAML Assertion を正しく受け取るために必要な設定です。
- Application ACS URL:
https://<Cognito ドメイン>.auth.<リージョン>.amazoncognito.com/saml2/idpresponse - Application SAML audience:
urn:amazon:cognito:sp:<Cognito User Pool ID>
ACS URL は Cognito User Pool のドメイン名から、SAML audience は Cognito User Pool の ID からそれぞれ導出されます。Step 2 で Cognito User Pool を作成した後に設定する、という順序になります*5。
同じくマネジメントコンソールから Attribute mappings を設定します。SAML Assertion に含めるユーザー属性と Cognito 側の属性との対応を定義するものです。
| User attribute in the application | Maps to this string value or user attribute in IAM Identity Center | Format |
|---|---|---|
| Subject(変更不可) | ${user:email} |
persistent |
${user:email} |
basic | |
| email_verified | true |
unspecified |
| name | ${user.name} |
unspecified |
email_verified を固定値 true としているのは弊社の運用上 IAM Identity Center で管理されているユーザーのメールアドレスは検証済みであると見なして差し支えないためです。これは以下の拙稿に詳細を譲りますが、弊社の IAM Identity Center は IdP として Entra ID を参照しており、Entra ID 上のユーザ情報を信頼できるケースに該当する為です。
最後に、マネジメントコンソールから SAML メタデータファイルをダウンロードします。
- IAM Identity Center コンソールへ移動
Application assignments→Applicationsを選択Customer managedタブへ移動- 対象の SAML アプリケーションを選択
IAM Identity Center metadata内のIAM Identity Center SAML metadata fileからダウンロード
このメタデータ XML ファイルは次のステップで Cognito に渡す必要があります。
Step 2: Cognito の構成
langfuse アカウント側では Cognito User Pool を作成し、IAM Identity Center を SAML IdP として登録した上で、Langfuse 向けの OIDC クライアントを構成します。
まず Step 1 でダウンロードした SAML メタデータ XML を S3 バケットに手動でアップロードし、Terraform からは data "aws_s3_object" で参照します*6。S3 バケット自体も Terraform で作成し、サーバーサイド暗号化とパブリックアクセスブロックを設定しています。
続いて、この SAML メタデータを使って aws_iam_saml_provider と aws_cognito_identity_provider の2つを設定します。前者は IAM レベルでの SAML プロバイダ登録、後者は Cognito User Pool に対する SAML IdP の登録です。aws_cognito_identity_provider では attribute_mapping で Step 1 の Attribute mappings との対応を指定します。
なお provider_details には ignore_changes を設定しています。Cognito は MetadataFile を解釈して内部的にいくつかの属性を展開するため、内容に変更がなくても terraform plan で差分が出続けます。これを抑制するための措置です。
次に aws_cognito_user_pool_client を設定します。これは Langfuse から見た OIDC クライアントに相当するリソースです。OAuth フローには Authorization Code Grant(code)を、スコープには openid / email / profile を指定します。callback_urls には Langfuse の OIDC コールバック URL(https://<Langfuse のドメイン>/api/auth/callback/custom)を設定します。トークンの有効期間やクライアントシークレットの生成など、必要な設定を適宜入れます。
最後に、Cognito が払い出した Client ID / Client Secret / Issuer URL を aws_ssm_parameter(SecureString)で SSM パラメータストアに格納し、Langfuse の ECS タスク定義から参照できるようにします。
Step 3: Langfuse への環境変数設定
Langfuse は Custom OAuth Provider としての設定を環境変数で受け取ります。ECS タスク定義に以下の環境変数を追加しました。
平文で渡すものとして AUTH_CUSTOM_NAME、AUTH_CUSTOM_CHECKS、AUTH_CUSTOM_ALLOW_ACCOUNT_LINKING の3つがあります。SSM パラメータストアから取得する秘密情報として AUTH_CUSTOM_CLIENT_ID、AUTH_CUSTOM_CLIENT_SECRET、AUTH_CUSTOM_ISSUER の3つがあり、これらは Step 2 で格納した値を参照します。
AUTH_CUSTOM_NAMEは Langfuse のログイン画面に表示される SSO ボタンのラベルです。わかりやすい名称を選ぶと良いでしょうAUTH_CUSTOM_CHECKSにはpkceを指定し PKCE (Proof Key for Code Exchange) を有効にしていますAUTH_CUSTOM_ALLOW_ACCOUNT_LINKINGをtrueにすると、同一メールアドレスの既存アカウントと SSO アカウントが紐付けられます- SSO 導入前のアカウントとの互換性のために有効にしています
- これが無いと後述のトラブルシュート時に Cognito 側で対応する手法が取れず Langfuse 側 DB を直接触ってアカウント管理をする手間が発生します
Step 4: SSO ログインへの一本化
SSO ログインが安定稼動していることを確認した後、環境変数 AUTH_DISABLE_USERNAME_PASSWORD を true に設定してメールアドレス / パスワードによるログインを無効化しました。これにより Langfuse のログイン画面は SSO ボタンのみが表示される状態になります。アカウント管理を IAM Identity Center に一元化でき、Langfuse 側での個別のアカウント発行 / 削除が不要となります。
サンプルコード
Step 1〜2 で使用した Terraform コードの抜粋を以下に示します。それぞれ collapsed になっていますので、クリックして中身を参照ください。
admin アカウント: IAM Identity Center への SAML アプリケーション登録
# Identity Store からユーザー情報を参照 data "aws_identitystore_user" "main" { for_each = toset(local.langfuse_users) identity_store_id = tolist(data.aws_ssoadmin_instances.main.identity_store_ids)[0] alternate_identifier { unique_attribute { attribute_path = "UserName" attribute_value = each.key } } } # カスタム SAML アプリケーションの登録 resource "aws_ssoadmin_application" "langfuse" { name = "Langfuse" application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom-saml" instance_arn = tolist(data.aws_ssoadmin_instances.main.arns)[0] portal_options { visibility = "ENABLED" sign_in_options { origin = "IDENTITY_CENTER" } } } # SSO ログインを許可するユーザーの割り当て resource "aws_ssoadmin_application_assignment" "langfuse" { for_each = toset(local.langfuse_users) # 職責に応じてフィルタしたユーザーリスト application_arn = aws_ssoadmin_application.langfuse.arn principal_id = data.aws_identitystore_user.main[each.key].id principal_type = "USER" }
langfuse アカウント: Cognito の構成
# ----- SAML メタデータの管理 ----- # メタデータ XML を格納する S3 バケット(暗号化・パブリックアクセスブロックは別途設定) resource "aws_s3_bucket" "saml_metadata" { bucket = "mntsq-${var.env}-saml-metadata" } # IAM Identity Center からダウンロードしたメタデータ XML を参照 data "aws_s3_object" "saml_metadata" { bucket = aws_s3_bucket.saml_metadata.id key = "langfuse.xml" } # ----- SAML プロバイダ・Cognito User Pool ----- resource "aws_iam_saml_provider" "langfuse" { name = "langfuse" saml_metadata_document = data.aws_s3_object.saml_metadata.body } resource "aws_cognito_user_pool" "langfuse" { name = "langfuse" auto_verified_attributes = ["email"] } resource "aws_cognito_user_pool_domain" "langfuse" { user_pool_id = aws_cognito_user_pool.langfuse.id domain = "mntsq-${var.env}-langfuse" } # IAM Identity Center を SAML IdP として登録 resource "aws_cognito_identity_provider" "langfuse" { user_pool_id = aws_cognito_user_pool.langfuse.id provider_name = "langfuse" provider_type = "SAML" provider_details = { "MetadataFile" = data.aws_s3_object.saml_metadata.body } attribute_mapping = { email = "email" email_verified = "email_verified" name = "name" } lifecycle { # Cognito が MetadataFile を解釈して provider_details を展開するため、 # 毎回差分が出るのを抑制する ignore_changes = [provider_details] } } # ----- OIDC クライアント (User Pool Client) ----- resource "aws_cognito_user_pool_client" "langfuse" { name = "langfuse" user_pool_id = aws_cognito_user_pool.langfuse.id allowed_oauth_flows_user_pool_client = true allowed_oauth_scopes = ["openid", "email", "profile"] allowed_oauth_flows = ["code"] supported_identity_providers = [aws_cognito_identity_provider.langfuse.provider_name] access_token_validity = 8 id_token_validity = 8 refresh_token_validity = 1 token_validity_units { access_token = "hours" id_token = "hours" refresh_token = "days" } callback_urls = ["https://<Langfuse のドメイン>/api/auth/callback/custom"] default_redirect_uri = "https://<Langfuse のドメイン>/api/auth/callback/custom" generate_secret = true } # ----- 認証情報の SSM パラメータストア格納 ----- resource "aws_ssm_parameter" "auth_id" { name = "/${var.env}/langfuse/AUTH_CUSTOM_CLIENT_ID" type = "SecureString" value = aws_cognito_user_pool_client.langfuse.id } resource "aws_ssm_parameter" "auth_secret" { name = "/${var.env}/langfuse/AUTH_CUSTOM_CLIENT_SECRET" type = "SecureString" value = aws_cognito_user_pool_client.langfuse.client_secret } resource "aws_ssm_parameter" "auth_issuer" { name = "/${var.env}/langfuse/AUTH_CUSTOM_ISSUER" type = "SecureString" value = "https://cognito-idp.${data.aws_region.current.name}.amazonaws.com/${aws_cognito_user_pool.langfuse.id}" }
トラブルシューティング
運用を開始して以降、SSO ログインが失敗するケースに遭遇しました。とくに初回ログイン時に顕著で、Cognito と Langfuse との間でユーザー情報のフェデレーションがうまくいかない場合に発生します。
リトライで解消するケースもありますが、それでも解決しない場合は Cognito 側のユーザーを一旦削除し、再度 Langfuse へ SSO ログインしてもらう ことで通るようになります。Cognito User Pool 内のユーザーは IAM Identity Center から federate されたものなので、削除しても次回の SSO ログイン時に再作成されます。
手順としては以下の通りです。
- Cognito User Pool のマネジメントコンソールへ移動
User management→Usersでログインに失敗しているユーザーを特定- 対象ユーザーを選択し、まず
Disable user accessを実施 - その後
Delete userを実行
ユーザー削除後に改めて SSO ログインを試みてもらえば、Cognito 側にユーザーが再作成されてログインが成功するはずです。
おわりに
IAM Identity Center を IdP として Langfuse に SSO ログインを実現する構成について解説しました。ポイントは Cognito を SAML と OIDC との翻訳役として活用する 点です。
設定にあたっては Terraform とマネジメントコンソールの手作業が混在する点がやや煩雑です。とくに IAM Identity Center 側の Application metadata や Attribute mappings、SAML メタデータの取得はコンソール操作が必要であり、手順として残しておかないと再現が難しくなります。
Cognito + Langfuse の SSO 構成については先例がありますが、IAM Identity Center を加えた構成についてはあまり情報が見当たりませんでした。このパターンは Langfuse に限らず、OIDC のみをサポートするアプリケーションに IAM Identity Center 経由の SSO を提供したい場合に広く応用できるものと考えています。同様の課題を抱えている方の参考になれば幸いです。
文責:MNTSQ 株式会社 SRE 秋本
注記:この記事は文責者の過去記事と弊社内のドキュメントをもとに Claude Opus 4.6 が作成した内容を9割程度そのまま使用しています
*1:https://langfuse.com/self-hosting/security/authentication-and-sso を参照。Langfuse は Google / GitHub / Azure AD (Entra ID) / Okta / Auth0 / Custom OAuth Provider 等をサポートしています
*2:興味のある方向け:ECS で ClickHouse 含めて Langfuse が必要とする諸要素を動作させ、DB は Aurora PostgreSQL、キャッシュ関連は ElastiCache にて Valkey クラスタを組んで動作させています
*3:実際には全ての設定を Terraform でカバーできるわけではないので、適宜 AWS マネジメントコンソールからの設定作業も必要になります
*4:この種の柔軟さをグループ単位で表現できなかったという内部事情もあります
*5:厳密には Cognito User Pool の作成 → IAM Identity Center の Application metadata 設定 → Cognito 側の残りの設定、という順序を踏む必要があります。Terraform で一括管理する場合は初回適用時にこの依存関係を意識する必要があります。要するに IAM Identity Center と Cognito とを行ったり来たりする必要が2026年2月時点で有ります
*6:SAML メタデータはコード管理の対象としていません。IAM Identity Center 側で生成されるものであり、かつ XML の内容が大きいため S3 経由での参照としました。メタデータの取得手順は Step 1 に記載の通りです