mod_auth_openidc で OpenID Connect (OIDC) 認証を --- 2. d アカウント・コネクト編

Apache HTTP Server + [mod\_auth\_openidc](https://github.com/zmartzone/mod_auth_openidc) で OpenID Connect (OIDC) 認証が必要な Web サイトを作ってみます。

本記事は「[1. 準備編](https://qiita.com/trueroad/items/1bcc1870d0b03e6f5029)」の続き「2. d アカウント・コネクト編」です。準備編の設定が終わっている状態からはじめて、d アカウント・コネクトでソーシャルログインできるようにしてみます。次の記事は d アカウント・コネクトではなくて Google アカウントでソーシャルログインできるようにしてみる「[3. Google アカウント編](https://qiita.com/trueroad/items/593870ebafb675196e88)」です。さらにその次にはログイン時に d アカウント・コネクトか Google アカウントかを選択できるようにする「4. マルチプロバイダ編」を予定しています。

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

## d アカウント・コネクト

d アカウントでソーシャルログインできるサービスです。OpenID Connect に対応しているので mod\_auth\_openidc で使えるハズです。ログイン機能のみであればオンラインでの申し込みが可能かつ無料で利用でき、メールアドレスやその他のユーザ情報が欲しければ別途審査が必要だったり有償になったりするようです（2022年6月現在）。

### 設定

今回は、無償・オンライン申し込みで利用できるタイプのものを使います。申し込み方法などは本記事では説明しませんのでご自分でお調べください。申請が通ると技術情報としてマニュアルやライブラリなどがダウンロードできるようになるようですが、諸事情によりそれらは一切参照せず利用せずに設定していきます。

払い出してもらう設定と、こちらから設定する項目とその内容は以下の通りです。

* 払い出してもらう設定
    + クライアント ID（文字列）
    + クライアントシークレット（文字列、秘密にしておく必要がある）
* 設定する項目と設定内容
    + リダイレクト URI ： `https://example.com/oidc/redirect_uri`
        - OIDC 認証を要する URL が https://example.com/oidc/ 以下になるように設定することにして、その中のコンテンツを置かない適当な URL を指定します
    + 利用可能スコープ： `openid`
        - https://github.com/auth0/passport-daccount/blob/v0.1.0/lib/strategy.js#L48 の設定値を参考に OIDC 規格で必要最低限なスコープ openid だけにしておきます

## mod\_auth\_openidc

OIDC 認証をしてくれるモジュールです。

### インストール

Ubuntu 20.04 にはパッケージがあるので、それをインストールします。

```
$ sudo apt install libapache2-mod-auth-openidc
```

これで mod\_auth\_openidc ver. 2.4.1 がインストールされました。

### 設定

mod\_auth\_openidc の設定をしていきます。デフォルトの設定ファイルが `/etc/apache2/mods-available/auth_openidc.conf` にあるので、これを `auth_openidc.conf.org` にバックアップしておき、以下のように設定を書き換えていきます。

#### OIDCRedirectURI

リダイレクト URI を指定します。d アカウント・コネクトへ設定した内容と同じものにします。

```
OIDCRedirectURI https://example.com/oidc/redirect_uri
```

#### OIDCCryptoPassphrase

mod\_auth\_openidc が内部で使用する暗号化のパスフレーズを設定します。何かランダムな文字列を生成して設定するとよいでしょう。以下の例では文字列 `foobarbaz` を設定しています。

```
OIDCCryptoPassphrase foobarbaz
```

#### OIDCProviderIssuer

Provider の issuer identifier を設定します。 https://github.com/auth0/passport-daccount の設定値を参考に手動で ID トークンを取得したところ iss クレームに `https://conf.uw.docomo.ne.jp/` が設定されていましたのでこれを設定します。

```
OIDCProviderIssuer https://conf.uw.docomo.ne.jp/
```

#### OIDCProviderAuthorizationEndpoint

Authorization endpoint を設定します。 https://github.com/auth0/passport-daccount/blob/v0.1.0/lib/strategy.js#L46 を元に以下の設定にします。

```
OIDCProviderAuthorizationEndpoint https://id.smt.docomo.ne.jp/cgi8/oidc/authorize
```

#### OIDCProviderTokenEndpoint

Token endpoint を設定します。 https://github.com/auth0/passport-daccount/blob/v0.1.0/lib/strategy.js#L47 を元に以下の設定にします。

```
OIDCProviderTokenEndpoint https://conf.uw.docomo.ne.jp/common/token
```

#### OIDCProviderTokenEndpointAuth

Token endpoint へアクセスするときの認証方式です。 https://github.com/auth0/passport-daccount/blob/v0.1.0/lib/strategy.js#L53 の記述内容から client\_secret\_basic 方式であることがわかるので、これを設定します。

```
OIDCProviderTokenEndpointAuth client_secret_basic
```

#### OIDCScope

スコープを設定します。d アカウント・コネクトへ設定した内容と同じものにします。

```
OIDCScope "openid"
```

#### OIDCResponseType

フローを設定します。 https://github.com/auth0/passport-daccount/blob/v0.1.0/test/strategy.test.js#L49 に `response_type=code` とあるのでこれを設定します。

```
OIDCResponseType "code"
```

#### OIDCClientID

クライアント ID を設定します。d アカウント・コネクトから払い出された文字列をそのまま設定します。

```
OIDCClientID xxxxxxxxxxxxxxxx
```

#### OIDCClientSecret

クライアントシークレットを設定します。d アカウント・コネクトから払い出された文字列をそのまま設定します。

```
OIDCClientSecret XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

#### OIDCCookiePath

mod\_auth\_openidc が使用する Cookie のパスを設定します。今回は OIDC 認証を要する URL を https://example.com/oidc/ 以下にするため `/oidc/` を設定します。

```
OIDCCookiePath /oidc/
```

#### 差分

以上により設定ファイルのバックアップとの差分は以下のようになりました。

```auth_openidc.conf.diff
--- auth_openidc.conf.org
+++ auth_openidc.conf
@@ -10,14 +10,14 @@
 # but it must NOT point to any actual content that needs to be served.
 # You can use a relative URL like /protected/redirect_uri if you want to
 # support multiple vhosts that belong to the same security domain in a dynamic way
-#OIDCRedirectURI https://www.example.com/protected/redirect_uri
+OIDCRedirectURI https://example.com/oidc/redirect_uri
 
 # (Mandatory)
 # Set a password for crypto purposes, this is used for:
 # - encryption of the (temporary) state cookie
 # - encryption of cache entries, that may include the session cookie, see: OIDCCacheEncrypt and OIDCSessionType
 # Note that an encrypted cache mechanism can be shared between servers if they use the same OIDCCryptoPassphrase
-#OIDCCryptoPassphrase <passphrase>
+OIDCCryptoPassphrase foobarbaz
 
 #
 # All other entries below this are optional though some may be required in a
@@ -49,11 +49,11 @@
 
 # OpenID Connect Provider issuer identifier (e.g. https://localhost:9031 or https://accounts.google.com)
 # Used when OIDCProviderMetadataURL is not defined or the metadata obtained from that URL does not set it.
-#OIDCProviderIssuer <issuer>
+OIDCProviderIssuer https://conf.uw.docomo.ne.jp/
 
 # OpenID Connect Provider Authorization Endpoint URL (e.g. https://localhost:9031/as/authorization.oauth2)
 # Used when OIDCProviderMetadataURL is not defined or the metadata obtained from that URL does not set it.
-#OIDCProviderAuthorizationEndpoint <authorization_endpoint>
+OIDCProviderAuthorizationEndpoint https://id.smt.docomo.ne.jp/cgi8/oidc/authorize
 
 # OpenID Connect Provider JWKS URL (e.g. https://localhost:9031/pf/JWKS)
 # i.e. the URL on which the signing keys for this OP are hosted, in JWK formatting
@@ -62,14 +62,14 @@
 
 # OpenID Connect Provider Token Endpoint URL (e.g. https://localhost:9031/as/token.oauth2)
 # Used when OIDCProviderMetadataURL is not defined or the metadata obtained from that URL does not set it.
-#OIDCProviderTokenEndpoint <token_endpoint>
+OIDCProviderTokenEndpoint https://conf.uw.docomo.ne.jp/common/token
 
 # Authentication method for the OpenID Connect Provider Token Endpoint.
 # When "private_key_jwt" is used, OIDCPrivateKeyFiles and OIDCPublicKeyFiles must have been set.
 # When not defined the default method from the specification is used, i.e. "client_secret_basic".
 # Used when OIDCProviderMetadataURL is not defined or the metadata obtained from that URL does not set it.
 # NB: this can be overridden for dynamic client registration on a per-OP basis in the .conf file using the key: token_endpoint_auth
-#OIDCProviderTokenEndpointAuth [ client_secret_basic | client_secret_post | client_secret_jwt | private_key_jwt | none ]
+OIDCProviderTokenEndpointAuth client_secret_basic
 
 # Extra parameters that need to be passed in the POST request to the Token Endpoint.
 # Parameter names and values need to be provided in URL-encoded form.
@@ -80,7 +80,7 @@
 # OpenID Connect Provider UserInfo Endpoint URL (e.g. https://localhost:9031/idp/userinfo.openid)
 # When not defined no claims will be resolved from such endpoint.
 # Used when OIDCProviderMetadataURL is not defined or the metadata obtained from that URL does not set it.
-#OIDCProviderUserInfoEndpoint <user_info_endpoint>
+OIDCProviderUserInfoEndpoint https://conf.uw.docomo.ne.jp/common/userinfo
 
 # OpenID OP Check Session iFrame URL, for Session Management purposes.
 # When not defined, no Session Management will be applied.
@@ -113,7 +113,7 @@
 # When not defined, the bare minimal scope "openid" is used.
 # NB: multiple scope values must be enclosed in a single pair of double quotes 
 # NB: this can be overridden on a per-OP basis in the .conf file using the key: scope
-#OIDCScope "<scope(s)-separated-by-spaces-and-enclosed-in-double-quotes>"
+OIDCScope "openid"
 
 # Extra parameters that will be sent along with the Authorization Request.
 # These must be URL-query-encoded as in: "display=popup&prompt=consent" or
@@ -192,7 +192,7 @@
 # The response type (or OpenID Connect Flow) used (this serves as default value for discovered OPs too)
 # When not defined the "code" response type is used.
 # NB: this can be overridden on a per-OP basis in the .conf file using the key: response_type
-#OIDCResponseType ["code"|"id_token"|"id_token token"|"code id_token"|"code token"|"code id_token token"]
+OIDCResponseType "code"
 
 # The response mode used (this serves as default value for discovered OPs too)
 # When not defined the default response mode for the requested flow (OIDCResponseType) is used.
@@ -201,12 +201,12 @@
 
 # Only used for a single static provider has been configured, see below in OpenID Connect Provider.
 # Client identifier used in calls to the statically configured OpenID Connect Provider.
-#OIDCClientID <client_id>
+OIDCClientID xxxxxxxxxxxxxxxx
 
 # Only used for a single static provider has been configured, see below in OpenID Connect Provider.
 # Client secret used in calls to the statically configured OpenID Connect Provider.
 # (not used/required in the Implicit Client Profile, i.e. when OIDCResponseType is "id_token")
-#OIDCClientSecret <client_secret>
+OIDCClientSecret XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
 # Filename with the PEM-formatted client certificate used to authenticate the Client in calls to the
 # token endpoint of the OAuth 2.0 Authorization server.
@@ -449,7 +449,7 @@
 
 # Define the cookie path for the "state" and "session" cookies.
 # When not defined the default is a server-wide "/".
-#OIDCCookiePath <cookie-path>
+OIDCCookiePath /oidc/
 
 # Specify the domain for which the "state" and "session" cookies will be set.
 # This must match the OIDCRedirectURI and the URL on which you host your protected
```

### 有効化

mod\_auth\_openidc を有効化（`/etc/apache2/mods-enabled` にシンボリックリンクを作る）します。

```
$ sudo a2enmod auth_openidc
```

## Apache 設定

https://example.com/oidc/ 以下で OIDC 認証が必要になるよう設定します。

### http

`/etc/apache2/sites-available/001-example.com.conf` の `</VirtualHost>` 行の前に以下の設定を追加します。これは http を http**s** へリダイレクトする、つまり http://example.com/oidc/ 以下へのアクセスがあった場合に https://example.com/oidc/ 以下へリダイレクトする設定です。

```
	<Location /oidc/>
		RewriteEngine on
		RewriteCond %{HTTPS} off
		RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
	</Location>
```

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

```001-example.com.conf
<VirtualHost *:80>
	ServerName example.com

	ServerAdmin webmaster@example.com
	DocumentRoot /var/www/example.com

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

	<Location /oidc/>
		RewriteEngine on
		RewriteCond %{HTTPS} off
		RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
	</Location>
</VirtualHost>
```

### https

`etc/apache2/sites-available/example.com-ssl.conf` の `</VirtualHost>` 行の前に以下の設定を追加します。

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

さらに動作確認用に環境変数を見たいため SSI を使えるようにします（OIDC 認証したいだけであれば不要な設定です）。

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

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

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

```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

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

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

### リダイレクト有効化

https へのリダイレクトができるように mod_rewrite を有効化（`/etc/apache2/mods-enabled` にシンボリックリンクを作る）します。

```
$ sudo a2enmod rewrite
```

### SSI 有効化

SSI を有効化（`/etc/apache2/mods-enabled` にシンボリックリンクを作る）します。

```
$ sudo a2enmod include
```

### コンテンツ

以下の OIDC 動作確認用コンテンツを `/var/www/example.com/oidc/index.shtml` に置きます。

```index.shtml
<DOCTYPE! html>
  <html lang="ja">
    <head>
      <meta charset="UTF-8">
      <title>OpenID Connect ログインしました</title>
    </head>
    <body>
      <h1>OpenID Connect ログインしました</h1>
      <p>REMOTE_USER=<!--#echo var="REMOTE_USER" --></p>
    </body>
  </html>
```

トップページから OIDC 動作確認用コンテンツへのリンクを貼るため、`/var/www/example.com/index.html` を以下のようにします。

```index.html
<DOCTYPE! html>
  <html lang="ja">
    <head>
      <meta charset="UTF-8">
      <title>example.com テストページ</title>
    </head>
    <body>
      <h1>example.com テストページ</h1>
      <p><a href="/oidc/">ログイン</a></p>
    </body>
  </html>
```

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

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

```
$ sudo apache2ctl configtest
Syntax OK
```

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

```
$ sudo service apache2 restart
```

として再起動します。

## 動作確認

Windows のブラウザ（Edge など）で https://example.com を開き、「ログイン」をクリックします。d アカウント・コネクトの認証画面になり、認証されたら「遷移先 URL の確認」画面が出て、遷移先 URL として `https://example.com/oidc/redirect_uri` が示されます。「OK」ボタンを押すと、さきほど設置した OIDC 動作確認用コンテンツ「OpenID Connect ログインしました」画面（ログイン中画面）になります。ここでは mod\_auth\_openidc が環境変数 REMOTE_USER に設定した認証したユーザの識別子が表示されます。

この識別子はアカウントを一意に識別することができる文字列ではあるようですが、d アカウントの ID や設定されたメールアドレスとは全く関係ない文字列になっています。

## ログアウト

mod\_auth\_openidc はログアウトの処理をすることもできます。あくまでも mod\_auth\_openidc のセッションをログアウトする処理であって OIDC のログアウトとは異なります。この機能でログアウトできるようにしてみます。

### mod\_auth\_openidc 設定

`/etc/apache2/mods-available/auth_openidc.conf` へログアウトした時に表示される URL を指定します。ここでは https://example.com/loggedout.html に設定してみます。

```
OIDCDefaultLoggedOutURL https://example.com/loggedout.html
```

### コンテンツ

ログアウトした時に表示される以下のようなコンテンツを `/var/www/example.com/loggedout.html` に置きます。

```loggedout.html
<DOCTYPE! html>
  <html lang="ja">
    <head>
      <meta charset="UTF-8"/>
      <title>OpenID Connect ログアウトしました</title>
    </head>
    <body>
      <h1>OpenID Connect ログアウトしました</h1>
      <p><a href="/">トップに戻る</a></p>
    </body>
  </html>
```

ログイン中の画面 `/var/www/example.com/oidc/index.shtml` にログアウトのリンクを追加します。

```index.shtml
<DOCTYPE! html>
  <html lang="ja">
    <head>
      <meta charset="UTF-8">
      <title>OpenID Connect ログインしました</title>
    </head>
    <body>
      <h1>OpenID Connect ログインしました</h1>
      <p>REMOTE_USER=<!--#echo var="REMOTE_USER" --></p>
      <p><a href="/oidc/redirect_uri?session=logout">ログアウト</a></p>
    </body>
  </html>
```

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

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

```
$ sudo apache2ctl configtest
$ sudo service apache2 restart
```

### 動作確認

ログイン中の画面から「ログアウト」のリンクをクリックするとログアウトすることができ、再度ログインしようとすると OIDC の認証が走るようになります。

## 設定の改良

ここまで、mod\_auth\_openidc のデフォルト設定ファイル `/etc/apache2/mods-available/auth_openidc.conf` に設定していましたが、これだとすべての virtual host に共通の設定になってしまいます。そこで virtual host 別の設定になるようにしてみます。

### デフォルト設定ファイルを元に戻す

バックアップしてあった元のファイルで上書きします。元のファイルはすべてコメント行か空行なので設定としては完全に空になります。

```
$ cd /etc/apache2/mods-available/
$ sudo cp auth_openidc.conf.org auth_openidc.conf
```

### d アカウント・コネクト設定

上記 mod\_auth\_openidc 設定のうち、d アカウント・コネクトに特有の設定値を `/etc/apache2/mods-available/auth_openidc_d_account_connect.conf` に書きます。

```auth_openidc_d_account_connect.conf
OIDCProviderIssuer https://conf.uw.docomo.ne.jp/
OIDCProviderAuthorizationEndpoint https://id.smt.docomo.ne.jp/cgi8/oidc/authorize
OIDCProviderTokenEndpoint https://conf.uw.docomo.ne.jp/common/token
OIDCProviderTokenEndpointAuth client_secret_basic
OIDCProviderUserInfoEndpoint https://conf.uw.docomo.ne.jp/common/userinfo
OIDCScope "openid"
OIDCResponseType "code"
```

### クライアント ID およびクライアントシークレット

クライアント ID およびクライアントシークレットは別ファイル `/etc/apache2/mods-available/auth_openidc_client_secret.conf` に書いておきます。

```auth_openidc_client_secret.conf
OIDCClientID xxxxxxxxxxxxxxxx
OIDCClientSecret XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
```

念のため、オーナーとパーミッションをいじって他のユーザから見えないようにします。

```
$ cd /etc/apache2/mods-available/
$ sudo chown www-data:www-data auth_openidc_client_secret.conf
$ sudo chmod 600 auth_openidc_client_secret.conf
```

### サイト設定

d アカウント・コネクト設定とクライアント ID およびクライアントシークレット設定をインクルードするとともに、サイト特有の設定をするため、`etc/apache2/sites-available/example.com-ssl.conf` の `<Location /oidc/>` の前に以下の行を追加します（mod\_auth\_openidc の設定は 1 virtual host あたり 1 つだけのようで `<Location /oidc/>` ～ `</Location>` の中に書くとエラーになってしまいました）。

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

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

```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_d_account_connect.conf
		Include mods-available/auth_openidc_client_secret.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
$ sudo service apache2 restart
```

### 動作確認

特に問題なくログイン・ログアウトができました。

## 次回

本記事「2. d アカウント・コネクト編」では、d アカウント・コネクトでソーシャルログインできるようにしてみました。次の記事は d アカウント・コネクトではなくて Google アカウントでソーシャルログインできるようにしてみる「[3. Google アカウント編](https://qiita.com/trueroad/items/593870ebafb675196e88)」です。さらにその次にはログイン時に d アカウント・コネクトか Google アカウントかを選択できるようにする「4. マルチプロバイダ編」を予定しています。