Authleteを使った認可サーバの構築と、OAuthクライアント(Webアプリケーション)からの疎通




OAuthのサーバを素早く構築するために、クラウドサービスのAuthleteを利用した際のメモ。

[Authlete](https://www.authlete.com/)とは、OAuth2.0 serverとOpenID provider を構築するためのクラウドサービスです。
OAuth2.0やOpenID Connectに対応したサーバをホンキで構築するにはそれなりにメンドクサイわけですが、そのメンドクサイ処理をAuthleteに委譲することで、簡単に認可サーバを構築することが出来るというモノです。


## 前提事項
記事の簡略化のため、OAuth/OpenID Connect の知識をある程度持ってる前提で話を進めます。Authorization Code Grant Flow の処理シーケンスをだいたい知ってるとか。

## 環境
Mac OS X 10.12.6 (macOS Sierra)
Apache Maven 3.2.3
Java version: 1.8.0_25
git version 2.11.0 (Apple Git-81)

でやってますが、mavenとgitが動けば、OS含めなんでもOKだと思います


また、各種サーバなど登場人物は以下の通り:

|サーバ名|用途|URL|
|:-----------|:------------|:------------|
|java-oauth-server|ユーザIDごとの認可情報を管理するサーバ。認可サーバと呼ぶ|http://oauth.example.com:8080/|
|java-resource-server|ユーザIDごとのデータや機能をもったサーバ。リソースサーバと呼ぶ|http://resource.example.com:8081/|
|java-oauth-client|リソースサーバのリソースを使用する、Webアプリケーション|http://client.example.com:8082/|


今回の環境では各サーバはローカルに立ち上げようとしているので、適宜 /etc/hosts などで 127.0.0.1 に名前解決しておいてください。もちろんAWSのEC2など、任意の場所にあげていただいても結構です。

## おおまかな流れ
おおまかな流れは以下の通りです。

1. Authleteへのサインアップ
2. 認可サーバの登録・クライアントの登録
3. 認可サーバ、リソースサーバの構築
4. Webアプリケーションからの疎通


## Authlete管理画面での操作
###サインアップと 認可サーバ情報の登録・作成
Authleteを用いるためにまずはサインアップします。
https://www.authlete.com/ の右肩よりSign Upします。

![01.png](https://qiita-image-store.s3.amazonaws.com/0/73777/d848c5bc-e88b-8555-9778-2bdfb75e7027.png "01.png")


つぎの画面で、下記のように ID/password/アドレス などをいれればOKです。

![02.png](https://qiita-image-store.s3.amazonaws.com/0/73777/09ec6fff-fe88-5e85-b67f-6145362114ad.png "02.png")


サインアップが完了するとトップページが表示されます。

![03.png](https://qiita-image-store.s3.amazonaws.com/0/73777/2e783b47-6607-43fe-25ff-4c801c6d9c64.png "03.png")


アカウントが作成され、一つの認可サーバ(Service 385xxxxxxxx)がすでに作成済みになっているようです。



APIキーとAPIシークレットが表示されていますが、あとで認可サーバやリソースサーバを構築する時に使用しますので、

| API キー |API シークレット|
|:-----------|:------------|
| 385xxxxxxxx |NeE89hxxxxxxxxxxxxxxxxxxx|



はメモしておきましょう。

ちなみに次回以降のログインは
https://so.authlete.com/
ココからID/passwordでログインします。



###クライアントの登録・作成
うえのキャプチャにある「クライアントアプリ開発者コンソール」のURLにアクセスすると、上のとは別の、クライアント開発者向けログイン画面が表示されます。こちらはOAuthクライアントの管理画面で、上記のAPIキー(385xxxxxxxx)、API シークレット(NeE89hxxxxxxxxxxxxxxxxxxx)でログインすることが出来ます。


![04.png](https://qiita-image-store.s3.amazonaws.com/0/73777/605bcce2-f917-b70e-a5bb-55cc039d5e33.png "04.png")

ログインすると、上記の通りクライアント名(Client 4455xxxx)というのが表示され、そのクライアントのクライアントIDとクライアントシークレットが表示されています。これらもあとで OAuthクライアントつまりWebアプリケーションを構築するときに使いますので、メモしておきましょう。


| クライアント ID |クライアントシークレット|
|:-----------|:------------|
| 4455xxxx | FZlETE9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |


## 整理
さて、ここまでで一旦整理すると、

* Authleteへのログインアカウント
* Authleteで認可サーバ・リソースサーバを立ち上げるための「API キー」「APIシークレット」
* 上記認可サーバに登録されたOAuthクライアント(Webアプリケーション)を構築するための「クライアントID」「クライアントシークレット」

が払い出されたことになります。Authleteはアカウントを作ると認可サーバ(Service 385xxxxxxxx)が一つ作られ、それに紐付くクライアント(Client 4455xxxx)も一つ、自動で作成されるようです。


## 認可サーバとリソースサーバの構築

つづいて、OAuthクライアントがユーザ情報にアクセスする際の認可を行う認可サーバと、OAuthクライアントからのリクエストを受けて権限に応じた処理を実行するリソースサーバを構築します。

今回の構築では、Authleteさん提供の下記のコードを使ってみます。

* 認可サーバ: [java-oauth-server](https://github.com/authlete/java-oauth-server)
* リソースサーバ: [java-resource-server](https://github.com/authlete/java-resource-server)

構築といっても、上記のコードをGitHubから落としてきて、サインアップしたときに払い出された API キー、API シークレット を設定ファイルに書き込むだけです。

### 認可サーバの構築

認可サーバを構築します。先ほどのリポジトリをcloneしてきて、設定ファイル(authlete.properties)にAPI キー、API シークレットを設定すればOKです。

```bash
$ git clone https://github.com/authlete/java-oauth-server.git
$ cd java-oauth-server
$ cat authlete.properties
service.api_key = 385xxxxxxxx   ← 正しい API キー に変更してください
service.api_secret = NeE89hxxxxxxxxxxxxxxxxxxx   ← 正しい API シークレット に変更してください
$ mvn clean jetty:run
```


### リソースサーバの構築

つづいてリソースサーバを構築します。先ほどのリポジトリをcloneしてきて、設定ファイル(authlete.properties)にAPI キー、API シークレットを設定すればOKです。認可サーバとおなじ作業ですね。


```bash
$ git clone https://github.com/authlete/java-resource-server.git
$ cd java-resource-server/
$ cat authlete.properties
service.api_key = 385xxxxxxxx   ← 正しい API キー に変更してください
service.api_secret = NeE89hxxxxxxxxxxxxxxxxxxx   ← 正しい API シークレット に変更してください
$ mvn clean jetty:run
```

ちなみにリソースサーバは、

```bash
curl http://resource.example.com:8081/api/country/JP -H "Authorization: Bearer xxxxxxxxxxx" -G
```

のように Authorization にアクセストークンをつけてリクエストすると、アクセストークンが正しければ

```json
{
  "name": "Japan",
  "alpha2": "JP",
  "alpha3": "JPN",
  "numeric": 392,
  "currency": "JPY"
}
```

のようなレスポンスが返るように作られています。

以上で、認可サーバとリソースサーバの構築は完了です。


## Webアプリケーションからの疎通

つぎにWebアプリケーションからの疎通を行います。WebアプリケーションはURLが
http://client.example.com:8082/RedirectServlet
であるとします。認可コード取得後のリダイレクト先も、おなじサーブレット http://client.example.com:8082/RedirectServlet にしてみます。

ちなみに Authorization Code Grant Flow の処理シーケンスはだいたい以下の通りとなります。Authleteは認可サーバからお願いされて認可コードを発行したり、その認可コードをもとにアクセストークンを発行したり、リソースサーバがWebアプリケーションからアクセストークンを受け取った際に、そのアクセストークンが正しいものかを教えてくれたり、誰に紐付いているトークンかなどを教えてくれたりします。

![sequence.png](https://qiita-image-store.s3.amazonaws.com/0/73777/ee9042d0-a1a5-5555-abd5-434657c66a1b.png "sequence.png")

### リダイレクト先の登録
AuthleteにこのWebアプリケーションのリダイレクト先URIを登録します。「クライアントアプリ開発者コンソール」を開いて、一番下の編集ボタンをクリックします。

![05.png](https://qiita-image-store.s3.amazonaws.com/0/73777/a44619c3-53de-55fb-f7a2-5bb440533aba.png "05.png")


そのあと、認可タブに リダイレクトURI という設定があるので「 http://client.example.com:8082/RedirectServlet 」と記載して、更新ボタンをクリックすれば完了です。

![06.png](https://qiita-image-store.s3.amazonaws.com/0/73777/ca31a1a0-39e0-33e2-3a7f-376a2b74039f.png "06.png")

登録されました。


### Webアプリケーションの構築

Webアプリケーションは上記のシーケンス図で示したとおり、

0. Web画面で○○でログイン、などのリンクをクリック(そのリンク先がRidirectServletになります)。
1. 認可サーバの画面表示(サーブレットから302でリダイレクト)
2. ユーザが認可サーバ上で認証・認可オペを実行
3. 認証認可オペのあと認可コードが生成され、認可サーバは指定されたURIへリダイレクト(もとのサーブレットに302でリダイレクトされて戻ってくる)
4. 戻ってきたら、認可コードともに認可サーバにアクセスして、アクセストークンを取得する
5. アクセストークンをつかって、リソースサーバへAPI呼び出し(データ取得の問い合わせとか)
6. 結果を取得

なんていう処理フローになるのですが、そんなサーブレットを作成しましたので、そのままそのプロジェクトを利用します。

* Webアプリケーション: [java-oauth-client](https://github.com/masatomix/java-oauth-client)

を落としてきたあと、設定ファイル(settings1.properties)のclient_id, client_secret を値を下記の通り クライアント ID、クライアントシークレットに変更してください。


```bash
$ git clone https://github.com/masatomix/java-oauth-client.git
$ cd java-oauth-client/
$ git checkout -t origin/feature/forQiita
$ cat src/main/resources/settings1.properties
client_id = 4455xxxx    ← 正しい クライアント ID に変更してください
client_secret = FZlETE9xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx    ←正しい クライアントシークレット に変更してください
$ mvn clean jetty:run
```


### 疎通してみる。

http://client.example.com:8082/ にアクセスすると、「AuthleteをもちいたOAuthサーバで認可処理。」というリンクが表示されますのでクリックします。

![07.png](https://qiita-image-store.s3.amazonaws.com/0/73777/952f648a-37c0-30e4-0dbe-9fa4858f596a.png "07.png")

そうすると認可サーバは下記のような「このWebアプリがあなたの情報に対して、アクセスしたいと言ってるけど認可する？」をだしてくるので、ユーザは考えたのち「私はだれ」を表すLogin IDとPasswordを入力し、Authorizeを押してアクセスを認可します。

![08.png](https://qiita-image-store.s3.amazonaws.com/0/73777/90304a90-97b9-5f70-5163-2caa39831cbb.png "08.png")


Authleteさん提供の認可サーバ・リソースサーバはダミーのユーザ認証がコーディングされているので

| Login ID | Password |
|:-----------|:------------|
| john | john |

というアカウントで認証・認可を行ってください。


Webアプリケーションが認可をされると、Webアプリケーションに対して認可コードが発行され、もとのサーブレットに302でリダイレクトされて戻ってきます。
さらにサーブレットは認可サーバにアクセストークンの取得をお願いし、認可サーバからアクセストークンを取得します。
認可サーバからアクセストークンを取得したら、サーブレットはHTTPヘッダに

```
Authorization: Bearer uU3Xml7rdxxxxxxxxx ←アクセストークン
```

をつけて、リソースサーバにデータ取得を依頼します。
リソースサーバはAuthleteにそのトークンの正当性を確認してもらったあと、ロジックを動かしてサーブレットにデータを返します。

これらの一連の処理が行われ、最終的に画面にデータが返ってくればＯＫです。

![09.png](https://qiita-image-store.s3.amazonaws.com/0/73777/6b8a2e03-e3d7-ac14-b114-9a5cd551ce00.png "09.png")



お疲れ様でした。

サーブレット内の処理では

* トークンを取得する token_endpoint のAPIから、アクセストークンとともに返却されるid_token (いわゆるOpenID Connectのid_token)の表示
* その id_token をJWTの仕様で Header、Payload(本体)、Signature(署名)に分割して、Base64デコードして表示
* JWTのヘッダで署名方式を確認して、署名を検証することで改ざんが行われていないことをチェック。(デフォルトではAuthleteは クライアントシークレットで HS256で署名してるぽいので、それで検証している)
* RS256で署名してる場合など必要に応じて、JWK(JSON Web Key)が公開されてるエンドポイント(jwks_uri)へアクセスし公開鍵を取得して、公開鍵で署名検証する
* OpenID Connectの仕様に基づくuserinfo_endpoint へのデータ取得

などなどいろいろ書いてますが、興味があればコードをご参照ください。

ちなみに、実際の本番構築時には、CSRF対策やらなんやらセキュリティ関連の検討事項がいろいろとありますので、ご注意ください。



##関連リンク

* [OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る](http://qiita.com/TakahikoKawasaki/items/f2a0d25a4f05790b3baa#_reference-b8f91e616553e7f6ec06) OAuthとOpenID Connectについてかなり細かく説明されています。かなりスッキリしてきました。ありがとうございます。
* [【第二弾】OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る](http://qiita.com/TakahikoKawasaki/items/30fbd546935cea914e4f) Introspection Endpointの仕様について細かな説明が参考になりました。
* [Authlete を使って超高速で OAuth 2.0 & Web API サーバーを立てる](http://qiita.com/hidebike712/items/ede39abf0c860b3b96e5) 前半はほとんどこのサイトの記述を参考にさせていただきました！ありがとうございます！
* [Authleteを使った認可サーバの構築手順(CIBA対応版)](https://qiita.com/masatomix/items/9598b55c2658fb963120) CIBA対応したAuthleteとの疎通


* 認可サーバ: [java-oauth-server](https://github.com/authlete/java-oauth-server)
* リソースサーバ: [java-resource-server](https://github.com/authlete/java-resource-server)
* Webアプリケーション: [java-oauth-client](https://github.com/masatomix/java-oauth-client/tree/feature/forQiita)