iPadでWebSocket + Authorizationがつながらない


あれ?つながらない!

発端は、iPadでの開発環境を整備しようと思ったからでした。
VPSに開発環境を入れられたら便利だなと思い、Theiaをインストールして、そのままじゃ誰でも使えてしまうので、Apacheをリバースプロキシにして、Digest認証を入れて、さらにSSLにしてと、構築してきました。ところが、PCのブラウザからは期待通りにつながるのですが、iPadだとつながらない。。。
意味ないじゃありませんか!

PCの場合

TheiaはNode.jsでデフォルトでポート3000で動作します。
Apacheをリバースプロキシとして、https://example.com/theia/を経由してアクセスするよう設定しました。
さらに、TheiaはWebSocketで/theia/services以下にアクセスするようなので、リバースプロキシ設定で/theia/services/へのアクセスは、ws://localhost:3000/services/にアクセスするように設定しています
(詳しくはこちらをご覧ください)。

Apacheのログに主要なリクエストヘッダを出力させた結果が以下になります。
(どのヘッダを出力するかはブラウザの開発者ツールで確認しているのですが、iPadの場合と比較するために、Apacheのログに出力して確認することにしました。)

"GET /theia/services HTTP/1.1" 200
Authorization:"Digest username=\"watashi\", realm=\"DAuth\", nonce=\"xxxxx\", uri=\"/theia/services\", algorithm=MD5, response=\"yyyyy\", qop=auth, nc=0000000c, cnonce=\"zzzzz\"
Sec-WebSocket-Extensions:"permessage-deflate; client_max_window_bits"
Sec-WebSocket-Key:"mfnf6LozGNRKYis+K8NXhQ=="
Sec-WebSocket-Version:"13"
Upgrade:"websocket"

プロトコルが"websocket"にアップグレードされ、Digest認証のためのAuthorizationヘッダもあります(一部編集しています)。

iPadの場合

"GET /theia/services HTTP/1.1" 401
Authorization:"-"
Sec-WebSocket-Extensions:"x-webkit-deflate-frame"
Sec-WebSocket-Key:"3CEhfYMZMFoXy4yWzU+igQ=="
Sec-WebSocket-Version:"13"
Upgrade:"websocket"

同じサイトにアクセスした結果です。プロトコルが"websocket"にアップグレードされていますが、Digest認証のためのAuthorizationヘッダがありません!
ChromeもSafariも同様でした。
Sec-WebSocket-Extensionsの内容が異なりますが、圧縮に関するパラメータなので関係なさそうです。

それならば...Apache Form認証

『ヘッダがないならCookieを食べればいいのに』との言葉どおり、Cookieを使ったログインセッションで、思惑どおりの動作になるか試してみます。Theiaの改造はおろか、何かそのために作るのは面倒なので、Apache Form認証を利用します。

sudo a2enmod hogehogeでモジュールを追加する必要がありましたが、私の場合は以下が必要でした。それまでに入れてあるモジュールにもよると思いますので、Apacheを再起動してエラーが出るようであれば、エラーメッセージを参考にして追加してください。

  • auth_form
  • request
  • session_cookie
  • session_crypt
  • session

サイト構成はこんな感じ。

/             ←ここには何も置かない
|—- auth      ←login.htmlとかを置く
|—- theia     ←ここをForm認証の対象にする

認証の設定はこんな感じ。httpsのときはhttpのところをhttpsに変えましょう。Basic認証と同じ.htpasswdを作って置きます。

000-default.conf


        <Location "/theia">
          AuthType Form
          AuthName "FormAuth"
          AuthUserFile /etc/apache2/.htpasswd
          AuthFormProvider file
          AuthFormLoginRequiredLocation http://%{SERVER_NAME}/auth/login.html
          Session On
          SessionCookieName auth_form path=/theia
          SessionCryptoPassphrase secure
          Require valid-user
        </Location>
        <Location "/dologin.html">
          SetHandler form-login-handler
          AuthFormLoginRequiredLocation http://%{SERVER_NAME}/auth/login.html
          AuthFormLoginSuccessLocation http://%{SERVER_NAME}/theia/
          AuthFormProvider file
          AuthUserFile /etc/apache2/.htpasswd
          AuthType Form
          AuthName "FormAuth"
          Session On
          SessionCookieName auth_form path=/theia
          SessionCryptoPassphrase secure
       </Location>
       <Location /dologout.html>
          SetHandler form-logout-handler
          AuthFormLogoutLocation http://%{SERVER_NAME}/auth/loggedout.html
          Session On
          SessionMaxAge 1
          SessionCookieName auth_form path=/theia
          SessionCryptoPassphrase secure
       </Location>

login.html


<html>
  <body>
      <h1>Login for Theia.</h1>
      <form method="POST" action="/dologin.html">
         Username: <input type="text" name="httpd_username" value="" />
        <br>
        Password: <input type="password" name="httpd_password" value="" />
        <input type="submit" name="login" value="Login" />
     </form>
  </body>
</html>

loggedout.html

<html>
  <body>
    Logged out.
    <br>
    <a href="login.html">Login</a>
  </body>
</html>

これでようやくできました!

けど、logoutが出番なし