EntraIDを使ったOIDC認証の実装

はじめに

こんにちは。アドウェイズ 技術本部 インフラディビジョンにてインフラエンジニアとして働いている kawatatsu です。

普段は社内インフラの整備や運用に携わっています。最近では、弊社の認証基盤を長年利用してきたLDAP認証からMicrosoft Entra ID(旧Azure AD)への移行を進めており、自社サービスとの連携にはOpenID Connect(OIDC)を推奨しています。

本記事では、このOIDC認証について、その基本的な概念からEntra IDを使った具体的な実装例までを、インフラエンジニアの視点から詳しく解説していきます。認証・認可の仕組みを深く理解したい方、Entra IDとの連携を検討されている方の参考になれば幸いです。

OIDCとは?

認証や認可といったセキュリティの仕組みについて調べていると、必ずと言っていいほど「OAuth 2.0」という言葉を耳にするかと思います。

OAuth 2.0 (RFC 6749 で定義) は、厳密には「認可 (Authorization)」の実装フレームワークです。
その原文を見ても分かる通り、OAuth 2.0の主な目的は、あるリソースへのアクセスを許可することであり、 ユーザーの身元を保証する「認証」の仕組みは含まれていません。
したがってOAuth 2.0のみで認証を実装しようとすると、別途ユーザー認証の手段を考える必要があります。

実は私自身、最初はOAuth 2.0で認証と認可の両方が行えると思っていました。
Googleログインのような昨今のWebアプリの挙動を見ていると、アカウントがなくてもGoogleアカウントを使ってログイン(認証)が行えるように見え、Googleの管理画面ではOAuthでGitHubなどの連携サービスが表示されるため、これらが混同されやすかったのです。
しかし実際にはGoogleが認証を行い、認証されたユーザーが、外部サービスに対してどの機能の利用やユーザー情報の閲覧を許可(これが「認可 (Authorization)」です)するかの実装がOAuth 2.0の役割なのだと理解を深めました。

つまり、 OAuth 2.0単体では認証を実装することはできず、認証の担保は別途実装する必要があります。

ここで登場するのが OpenID Connect (OIDC) です。OIDCは、そのOAuth 2.0に 認証の機能を拡張したプロトコル であり、OAuth 2.0をベースに構築されています。
そのため集合論的に見ると「OAuth2 ∈ OIDC」という関係が成り立ちます。これはOIDCがOAuth2の認可フレームワークを完全に包含し、その上に「誰であるか」を保証する認証のレイヤーを追加しているためです。

OIDCはOAuth 2.0の認可フレームワークを基盤とし、その上にユーザーの身元を確認するために必要な機能(IDトークンやUserInfoエンドポイントなど)を追加した仕組みのため、OIDCを深く理解するためには、認証と認可、そしてOAuth 2.0の基本的な理解が不可欠となります。

OIDCの認証の流れ

OIDCは複数の当事者(ユーザー、Webアプリ、認証サーバーなど)が連携して動作する、複雑なプロトコルです。文字だけでは分かりづらい部分もあるため、まずは視覚的にフローを見ていきましょう。

OIDCにはいくつか認証フローがありますが、ここでは最も一般的で推奨される 認可コードフロー (Authorization Code Flow) について説明します。

この図は、OIDCと基盤となるOAuth 2.0の認可プロセスを含んだ全体像を示しています。

上記の図のように、OIDCは複数のURLへのリダイレクトやサーバー間でのPOST通信を伴い、複数のステップで構成されています。
Webアプリは、これらの認証に必要なエンドポイント(認証エンドポイント、トークンエンドポイントなど)のURLや、トークンの検証に必要な公開鍵などの情報を知っている必要があります。

これらの情報をWebアプリに静的に設定することは可能ですが、それだと管理が大変になり、IdP側の設定変更に追従するのが困難になります。
そこで、OIDC認証に必要な情報をIdP(OIDCではOPと呼びます)側が公開し、Webアプリが自動で取得できる便利な仕組みがあります。それが、OpenID Connect Discovery 1.0 です。

OpenID Connect Discovery 1.0 とは?

OIDC認証において、WebアプリがIdP(OP)と連携するために必要な様々な公開情報(認証エンドポイントURL、トークンエンドポイントURL、公開鍵情報など)は、静的に設定することもできます。
しかし、そうするとIdP側の設定変更に追従するのが大変になり、運用負荷が高まります。

そこで便利なのが OpenID Connect Discovery 1.0 です。

これは、OIDC認証に必要な公開情報をOPが特定のURLに設置し、WebアプリはOPの Issuer (発行者) URLさえ分かれば、それらの認証情報を自動で取得できる仕組みです。
これにより、Webアプリは動的にIdPの構成情報を取得し、手間なく連携できるようになります。

具体的には、OPの Issuer URLの末尾に /.well-known/openid-configuration を付与したURLにアクセスすることで、OIDCのメタデータ(JSON形式)を入手できます。
curl コマンドやブラウザでこのURLにアクセスしてみると、OIDCで認証する際にどこに接続すればよいか(例: authorization_endpointtoken_endpoint)、どのような認証フローがサポートされているか、IDトークンの署名検証に必要な公開鍵情報などが表示されるのを確認できます。

例えば、Microsoft Entra ID(旧Azure AD)の場合だと以下のようになります。
{tenant} の部分には、ご自身のEntra IDテナントのテナントIDまたはプライマリドメイン名が入ります。
もし個人のMicrosoftアカウントの場合のメタデータを確認したい場合は、{tenant} の部分を consumers とすると参照できます。

Entra IDのIssuer URL:

https://login.microsoftonline.com/{tenant}/v2.0/

Entra IDのOIDCメタデータURL:

https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration

このようにDiscovery機能を使うことで、authorization_endpointtoken_endpointuserinfo_endpoint といった情報を手動でWebアプリ側に登録する手間を省き、より堅牢で柔軟なOIDC連携が可能になります。

EntraIDをIdPとしたOIDCの実装の流れ

EntraIDをIdP(OP)とし、codeベースフローでの流れを先の図をベースに記載すると以下になります。
今回はOIDCでの認証の部分にフォーカスするため、ResourceServer(EntraIDのリソース、AccessToken)については割愛します。
認可の部分は、認証のフロー完了後に処理されます。
先の図では認証要求やエンドポイントへリダイレクトという感じで実際の値が分からないため、ここではEntraIDの場合の実際のURLやパラメータを踏まえて説明していきます。

1.アクセス

ここはシンプルに ユーザー が curl 、ブラウザを使いWebアプリに接続を行います。

例: https://webapp/

2.認証要求

サービスへのログインページへ遷移します。

例: https://webapp/login

3.認証エンドポイントへのリダイレクト

Webアプリはブラウザに対して、認証エンドポイントへのリダイレクト情報を渡します。
このリダイレクト先のURLは、通常、OIDC Discoveryのメタデータ内の authorization_endpoint から取得されます。
この際に付与されるクエリパラメータは、認証リクエストの詳細をIdPに伝えるために非常に重要です。

リダイレクトの例:
クライアント(Webアプリ)のログインページ https://webapp/login から、以下のようにIdPの認証エンドポイントへリダイレクトされます。
この際、redirect_uriscope の値はURLエンコード(パーセントエンコード)される点に注意してください。

https://login.microsoftonline.com/{tenant}/v2.0/authorize?
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&response_type=code
&redirect_uri=https%3A%2F%2Fwebapp%2Fentra_id%2Fcallback
&scope=openid%20email%20profiles
&response_mode=query
&state=12345
&nonce=67890

主要なクエリパラメータの説明:

パラメータ名 必須/任意 説明
client_id 必須 クライアント(Webアプリ)を識別するためのID。IdP(認可サーバー)に事前に登録されたアプリケーションIDです。
response_type 必須 取得したい応答のタイプ。code はOIDCの標準的なフローである認可コードフローを示します。
redirect_uri 必須 認証完了後、IdPから認可コードやエラーが送り返されるクライアントのURI。このURIはIdPに事前に登録されている必要があります。
scope 必須 認可されたアクセスの範囲や、IdPから取得したいユーザー情報の種類を指定します。openid はOIDC認証を行うために必須です。emailprofile は、それぞれユーザーのメールアドレスや基本的なプロフィール情報を要求します。
response_mode 任意 IdPからクライアントへの応答(認可コードなど)をどのように返却するかを指定します。query はURIのクエリパラメータとして返されることを示します。form_post など、他のモードもあります。
state 必須 CSRF攻撃対策に利用される、クライアントが生成するランダムな文字列。認証リクエスト時に発行し、IdPからのコールバック時に同じ値が返ってくることを確認することで、リクエストとコールバックの対応付けを行います。
nonce 必須 リプレイ攻撃対策に利用される、クライアントが生成するランダムな文字列。認証リクエスト時に発行し、IdPから発行されるIDトークンに含まれる nonce クレームと照合して検証します。

4.認可リクエスト

WebAppから上記のURLとクエリパラメータの指示をもらい、ブラウザは指定されたURLにリダイレクトします。

5.認証・同意 (ログイン画面表示)

リクエストを受け取ったIdPは、ユーザーにログインするためのプラットフォームを提供します。

6.認証情報入力

ユーザー名やパスワード、もしくはMFAなどで認証を行います。

7.認可コードを付与してリダイレクト

EntraIDは、新たにリダイレクトのURLを発行し、ブラウザに対してリダイレクトを指示します。
リダイレクトの例:

https://webapp/entra_id/callback?code=*******************

このリダイレクト先のURLは、認証エンドポイントへのリダイレクトの際に指定した、redirect_uriが指定されます。
また、resposnse_typeとしてcodeを指定しているため、クエリパラメータにcodeという変数が付与されています。
これは token_endpoint で各種トークン(ID, Access, Refresh)を発行する際に利用する一時的な値であり、Webアプリのサーバーのクライアントシークレットと併せて使うことでトークンの発行が行えます。
通常このコード(authorization_code)は、10分で期限が切れるため、このコードを使いまわすことはせず、後述するアクセストークンを用いて、EntraIDからの情報取得や、操作を行います。

8.認可コードを受け取り

認可コード(リダイレクト時のクエリパラメータ)をWebアプリにリダイレクトすることで、Webアプリ側がクエリパラメータからコードを受け取ります。
今回の例では以下のURL

https://webapp/entra_id/callback

ここのURLには、code の値を読み取り、その値とサーバー内に予めセットしているクライアントシークレットを用いることで、各種トークンの発行を試みます。

9.認可コードとクライアント認証情報でトークンリクエスト

Webアプリは次のようなリクエストを行い、各種トークンの取得を行います。
Webアプリは、認可コードとクライアント認証情報を用いて、Entra IDのトークンエンドポイントに直接HTTP POSTリクエストを送信し、各種トークンの取得を行います。この通信はサーバー間で行われるため、ブラウザからは見えません。
HTTP POSTリクエストの例 (リクエストボディは application/x-www-form-urlencoded):

POST https://login.microsoftonline.com/{tenant}/v2.0/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope=openid%20email%20profiles
&code=********
&redirect_uri=https%3A%2F%2Fwebapp%2Fentra_id%2Fcallback
&grant_type=authorization_code
&client_secret=samplesecret
パラメータ名 必須/任意 説明
client_id 必須 クライアント(Webアプリ)を識別するためのID。
scope 必須 認可リクエストと同じスコープを指定します。
code 必須 ユーザー認証後に取得した認可コード。
redirect_uri 必須 認可リクエストと同じリダイレクトURI。
grant_type 必須 認可コードフローでは authorization_code を指定します。
client_secret 必須 クライアント(Webアプリ)の機密情報。IdPに登録されているものと一致する必要があります。公開クライアントでは使用されません。

10. IDトークン、アクセストークン、リフレッシュトークンを返却

EntraIDから各種トークンの提供や、ユーザーの追加情報が返ってきます。
成功応答例

{
    "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
    "token_type": "Bearer",
    "expires_in": 3599,
    "scope": "openid%20email%20profiles",
    "refresh_token": "",
    "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD..."
}

ここで返却される各トークンには、それぞれ異なる役割があります。

  • access_token (アクセストークン):
    これは、Webアプリが保護されたリソース(例:Microsoft Graph APIなど)にアクセスする際に使用するトークンです。直接ユーザーの情報は含まず、主に権限を証明するために利用されます。通常、短時間(例:1時間)で有効期限が切れます。

  • id_token (IDトークン):
    このトークンはユーザーの認証情報を含んでおり、Webアプリがユーザーの身元を確認するために使用します。JWT(JSON Web Token)形式で、ユーザーの一意のID (sub)、トークンの発行者 (iss)、対象となるWebアプリ (aud)、発行時刻 (iat)、有効期限 (exp)、そして認証リクエスト時に渡したnonceなどが含まれます。Webアプリは受け取ったIDトークンを検証し、その内容を安全にデコードすることで、ログインしたユーザーに関する情報を取得します。

  • refresh_token (リフレッシュトークン):
    このトークンは、access_tokenが期限切れになった際に、ユーザーの再認証なしで新しいaccess_token(と場合によっては新しいid_token)を取得するために使用されます。

上記の応答例でrefresh_tokenが空になっているのは、認証リクエスト時のscopeにoffline_accessスコープが含まれていないためです。
offline_accessスコープを要求しない限り、リフレッシュトークンは発行されません。
refresh_tokenは通常、Webアプリからは内容を直接読み取ることができない不透明な文字列です。これは、認可サーバーが内部的に管理するための参照値であり、access_tokenよりも長い有効期限を持ち、オフラインアクセス(ユーザーがアクティブでない間にバックグラウンドでリソースにアクセスするなど)を許可する場合に特に有用です。
重要な点として、Entra IDではrefresh_tokenを使って新しいaccess_tokenが発行されると、以前発行された access_tokenrefresh_tokenは即座に無効化されます。これは、古いトークンが漏洩した場合のセキュリティリスクを低減するための「トークンリフレッシュ時の再利用検出」や「スライディングウィンドウ」といった仕組みによるものです。したがって、新しいaccess_tokenを取得したら、必ずそれを利用するように実装する必要があります。

このステップでWebアプリはIDトークンを検証し、ユーザーセッションを確立します。

11.サービス利用開始

取得した情報にてユーザー名(メールアドレスなど)で、Webアプリはユーザーのセッションを作成し、Webアプリへのログイン状態を確立します。
一度EntraIDとログインしているため、EntraIDとブラウザはセッションが残っていますが、これはWebアプリのセッションとは完全に別のものとなっています。
そのため、EntraIDから強制的にログアウトされたとしても、Webアプリはログインした状態となるので注意しましょう。

OIDCの認証実装例

上記で説明したOIDCの仕組みでは、RedirectやPostが何度も行われていて大変に感じますが、便利なモジュールなどが用意されているため、実装の際に意識することは少ない印象です。
とはいえ上記の仕組みを理解していると、トラブルシュートの際の原因特定が容易になるのと、モジュールを使う際の設定で迷うことが少なくなるので、覚えておいて損は無いと思います。

今回は端折りましたが、PCKEというセキュリティをより強化する仕組み(利用が推奨されている)や、IDトークンのJWTの検証などのプロセスもあるため、興味のある方は是非調べて見てください。

次からはOIDC連携の実装について書いていきます。

Ruby on Rails での実装

Rubyの代表的なWebフレームワークであるRuby on RailsでのOIDC認証実装について解説します。ここでは、広く使われている認証ライブラリである devise と、OpenID Connectに対応したOmniAuthストラテジーである omniauth_openid_connect を組み合わせて実装を進めます。

認証情報はRailsのCredentials機能で管理し、安全にアプリケーションから利用します。

config/initializers/devise.rb

Devise設定

Devise.setup do |config|
  # ... 他のDevise設定は省略 ...

  # OmniAuth OpenID Connectの設定
  config.omniauth :openid_connect, {
    # コールバックURLのパス(/users/auth/entra_id/callback)に影響します。
    name: :entra_id,

    # 要求するスコープ。refresh_tokenが必要な場合は :offline_access も追加します。
    scope: [:openid, :email, :profile],

    # 認可コードフロー(Code Flow)を利用することを示します。
    response_type: :code,

    # OpenID Connect Discovery を利用し、IdPのメタデータを自動で取得します。
    discovery: true,

    # CSRF対策のためのstateパラメータを生成します。
    state: Proc.new { SecureRandom.hex(32) },

    # IdP(Entra ID)のIssuer URLを指定します。
    # {tenant} には、ご自身のEntra IDテナントIDまたはプライマリドメイン名が入ります。
    issuer: 'https://login.microsoftonline.com/{tenant}/v2.0',

    client_options: {
      # Entra IDで発行されたアプリケーション(クライアント)ID
      identifier: Rails.application.credentials.entra_id.client_id,

      # Entra IDで発行されたクライアントシークレット
      secret: Rails.application.credentials.entra_id.client_secret,

      # 認証後のコールバックURL。
      # このURLは、Entra IDのアプリケーション登録にも正確に登録する必要があります。
      # 開発環境ではlocalhostを使用しますが、本番環境ではHTTPSのドメインに変更します。
      redirect_uri: 'http://localhost:3000/users/auth/entra_id/callback'
    }
  }
end

client_options内のidentifiersecretは、RailsのCredentials機能を使って安全に管理されています。以下のようにコマンドを実行し、ファイルを編集して認証情報を保存します。

# コマンドでエディタを開き、暗号化された認証情報を編集します。
EDITOR="vim" rails credentials:edit

このコマンドで開かれるファイルの内容は以下のようになります。

# config/credentials.yml.enc の内容例
entra_id:
  client_id: "YOUR_CLIENT_ID_FROM_ENTRA_ID"
  client_secret: "YOUR_CLIENT_SECRET_FROM_ENTRA_ID"

この情報は、config/credentials.yml.enc に暗号化された形式で保存されるため、バージョン管理システム(Gitなど)で安全に共有できます。

Apache + mod_auth_openidc での実装

一部のLinuxディストリビューションでは、mod_auth_openidc が標準のdnf/yumリポジトリに存在しない場合があり、ソースコードから自分でビルドして作成する必要があります。
ビルドの際には依存関係としてcjoseが必要になりますが、cjoseの公式リポジトリは既に更新が停止しているため、mod_auth_openidcのGitHubリポジトリにあるforkされたcjoseを利用すると良いでしょう。

ここではモジュールの作成手順は割愛し、Amazon Linuxを例にApacheの設定について記載します。

/etc/httpd/conf.d/openidc.conf

Apache設定

# モジュールの読み込み
LoadModule auth_openidc_module modules/mod_auth_openidc.so

# 認証関連のパラメータが記載されたメタデータのURLを設定します。
# {tenant} には、ご自身のEntra IDテナントのテナントIDまたはプライマリドメイン名が入ります。
OIDCProviderMetadataURL https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration

# クライアントIDとシークレットを設定します。
# Entra IDで定義したアプリケーション登録で発行された値を指定してください。
# 例: OIDCClientID your-client-id-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
OIDCClientID YOUR_CLIENT_ID
# 例: OIDCClientSecret your_client_secret_xxxxxxxxxxxxxxxxxxxxxxxx
OIDCClientSecret YOUR_CLIENT_SECRET

# 認証後のリダイレクト先URI (コールバックURI) を設定します。
# このURIは、Entra IDに事前に登録された「リダイレクトURI」と完全に一致させる必要があります。
# mod_auth_openidcがこのパスへのリクエストを処理するため、ここに静的なファイルや他のWebアプリケーションが存在してはいけません。
OIDCRedirectURI https://webapp/entra_id/callback

# OIDC認証を有効にするLocationディレクティブ
<Location /entra_id/callback> # OIDCRedirectURIのパスと一致させる
    AuthType openid-connect
    Require valid-user
</Location>

# OIDC認証フローで取得するスコープを指定します。
# openid はOIDC認証に必須です。profile や email はユーザー情報を取得するために含めます。
OIDCScope "openid profile email"

# OIDC認証の応答タイプ(フロー)を指定します。認可コードフローでは 'code' を指定します。
OIDCResponseType code

# OIDCモジュールがセッション情報などの暗号化に利用するパスフレーズ。
# 十分な長さと複雑さを持つランダムな文字列(例: 32文字以上)を設定してください。
# `head /dev/urandom | tr -dc A-Za-z0-9_ | head -c 32` のようなコマンドで生成できます。
OIDCCryptoPassphrase your_long_random_cryptopassphrase_here

# リバースプロキシやロードバランサーの背後でHTTPS通信をオフロードしている場合に必要な設定です。
# mod_auth_openidc が、HTTPリクエストであるにも関わらず、実際はHTTPSで接続されたと判断できるように、
# プロキシが設定するヘッダー(例: X-Forwarded-Proto)を指定します。
# これを設定しないと、HTTPS必須のOIDC要件に違反すると見なされ、認証エラーになる可能性があります。
OIDCXForwardedHeaders X-Forwarded-Proto X-Forwarded-Port

Nginx + OAuth2Proxy での実装

NginxとOAuth2Proxyを組み合わせてOIDC認証を行う場合、Nginxのauth_requestモジュールを利用して、認証処理をOAuth2Proxyに委任します。
これにより、NginxがWebサーバーとしての役割に徹しつつ、柔軟な認証メカニズムを導入できます。

ここでは、OAuth2Proxyのインストールと設定、およびNginxでの具体的な設定について記載します。

また、以下の例ではセッションストレージにCookieを利用していますが、推奨されません。
ユーザー情報をCookieに保存するため、Cookieサイズが大きくなり、分割が発生します。
Cookieが分割すると、Cookieの更新タイミングがずれ、認証が失敗し、頻繁に認証が走ることになります。
また、Webアプリ側でのセッション制御が実質行えないため、基本的に redis を使い、ユーザー側のCookieにはセッションIDのみを格納するようにすることがお勧めです。

OAuth2-Proxyのダウンロードと設定

OAuth2ProxyはGo言語で開発されており、バイナリファイルとして配布されています。
次のようにパッケージをダウンロード+設置できます。  

# 例: /usr/local/bin に配置
curl -L https://github.com/oauth2-proxy/oauth2-proxy/releases/download/v7.8.1/oauth2-proxy-v7.8.1.linux-amd64.tar.gz | sudo tar -xz -C /usr/local/bin --strip-components=1 oauth2-proxy

設定ファイルを用意する前にCookieの暗号化に利用するシークレットを作成します。

# openssl rand -base64 32 | tr -- '+/' '-_'

/etc/oauth2-proxy/oauth2-proxy.cfg に以下の内容で設定ファイルを作成します。YOUR_CLIENT_ID_FROM_ENTRA_IDYOUR_CLIENT_SECRET_FROM_ENTRA_IDYOUR_GENERATED_COOKIE_SECRET{tenant_id_or_domain}https://nginx.your-domain.com/oauth2/callback はご自身の環境に合わせて置き換えてください。

# /etc/oauth2-proxy/oauth2-proxy.cfg
provider = "entra-id"
reverse_proxy = true
oidc_issuer_url = "https://login.microsoftonline.com/{tenant_id_or_domain}/v2.0"
skip_provider_button = true
client_id = "YOUR_CLIENT_ID_FROM_ENTRA_ID"
client_secret = "YOUR_CLIENT_SECRET_FROM_ENTRA_ID"
cookie_secure = false # 本番環境でHTTPS終端している場合はtrueに設定
cookie_samesite = "lax"
cookie_secret = "YOUR_GENERATED_COOKIE_SECRET"
redirect_url = "https://nginx.your-domain.com/oauth2/callback" # Entra IDに登録するリダイレクトURIと一致
email_domains = ["*"] # 許可するメールドメイン。全て許可する場合は "*"

注意: redirect_url はEntra IDのアプリケーション登録で「リダイレクトURI」として正確に登録してください。 cookie_secure は、リバースプロキシやロードバランサーでHTTPSを終端している場合、OAuth2Proxy自体はHTTPで動作するためfalseに設定することもありますが、本番環境ではブラウザからOAuth2Proxyまでの経路全体がHTTPSであることを確認し、trueに設定すべきです。

OAuth2Proxyをサービスとして起動し、管理できるようにします。
ExecStart のパスは、実際にOAuth2Proxyを配置した場所に合わせてください。

# /etc/systemd/system/oauth2-proxy.service
[Unit]
Description=OAuth2 Proxy Service
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/oauth2-proxy --config=/etc/oauth2-proxy/oauth2-proxy.cfg
ExecReload=/bin/kill -HUP $MAINPID
Restart=always

[Install]
WantedBy=multi-user.target

サービスファイルを保存後、以下のコマンドでサービスを起動します。

sudo systemctl daemon-reload
sudo systemctl start oauth2-proxy
sudo systemctl enable oauth2-proxy # 自動起動を有効にする場合

OAuth2Proxyの設定は以上で終わりです。

次にNginxの設定を行っていきます。

Nginxの設定ファイル(例: /etc/nginx/conf.d/oidc.conf)に以下を記述します。これにより、/ へのアクセス時にOAuth2Proxyによる認証が行われます。今回は、ALBでSSL証明書を終端しているため、NginxはHTTPポート(80番)でリッスンする例です。

nginx設定

# /etc/nginx/conf.d/oidc.conf
server {
    listen 80;
    server_name nginx.your-domain.net; # ご自身のドメインに置き換え

    # OAuth2Proxyが設定するCookieのサイズが大きい場合があるため、バッファを拡張
    proxy_buffer_size 16k;
    proxy_buffers 4 32k;
    proxy_busy_buffers_size 64k;
    large_client_header_buffers 8 64k;

    # 認証を要求するパス(例: /)
    location / {
        auth_request /oauth2/auth; # OAuth2Proxyの認証エンドポイントにリクエストを送信
        error_page 401 = /oauth2/sign_in; # 未認証時はOAuth2Proxyのサインインページへ

        # OAuth2Proxyがセットした認証Cookieをクライアントに透過
        auth_request_set $auth_cookie $upstream_http_set_cookie;
        add_header Set-Cookie $auth_cookie;

        # 認証成功時にOAuth2Proxyが付与するユーザー情報をヘッダーとしてアプリケーションに渡す
        auth_request_set $user $upstream_http_x_auth_request_user;
        auth_request_set $email $upstream_http_x_auth_request_email;
        proxy_set_header X-User $user;
        proxy_set_header X-Email $email;

        # 認証成功時に表示するコンテンツのルートパス
        root /usr/share/nginx/html;
        index index.html index.htm;
    }

    # OAuth2Proxyの各種エンドポイントへのリクエストをプロキシ (デフォルト: 127.0.0.1:4180)
    location /oauth2/ {
        proxy_pass http://127.0.0.1:4180;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme; # 元のプロトコル (HTTP/HTTPS) をOAuth2Proxyに伝える
    }

    # Nginxのauth_requestモジュールが認証ステータスを確認するために使用する内部ロケーション
    location = /oauth2/auth {
        internal; # 外部からの直接アクセスを禁止
        proxy_pass http://127.0.0.1:4180;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header Content-Length "";
        proxy_pass_request_body off;
    }

    # ロードバランサーからのヘルスチェック用ロケーション
    location /healthcheck {
        access_log off;
        return 200 'OK';
        add_header Content-Type text/plain;
    }
}

Nginxの設定を反映させるために、Nginxを再起動します。

sudo systemctl reload nginx

おわりに

長くなりましたが、このブログが少しでも皆様のOIDCへの理解に貢献出来たら幸いです。

最近はもっぱらネットワーク機器を触る機会が減り、SaaSのオペレーションや、SaaSを利用した業務改善をメインに行っています。

昨今ではAIサービスも普及しており、このブログの添削も、内容や流れを書いた後の添削や言い回しはGeminiさんに手伝ってもらって修正しています。

これからも色々な技術を吸収して、色々な解決策を提示できるエンジニアを目指して邁進していきたいと思います。

ここまで読んでいただきありがとうございました。