1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

mod_auth_openidc で OpenID Connect (OIDC) 認証を --- 4. マルチプロバイダ編

Posted at

Apache HTTP Server + mod_auth_openidc で OpenID Connect (OIDC) 認証が必要な Web サイトを作ってみます。

本記事は「1. 準備編」「2. d アカウント・コネクト編」「3. Google アカウント編」に続く「4. マルチプロバイダ編」です。d アカウント・コネクト編や Google アカウント編では、d アカウント・コネクトや Google アカウントでソーシャルログインできましたが、どちらを使うか設定によって切り替える必要があり、ユーザが選ぶことはできませんでした。本記事は Google アカウント編の設定が終わっている状態から、どちらでログインするかユーザが自分で選べるようにしてみます。

もしかしたらマズい設定などがあるかもしれません。気が付いた方はご指摘いただけるとありがたいです。

mod_auth_openidc

d アカウント・コネクト編で作った /etc/apache2/mods-available/auth_openidc_d_account_connect.conf や Google アカウント編で作った /etc/apache2/mods-available/auth_openidc_google_account.conf のように、マルチプロバイダ用の設定ファイルを /etc/apache2/mods-available/auth_openidc_multiple_provider.conf に作ります。

マルチプロバイダ用設定ファイル

設定項目は OIDCMetadataDir だけです。マルチプロバイダ用のメタデータを置くディレクトリを指定します。とりあえずデフォルトの設定ファイル /etc/apache2/mods-available/auth_openidc.conf でコメントアウトで設定されていたディレクトリをそのまま指定します。これを /etc/apache2/mods-available/auth_openidc_multiple_provider.conf に置きます。

auth_openidc_multiple_provider.conf
OIDCMetadataDir /var/cache/apache2/mod_auth_openidc/metadata

各 OIDC プロバイダのメタデータ

メタデータ用のディレクトリに各 OIDC プロバイダ (OP) の情報を置きます。1 つの OP につき下記 3 つのファイルを置きます。ファイル名の <urlencoded-issuer-value-with-https-prefix-and-trailing-slash-stripped> の部分は OP の issuer 文字列(ID トークンの iss クレームの文字列)から、最初の https:// と(あれば)最後の / を取って URL エンコードした文字列です。

  • <urlencoded-issuer-value-with-https-prefix-and-trailing-slash-stripped>.provider
    • OpenID Connect Discovery で得られた json そのもの
  • <urlencoded-issuer-value-with-https-prefix-and-trailing-slash-stripped>.client
    • クライアント ID とクライアントシークレットが入った json
  • <urlencoded-issuer-value-with-https-prefix-and-trailing-slash-stripped>.conf
    • OP 毎に変更する設定値の json

Google アカウント

まずは OpenID Connect Discovery の json を取得します。ファイル名は仮に google.json にしておきます。

$ curl https://accounts.google.com/.well-known/openid-configuration -o google.json

ここから issuer の文字列を取り出し、最初の https:// と(あれば)最後の / を取った文字列を求めます。

$ cat google.json | jq .issuer | sed 's|^"||;s|"$||;s|^https://||;s|/$||'
accounts.google.com

accounts.google.com が得られたので、これをさらに URL エンコードします(この場合は変わりませんけども)。

$ echo -n 'accounts.google.com' | jq -s -R -r @uri
accounts.google.com

というわけで最終的に accounts.google.com が得られました。ので google.jsonaccounts.google.com.provider にリネームします。

$ mv google.json accounts.google.com.provider

次に accounts.google.com.client としてクライアント ID とクライアントシークレットが入った json を用意します。以下のようにします。

accounts.google.com.client
{
    "client_id":
    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
    "client_secret":
    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

最後に accounts.google.com.conf として OP 毎に変更する設定値を用意します。以下のようにしてみました。

accounts.google.com.conf
{
    "scope": "openid email profile",
    "response_type": "code",
    "pkce_method": "S256"
}

以上の 3 ファイル accounts.google.com.provider, accounts.google.com.client, accounts.google.com.conf をメタデータディレクトリ /var/cache/apache2/mod_auth_openidc/metadata に入れておきます。なお、このディレクトリと 1 つ上のディレクトリ(/var/cache/apache2/mod_auth_openidc)は最初からオーナーが www-data:www-data 、パーミッションが 700 になっていましたので、今回入れるファイルも同オーナー、パーミッション 600 にしておきます。

$ sudo cp accounts.google.com.* /var/cache/apache2/mod_auth_openidc/metadata/
$ sudo chown www-data:www-data /var/cache/apache2/mod_auth_openidc/metadata/accounts.google.com.*
$ sudo chmod 600 /var/cache/apache2/mod_auth_openidc/metadata/accounts.google.com.*

d アカウント・コネクト

d アカウント・コネクトは OpenID Connect Discovery に対応しているか否かよくわからなかったので、 https://github.com/auth0/passport-daccount の設定値や 2. d アカウント・コネクト編 の設定値、そして手動で ID トークンを取得してみた時の挙動などから適当に以下のように作ってみました。

d_account_connect.json
{
 "issuer": "https://conf.uw.docomo.ne.jp/",
 "authorization_endpoint": "https://id.smt.docomo.ne.jp/cgi8/oidc/authorize",
 "token_endpoint": "https://conf.uw.docomo.ne.jp/common/token",
 "userinfo_endpoint": "https://conf.uw.docomo.ne.jp/common/userinfo",
 "response_types_supported": [
  "code"
 ],
 "subject_types_supported": [
  "public"
 ],
 "id_token_signing_alg_values_supported": [
  "HS256"
 ],
 "scopes_supported": [
  "openid"
 ],
 "token_endpoint_auth_methods_supported": [
  "client_secret_basic"
 ],
 "grant_types_supported": [
  "authorization_code"
 ]
}

ここから issuer の文字列を取り出し、ファイル名を求めて、ファイル名を変更します。

$ cat d_accoutn_connect.json | jq .issuer | sed 's|^"||;s|"$||;s|^https://||;s|/$||'
conf.uw.docomo.ne.jp

$ echo -n 'conf.uw.docomo.ne.jp' | jq -s -R -r @uri
conf.uw.docomo.ne.jp

$ mv d_accoutn_connect.json conf.uw.docomo.ne.jp.provider

次に conf.uw.docomo.ne.jp.client としてクライアント ID とクライアントシークレットが入った json を用意します。以下のようにします。

conf.uw.docomo.ne.jp.client
{
    "client_id":
    "xxxxxxxxxxxxxxxx",
    "client_secret":
    "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

最後に conf.uw.docomo.ne.jp.conf として OP 毎に変更する設定値を用意します。以下のようにしてみました。

conf.uw.docomo.ne.jp.conf
{
    "scope": "openid",
    "response_type": "code"
}

以上 3 ファイル をメタデータディレクトリに入れ、オーナーとパーミッションを設定します。

$ sudo cp conf.uw.docomo.ne.jp.* /var/cache/apache2/mod_auth_openidc/metadata/
$ sudo chown www-data:www-data /var/cache/apache2/mod_auth_openidc/metadata/conf.uw.docomo.ne.jp.*
$ sudo chmod 600 /var/cache/apache2/mod_auth_openidc/metadata/conf.uw.docomo.ne.jp.*

Apache サイト設定

/etc/apache2/sites-available/example.com-ssl.conf でインクルードしている mod_auth_openidc の設定ファイルを先ほど作成した設定ファイルへ変更します。クライアントシークレットなどの設定はメタデータの方で指定するため不要になるので削除します。

		Include mods-available/auth_openidc_google_account.conf
		Include mods-available/auth_openidc_client_secret_google.conf

		Include mods-available/auth_openidc_multiple_provider.conf

に変更します。ファイル全体としては以下のようになります。

example.com-ssl.conf
<IfModule mod_ssl.c>
	<VirtualHost _default_:443>
		ServerAdmin webmaster@example.com

		DocumentRoot /var/www/example.com

		ErrorLog ${APACHE_LOG_DIR}/error.log
		CustomLog ${APACHE_LOG_DIR}/access.log combined

		SSLEngine on

		SSLCertificateFile /etc/ssl/certs/ssl-cert-test-example-com.pem
		SSLCertificateKeyFile /etc/ssl/private/ssl-cert-test-example-com.key

		SSLCertificateChainFile /etc/apache2/ssl.crt/test-intermediate-ca.pem

		Include mods-available/auth_openidc_multiple_provider.conf
		OIDCRedirectURI https://example.com/oidc/redirect_uri
		OIDCCryptoPassphrase foobarbaz
		OIDCCookiePath /oidc/
		OIDCDefaultLoggedOutURL https://example.com/loggedout.html

		<Location /oidc/>
			AuthType openid-connect
			Require valid-user

			Options +Includes
			DirectoryIndex index.shtml index.html
		</Location>
	</VirtualHost>
</IfModule>

設定の確認とサービス再起動

設定が正しいか確認します。

$ sudo apache2ctl configtest
Syntax OK

として上記のように Syntax OK と出たら確認 OK ですので、

$ sudo service apache2 restart

として再起動します。

動作確認

Windows のブラウザ(Edge など)で https://example.com を開き、「ログイン」をクリックします。「Select your OpenID Connect Identity Provider」という OP を選択する画面になり「conf.uw.docomo.ne.jp/」か「accounts.google.com」を選べるようになります。前者を選ぶと d アカウント・コネクトの認証画面に、後者を選ぶと Google アカウントの認証画面になって、どちらも認証されたら OIDC 動作確認用コンテンツ「OpenID Connect ログインしました」画面(ログイン中画面)になります。一旦「ログアウト」してトップに戻ってから再度「ログイン」すると、再度 OP 選択ができます。

やはりどちらを選んでも共通的に得られる情報は REMOTE_USER だけなので、ユーザ別に処理を変えたい場合には REMOTE_USER を見るのがよさそうです。例えば、特定のユーザだけに使わせたいような機能の場合は、あらかじめ何らかの方法で使用を許可する REMOTE_USER のリストなどを持っておいて、それとリクエストのあった REMOTE_USER の内容を照合するような方法ができそうです。

OP 選択画面のカスタマイズ

ここまでで出てくる OP 選択画面「Select your OpenID Connect Identity Provider」は mod_auth_openidc のデフォルトのものですが、「d アカウント・コネクト」などの名称が出てきませんしユーザには少々不親切な感じがします。そこでこれをカスタマイズしてみます。

CGI

カスタマイズするために mod_auth_openidc の説明 を読むと動的なパラメータ x_csrf を扱わなければならないことがわかります。つまり、選択画面を静的なページとして用意することができないということになります。パラメータによって動的にページを生成する方法はいくつかありますが、ここでは古くから使われている CGI を使ってみることにします。

CGI 有効化

まず、CGI が使えるように有効化(/etc/apache2/mods-enabled にシンボリックリンクを作る)します。

$ sudo a2enmod cgid

コンテンツ

とりあえず Ubuntu 20.04 には Python 3.8.10 が入っていたので、これで 適当に CGI を作ってみました。この op_selector.pyhttps://example.com/oidc/cgi-bin/op_selector.cgi でアクセスできるようにします。まずはファイルを置きます。

$ git clone https://gist.github.com/29974ea939e7b57a8539f0626cd8df6b.git
$ cd 29974ea939e7b57a8539f0626cd8df6b
$ sudo mkdir -p /var/www/example.com/oidc/cgi-bin/
$ sudo cp op_selector.py /var/www/example.com/oidc/cgi-bin/op_selector.cgi

この CGI は最初の方の変数 ISS_LIST が、OP 選択用表示名と iss のタプル、を入れたリストになっています。とりあえず d アカウント・コネクトと Google アカウントの 2 択になるようにしてありますが、このリストをいじれば他の OP を追加したり削除したりすることができます。また、コールバックの URL がパラメータ oidc_callback で指定されなかった場合(普通はそんなことないハズですが)のデフォルトコールバック URL を変数 DEFAULT_CALLBACK_URL へ指定するようにしています。

サイト設定

/etc/apache2/sites-available/example.com-ssl.conf に、 https://example.com/oidc/cgi-bin/ 以下で CGI が使えるようにする以下の設定を入れます。

		<Location /oidc/cgi-bin/>
			Options +ExecCGI
			AddHandler cgi-script .cgi
		</Location>

また、 https://example.com/oidc/cgi-bin/op_selector.cgi は /oidc/ 以下にあるので、そのままでは OIDC 認証が必要になってしまいます。しかし、OP 選択時にはまだ認証が終わっていない状態で表示できなければなりません。そこで、この URL だけ認証を必要としないようにする以下の設定を入れます。

		<Location /oidc/cgi-bin/op_selector.cgi>
			Require all granted
		</Location>

最後に、mod_auth_openidc に対して、デフォルトの OP 選択画面でなくて https://example.com/oidc/cgi-bin/op_selector.cgi を使うように指示する以下の設定を入れます。

		OIDCDiscoverURL https://example.com/oidc/cgi-bin/op_selector.cgi

ファイル全体は以下のようになります。

example.com-ssl.conf
<IfModule mod_ssl.c>
	<VirtualHost _default_:443>
		ServerAdmin webmaster@example.com

		DocumentRoot /var/www/example.com

		ErrorLog ${APACHE_LOG_DIR}/error.log
		CustomLog ${APACHE_LOG_DIR}/access.log combined

		SSLEngine on

		SSLCertificateFile /etc/ssl/certs/ssl-cert-test-example-com.pem
		SSLCertificateKeyFile /etc/ssl/private/ssl-cert-test-example-com.key

		SSLCertificateChainFile /etc/apache2/ssl.crt/test-intermediate-ca.pem

		Include mods-available/auth_openidc_multiple_provider.conf
		OIDCRedirectURI https://example.com/oidc/redirect_uri
		OIDCCryptoPassphrase foobarbaz
		OIDCCookiePath /oidc/
		OIDCDefaultLoggedOutURL https://example.com/loggedout.html
		OIDCDiscoverURL https://example.com/oidc/cgi-bin/op_selector.cgi

		<Location /oidc/>
			AuthType openid-connect
			Require valid-user

			Options +Includes
			DirectoryIndex index.shtml index.html
		</Location>
		<Location /oidc/cgi-bin/>
			Options +ExecCGI
			AddHandler cgi-script .cgi
		</Location>
		<Location /oidc/cgi-bin/op_selector.cgi>
			Require all granted
		</Location>
	</VirtualHost>
</IfModule>

設定の確認とサービス再起動

再度設定の確認とサービス再起動をします。

$ sudo apache2ctl configtest
$ sudo service apache2 restart

動作確認

いったんログアウトしてから再度ログインしようとすると、カスタマイズされた OP 選択画面になり、「d アカウント・コネクト」をクリックすると d アカウント・コネクトで、「Google アカウント」をクリックすると Google アカウントで、それぞれ認証ができるようになりました。

次回

Web で OIDC 認証としては、とりあえずできるところまでできたかなと思っています。次はネイティブアプリと連携して REST API 的なサービスを提供するにはどうすればいいのか考えてみたいと思っています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?