response_mode=web_messageを調べた

# 概要

response_mode=web_message がわからなかったので、さくっとわかる範囲で調査をしてみた。ざっくりまとめると以下の理解。

* SPA(Single Page Application) で prompt=none を使ったセッション確認が画面遷移を発生させずに行える
* 通常だと /authorize のレスポンスでリダイレクトが発生してしまうが、それだと SPA の利点が損なわれてしまう
* response_mode=web_message をつけることで、リダイレクトを発生させずに認可コードを取得することができる

この資料がとてもわかりやすかった。私のざっくりした説明より詳細が書いてあるので、もう少し理解したい人はこれを読もう。

|  OAuth 2.0 Web Messaging Response Mode - OpenID Summit Tokyo 2015   |
| ---- |
|  [![](https://image.slidesharecdn.com/openidsummit2015wmrm-151110080658-lva1-app6891/95/slide-1-1024.jpg)](https://www.slideshare.net/zigorou/oauth-20-web-messaging-response-mode-openid-summit-tokyo-2015)  |

# そもそも response_mode とは何か？

OAuth 2.0 では /authorize で response_mode をクエリパラメータに指定することで、どのように認可コード、トークンを受け取るか指示ができる。この記事ではわかりやすく、 response_type=code で認可コードを受け取る場合のみの話題に限定する。

|  response_mode  |  概要  |
| ---- | ---- |
|  query  |  クエリパラメータで受け取る。<br>例: `example.com?code=1234`  |
|  fragment  |  フラグメントで受け取る。<br>例: `example.com#code=1234`  |
|  form_post  |  値がフォームパラメータでpostされる形式で受け取る。レスポンスはhtmlで、htmlに書かれたJavaScriptからpostされる。  |

ちなみに、 response_mode=form_post はこちらの記事がとてもわかりやすい。

* [OAuth 2.0 Form Post Response Mode - マイクロソフト系技術情報 Wiki](https://techinfoofmicrosofttech.osscons.jp/index.php?OAuth%202.0%20Form%20Post%20Response%20Mode)

# Auth0 ドキュメントで調べる

私が今の仕事でよく Auth0 を触ることもあって、まずは Auth0 のドキュメントを確認してみる。

### Silent Authentication

Auth0 では web_message の説明は以下。
https://auth0.com/docs/protocols/oauth2#how-response-mode-works

> `web_message`: This response mode is defined by the OAuth 2.0 Web Message Response Mode specification. It uses HTML5 Web Messaging instead of the redirect for the Authorization Response from the Authorization Endpoint. This is particularly useful when using Silent Authentication.

どうやら Web Messaging API を使って認可コードを渡すことができるらしい。Web Messaging API がわからない人はこちらの記事が良いと思う→ [Web Messaging API を使ってみる - Qiita](https://qiita.com/naoiwata/items/0a31d999b2dcd5098289)


`This is particularly useful when using Silent Authentication`
web_message は **Silent Authentication** で使われるよ、と書いてある。では Silent Authentication とは何なのか？

https://auth0.com/docs/api-auth/tutorials/silent-authentication

> The OpenID Connect protocol supports a `prompt=none` parameter on the authentication request that allows applications to indicate that the authorization server must not display any user interaction (such as authentication, consent or MFA).

なるほど。Auth0 では prompt=none をつけたリクエストのことを Silent Authentication と呼んでいるらしい。

### prompt=none とは？

prompt=none は OpenID Connect 定義されている認可リクエストの方法の１つ。 /authorize に prompt=none をつけてリクエストを行うと、画面表示が行われなくなる。どういう場合に使うかというと、アプリケーションがログイン済みかどうか確認したいときに使う。例えば、すでに認証していて IdP にセッションが残っている場合、画面表示をスキップしてすぐ認可コードをレスポンスしてくれる。 prompt=none がないと画面遷移が発生してしまうので、ユーザに違和感を与えずアプリケーションが裏でこっそりセッションを確認するときに使える機能。

また例を出すが、Auth0 の SPA 用ライブラリ [auth0/auth0-spa-js](https://github.com/auth0/auth0-spa-js)  は Silent Authentication を使っている。初回表示時に prompt=none をつけた /authorize リクエストを裏で実行して、セッションが残っていればアプリケーションを自動的にログイン済み状態にしてくれる。

どうやら response_mode=web_message は prompt=none と一緒に使われるものらしい。

# response_mode=web_message とは？

ありがたいことに、冒頭で紹介した [SlideShareの資料](https://www.slideshare.net/zigorou/oauth-20-web-messaging-response-mode-openid-summit-tokyo-2015) で理解できた。

### 振る舞い

* ユーザに見えない iframe を生成する
* iframe から /authorize へリクエストしてレスポンスも受け取る
* そのレスポンスは Web Messaging API を使った html で構成されていて、親に認可コードを Web Message 経由で渡す（JavaScript で書かれた処理が実行される）

なるほど、そんな認可コードの渡し方があるのか...。毎回 OpenID Connect の知らない仕様を発見すると、よくこんな使い方を思いつくなと感心する。

仕様書のリンクにはわかりやすい図も書いてあった。
[OAuth 2.0 Web Message Response Mode](https://tools.ietf.org/html/draft-sakimura-oauth-wmrm-00)


### メリット

これの何が嬉しいかというと **画面遷移が発生しない** のがポイント。他の response_mode だとレスポンスでリダイレクトが発生してしまうが、 web_message だと発生しない。昨今のアプリケーションは SPA で開発されていることが多く、画面遷移が発生すると SPA で構築しているメリットが薄くなってしまうため、UX を下げずに認可コードをセキュアに受け取る方法が考えられたみたい。

prompt=none と併用されるのは、認証画面を開かないリクエストなので、ユーザに知られず裏でこっそり（バックグラウンド処理）確認するリクエストに適しているからだろう。[auth0/auth0-spa-js](https://github.com/auth0/auth0-spa-js) のソースコードも読んでみて、確かに iframe を使ってリクエストを行っているのが確認できた。[ソースのこの辺り](https://github.com/auth0/auth0-spa-js/blob/v1.8.1/src/Auth0Client.ts#L660)。

```typescript
// prompt=none と response_mode=web_message をセット
const url = this._authorizeUrl({
  ...params,
  prompt: 'none',
  response_mode: 'web_message'
});

// iframe から url を呼び出している
const codeResult = await runIframe(url, this.domainUrl, timeout);
```

### 補足

iframe でなく window.open を使ってポップアップから認証を行うケースも[仕様書](https://tools.ietf.org/html/draft-sakimura-oauth-wmrm-00)で定義されていた。この場合は認証画面（prompt）は表示されるため、上記で書いたユースケース以外で使われる。

# 余談

authorization リクエストのクエリパラメータに auth0Client ってついてるのを発見した。 base64 でエンコードされていたので中身を確認してみたら、トレース用の値が入ってあった。

```
eyJuYW1lIjoiYXV0aDAtc3BhLWpzIiwidmVyc2lvbiI6IjEuMi40In0=
```

```json
{
  "name": "auth0-spa-js",
  "version": "1.2.4"
}
```

どうやらAuth0 SDKで接続した場合に、ライブラリ側で自動的に付与するクエリパラメータの様子。Android のライブラリ実装もソースを読んだが、同じように Telemetry という機能として実装がなされていた。

これでどのバージョンがよく使われているとか収集しているんだろうか。