こんにちは、滝澤です。
Apache HTTP ServerをOpenID Connect Relying Partyにするmod_auth_openidcというモジュールを使ってみる機会がありましたので、本記事で情報共有します。
なお、記事が長くなったので本編と設定例である3編に分けました。
本記事では、OP(OpenID Provider)としてGoogle(G Suite)を利用する場合の設定例を紹介します。
設定例
ウェブサイトURI
まず、ウェブサイトURIとリダイレクトURIを決めます。
ウェブサイトURIには、利用するウェブサイトのURIを指定します。このとき、パス名を付けない形式にしてください。
リダイレクトURIには、リダイレクトURI(redirect_uri)として使用されるURIを指定します。コンテンツが存在するURIに被らないものにしてください。このリダイレクトURI経由でリクエストのやりとりが行われます。
ウェブサイトURI | https://example.com |
リダイレクトURI | https://example.com/redirect_uri |
アプリケーションの登録とクライアントIDとクライアントシークレットの取得
G Suiteを使う場合は次のヘルプのページに従って、アプリケーションの登録とクライアントIDとクライアントシークレットを取得してください。
- OAuth 2.0 の設定 (Google)
このとき、以下のような値を指定します。
アプリケーションの種類 | ウェブアプリケーション |
承認済みの JavaScript 生成元 | 先の「ウェブサイトURI」で指定した値(例: https://example.com) |
承認済みのリダイレクト URI | 先の「リダイレクトURI」で指定した値(例: https://example.com/redirect_uri) |
設定ファイルの編集
設定ファイル /etc/httpd/conf.d/auth_openidc.conf を編集して、各ディレクティブを設定します。
以降、ディレクティブ毎に説明します。ただし、デフォルト値で特に問題ないと思われるものについては説明はしません。
OIDCRedirectURI
リダイレクトURI(request_uri)として指定した値を記述します。
OIDCRedirectURI https://example.com/redirect_uri
OIDCCryptoPassphrase
cookieやキャッシュエントリーなどの暗号化に利用するパスフレーズの文字列を指定します。
OIDCCryptoPassphrase XXXXXXXXXXXXXXXXXXXXXXXX
OIDCProviderMetadataURL
OpenID Connect ProviderメタデータのURIを記述します。 Googleの場合は次のようになります。
OIDCProviderMetadataURL https://accounts.google.com/.well-known/openid-configuration
ちなみに、執筆時点でのGoogleのメタデータの内容は次のようになります。 エンドポイントの情報だけでなく、サポートしているタイプやアルゴリズムなども取得できます。
$ curl https://accounts.google.com/.well-known/openid-configuration 2>/dev/null | jq { "issuer": "https://accounts.google.com", "authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth", "token_endpoint": "https://www.googleapis.com/oauth2/v4/token", "userinfo_endpoint": "https://www.googleapis.com/oauth2/v3/userinfo", "revocation_endpoint": "https://accounts.google.com/o/oauth2/revoke", "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs", "response_types_supported": [ "code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token", "none" ], "subject_types_supported": [ "public" ], "id_token_signing_alg_values_supported": [ "RS256" ], "scopes_supported": [ "openid", "email", "profile" ], "token_endpoint_auth_methods_supported": [ "client_secret_post", "client_secret_basic" ], "claims_supported": [ "aud", "email", "email_verified", "exp", "family_name", "given_name", "iat", "iss", "locale", "name", "picture", "sub" ], "code_challenge_methods_supported": [ "plain", "S256" ] }
OIDCScope
scopeを指定します。 "openid"は必須です。
Googleの場合は、Authentication URI parametersより、"openid", "email", "profile"をサポートしていることがわかります。 これらを全て指定すると次のようになります。
OIDCScope "openid email profile"
OIDCAuthRequestParams
Authentication Requestの際に追加で要求するパラメータを指定します。
Googleの場合は、 Authentication URI parametersを見ると、prompt, display, hdなどのパラメータを追加で要求することができます。
promptパラメータには再認証および同意を要求するかを指定します。次の値を指定することができます。
- none(認証や同意の画面を表示しない)
- consent(クライアントにレスポンスを返す前に同意画面を表示する)
- select_account(ユーザーアカウントの選択画面を表示する)
何も指定しなければ、同意画面やログイン画面なしに再認証されます。
displayパラメータには認証および同意のためのユーザーインタフェースを指定します。
- page(ページ画面)
- popup(ポップアップ)
- touch(タッチインターフェース向け)
- wap(フィーチャーフォン向け)
しかし、Googleの場合はこのパラメータを指定しても動作は変わらないため、指定する必要はありません。
hdパラメータにはG Suiteで利用しているドメイン名を指定します。指定しない場合には、Googleの認証に成功したユーザーすべてに対して、認可されてしまうことになります。 別途、apacheのアクセス制御としても認可を制御できますが、この段階で制御した方が好ましいでしょう。
ここでは、次のように設定して、認可される対象のドメイン名を"example.com"に制限します。
OIDCAuthRequestParams hd=example.com
同意画面を表示させたい場合は次のようにします。
OIDCAuthRequestParams prompt=consent&hd=example.com
OIDCResponseType
レスポンスタイプ(response_type)を指定します。
OpenID Connect Core 1.0 -- 3. Authenticationに次の内容の"response_type"とFlowの対応が記述されています。
"response_type" value | Flow |
---|---|
code | Authorization Code Flow |
id_token | Implicit Flow |
id_token token | Implicit Flow |
code id_token | Hybrid Flow |
code token | Hybrid Flow |
code id_token token | Hybrid Flow |
「Authorization Code Flow」の場合は次のように"code"を指定します。
OIDCResponseType "code"
OIDCClientID
クライアントID(client_id)を指定します。
OIDCClientID XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com
OIDCClientSecret
クライアントシークレット(client_secret)を指定します。
OIDCClientSecret XXXXXXXXXXXXXXXXXXXXXXXX
OIDCPKCEMethod
PKCE code challenge method(code_challenge_method)を指定します。 "S256"でよいでしょう。
OIDCPKCEMethod S256
OIDCSessionInactivityTimeout
無操作タイムアウトの秒数を指定します。デフォルトは300秒です。
OIDCSessionInactivityTimeout 300
OIDCSessionMaxDuration
セッション有効期間を秒数で指定します。デフォルトは28800秒(8時間)です。 0を指定すると、IDトークンの期限と同じになります。
OIDCSessionMaxDuration 28800
OIDCCacheType
キャッシュのストレージのタイプを指定します。
本記事の動作確認ではRedisを使うので、"redis"を指定します。
OIDCCacheType redis
さらに、関連するディレクティブの設定も行ってください。
例えば、redisを指定した場合は下記のようにOIDCRedisCacheServerとOIDCRedisCachePasswordの指定も行う必要があります。
OIDCRedisCacheServer 127.0.0.1 #OIDCRedisCachePassword 8dTjsdkngM4Y2okg3XjYgWRX
OIDCHTMLErrorTemplate
エラーページのテンプレートファイルのパスを指定します。
OIDCHTMLErrorTemplate /etc/httpd/conf.d/auth_openidc.error.html
上記の設定を行った場合は、ファイル"/etc/httpd/conf.d/auth_openidc.error.html"に次のような内容の記述を行ってください。1つ目の%sにはエラーメッセージが、2つ目の%sにはエラーの説明が埋め込まれます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"/> <title>OpenID Connect: Error</title> </head> <body> <h1>OpenID Connect: Error</h1> <p>Message: %s</p> <p>Description: %s</p> <p><a href="/">Home</a></p> </body> </html>
OIDCDefaultLoggedOutURL
ログアウト後に遷移するURIを指定します。
OIDCDefaultLoggedOutURL https://example.com/loggedout.html
上記のように設定した場合は、次のよう内容のファイルloggedout.htmlをドキュメントルートに配置します。 再びログインできるようにトップページへにリンクを用意します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"/> <title>OpenID Connect: Logged out</title> </head> <body> <h1>OpenID Connect: Logged out</h1> <p><a href="/">Login</a></p> </body> </html>
OIDCClaimPrefix
claimを環境変数やHTTPヘッダーフィールドにする際に付与するプレフィックスを指定します。
OIDCClaimPrefix OIDC_CLAIM_
例えば、claimの一つ"name"は環境変数あるいはHTTPヘッダーフィールドの名前としては"OIDC_CLAIM_name"になります。
リバースプロキシーとして動かしている場合は、バックエンド側ウェブサーバー側でHTTPヘッダーを環境変数に変換する際の"-"と"_"の曖昧性を排除するために、次のように"_"を使わないようにした方がよいと思われます。
OIDCClaimPrefix OIDC-CLAIM-
OIDCRemoteUserClaim
apacheの認証されたユーザーIDを示す変数REMOTE_USERとして設定するclaimの名前を指定します。
デフォルトでは「sub@iss」の形式になります。 subはエンドユーザーの識別子で、issはIssuerから"https://"を除いたものになります。
Googleの場合は「sub@iss」の形式では「ユーザーを識別する約20桁の数字@accounts.google.com」となり、誰がログインしたのかわかりにくいので、一目で識別できる他の情報を指定してもよいでしょう。 この例では、emailから@の左側の文字列を取り出してユーザーIDとします。
OIDCRemoteUserClaim email ^(.*)@
OIDCPassClaimsAs
claimをアプリケーション環境にどのようにして渡すかを指定します。
- none
- environment (環境変数)
- headers (HTTPヘッダー)
- both (両方) (default)
OIDCPassClaimsAs both
OIDCAuthNHeader
リバースプロキシーとして動かしている場合は、REMOTE_USERの情報をバックエンドに送信するHTTPヘッダーに追加するヘッダーフィールド名を指定します。
OIDCAuthNHeader X-Remote-User
動作確認用コンテンツの配置
動作確認として取得した変数を表示するために、次の内容のSSIを使ったページindex.shtmlをドキュメントルートに配置してみます。
REMOTE_USER
はユーザーIDに、OIDC_CLAIM_name
はユーザーのフルネームに、OIDC_CLAIM_picture
はユーザーのプロフィール画像のURLに置き換わります。
明示的にログアウトできるように、ログアウトするパス「/redirect_uri?session=logout」へのリンクを用意します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"/> <title>OpenID Connect</title> </head> <body> <h1>OpenID Connect</h1> <p> <img src="<!--#echo var="OIDC_CLAIM_picture" -->" alt="" height="24" width="24"/> <!--#echo var="OIDC_CLAIM_name" --> (REMOTE_USER=<!--#echo var="REMOTE_USER" -->) </p> <p><a href="/redirect_uri?session=logout">Logout</a></p> </body> </html>
apacheの設定
apacheの設定は以下のようになります。
<Location /> # OpenID Connectによる認証・認可を要求する。 AuthType openid-connect # G Suiteの認可設定: # claimのhdがexample.comである場合のみ認可する。 Require claim hd:example.com # 動作確認のためのSSI用の設定を行う。 Options +Includes DirectoryIndex index.shtml index.html </Location> # ログアウト後に遷移するページでは認証されていないユーザーにもアクセスを許可する。 <Location /loggedout.html> Require all granted </Location>
設定の反映
設定が完了したら、設定を反映するために、apacheを再起動してみましょう。
$ sudo apachectl configtest $ sudo systemctl reload httpd.service
実際にアクセスしてみて何か問題があれば、エラーログに出力されるので確認しましょう。
$ sudo less /var/log/httpd/error_log
動作確認
ウェブブラウザで動作確認用のサイトにアクセスすると、OpenID Connectによる認証・認可の処理が行われます。
Google (G Suite)で認証済みでなければ、Googleのログインページにリダイレクトされるのでログインの操作を行います。
認証済みのユーザーの場合、あるいはログインが完了すると次のようなページが表示されます。
氏名とユーザーIDが表示されていれば成功です。
参考サイト
- modauthopenidc (GitHub)
- Google Identity Platform - OpenID Connect (Google)