JiraのOAuth認可を試してみる

#はじめに
Jiraには外からデータにアクセスするためのAPIが用意されています。最新の仕様は[ここ](https://docs.atlassian.com/software/jira/docs/api/REST/latest)にありますが、通常のGUIを介することなく課題のリストを取得したり、各々の課題の詳細を得たりそれらを改変したりができます。Jiraそれ自体もかなり強力なツールですが、APIを利用することで他のツールと連携させてより便利に利用するという可能性が広がります。

利用されている方はご存知のように、Jiraにはアカウントの概念があり通常はログインを行って利用することになります。そしてアカウントごとに閲覧できるプロジェクトや行える操作を細かく設定することができます。APIを利用する際も同様でまずはユーザ認証を行ってから利用することになります。一番簡単な方法は、HTTPのBasic認証を使ってユーザ名とパスワードを直接APIに渡す方法ですが、WebサービスからAPIを利用する場合にはこれでは不都合があるため、OAuthという方法を使って認証・認可を行います。本稿ではその仕組みと実現方法をステップバイステップで紹介したいと思います。

#OAuthとは
[OAuth](https://oauth.net/)は「認可(Authorization)」を行うための標準仕様です。認証(Authentication)となにが違うのか混乱しがちですが、一言で言うと、認証は「あなたがあなたであることを確認する（本人認証）」で、認可は「他の誰かにあなたの持ち物を使うことを許すこと（権限委譲）」です。

認可に比べて認証はわかりやすく、いわゆる「ログイン」のことです。これまではユーザ名とパスワードによって行うことが多かったですが、最近では指紋や静脈をつかったり、最新のiPhoneでは顔を見せることで本人確認できるようになっています。では認可とはなにか？

その説明のためにまずはじめに通常のJiraのアクセスを考えてみます。GUIであろうがAPIであろうが、ユーザはJiraに対してユーザ名・パスワードを渡してユーザ認証を行いJiraに格納されたデータにアクセスします。ここでは認証はありますが認可は必要ありません。
<img width="351" alt="Screen Shot 2018-12-22 at 14.52.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/30342/9a474844-eb96-dea1-87ef-927c47180b8f.png">
次にJiraのデータにアクセスして加工して表示するWebサービスを考えてみます。通常のアクセスの場合と同じように考えると、Webサービスに対してユーザ名・パスワードを渡し、Webサービスがそれを使ってJiraにアクセスし情報を取ってきて、それを加工して返すという形が考えられます。
<img width="592" alt="Screen Shot 2018-12-22 at 14.52.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/30342/23a97134-0da3-68a0-25ed-cd10d0aaf4cd.png">
ただこのやり方には問題があります。というのも、Webサービスに一度ユーザ名とパスワードを渡してしまうとそのWebサービスはいつでもどこでもそのアカウントの情報を取り放題ということになります。さらにパスワードを変えることすらでき、アカウントを乗っ取ってしまうこともできます。ほとんどのWebサービスがそんな悪意をもったサービスではないと思いますが、パスワードを第三者に渡すというのはやはり気持ち悪い。そこでOAuthと認可の考えが登場します。

OAuthではパスワードを渡すのはあくまでもデータを持っている側のサービスでこの場合はJiraになります。Jiraはユーザ認証したのちに「このサービスにデータを渡すけど良い？」と確認した後にトークンを発行します。これが認可の作業。Webサービスはそのトークンを使ってデータにアクセスできるというわけです。トークンには有効期限があるのでずっとアクセス権を与えっぱなしということにはならないですし、さらに必要であればいつでもJiraの側からそのトークンの無効化もできます。
<img width="434" alt="Screen Shot 2018-12-22 at 14.52.32.png" src="https://qiita-image-store.s3.amazonaws.com/0/30342/862a8e16-b79f-721e-21ce-d447cd500c26.png">


なお、OAuthには1.0(a)と2.0の二つのバージョンがあります。基本的な認可の考え方は同じですが、技術的なアプローチが異なり互換性がありません。私はこれまで2.0しか扱ったことがなかったのですが、JiraのAPIは1.0aで実装されていて今回初めて使ってみました。

#OAuthを使うための準備
JiraでOAuthを使うにはいくつかの準備が必要になります。

1. 非対称鍵ペアの作成
2. Jiraの設定変更

## 非対称鍵ペアの作成
お使いのシステムで`openssl`がインストールされていると仮定し、以下のコマンドで生成できます。一つ目でRSAの鍵ペアを生成し、二つ目で公開鍵の方を別ファイルに書き出しています。

```shell
$ openssl genrsa -out jira.pem 1024
$ openssl rsa -in jira.pem -pubout -out jira.pub
```
## Jiraの設定変更

設定変更にはJiraのAdmin権限が必要です。そして次のようにしてアプリケーションリンクを作成します。

1. Jiraの設定　→ アプリケーション　→ アプリケーションのリンクを開く
<img width="1359" alt="Screen Shot 2018-12-22 at 12.07.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/30342/22eb8807-32b7-850e-cf90-d0507f6c1bce.png">
2. リンクするアプリケーションのURLに何か入力し（何でもOK）、「新しいリンクを作成」をクリック。サーバから応答がないと言われるが無視してそのまま「続行」
3. アプリケーション名を入れて「続行」
<img width="1359" alt="Screen Shot 2018-12-22 at 12.07.31.png" src="https://qiita-image-store.s3.amazonaws.com/0/30342/581b0069-fc07-eb72-b92e-db63b2b6f66a.png">
4. できたリンクのエントリの鉛筆アイコンをクリックする
<img width="1359" alt="Screen Shot 2018-12-22 at 12.07.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/30342/eb2d4573-2ec2-ea35-1fa8-d705702e4c87.png">
5. 「受信認証」でコンシューマーキー（なんでも良いですが後で使うので覚えておく）、コンシューマー名、パブリックキーを入力。パブリックキーには上のステップで作った`jira.pub`の`-----BEGIN PUBLIC KEY-----`と`-----END PUBLIC KEY-----`に挟まれた部分をコピペします。最後に一番下にある「保存」をクリック。そうしないと設定が登録されないのでご注意を。
<img width="1359" alt="Screen Shot 2018-12-22 at 12.09.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/30342/6889119e-7008-6093-14ce-f63df7c71c5e.png">

これで準備は完了です。

#JiraのOAuth認可 ステップバイステップ
今回、OAuthの挙動を確認するためにサンプルのWebアプリケーション(qatlassian)をPython+Flaskで作ってみました。それを用いてどのようなステップで認証・認可が進むのかを説明したいと思います。なお、アプリのソースコードは[こちら](https://github.com/ksato9700/qatlassian)にあります。

基本的な流れは以下の通り。

1. qatlassianがJiraからリクエストトークンの取得し、認証・認可のURLを作ってブラウザに返す
2. ブラウザは返されたURLにリダイレクトされ（リダイレクト先はJira）、そこでユーザ名・パスワードを入力。qatlassianにデータを渡して良いかの確認（認可）をおこなう。
3. ブラウザは再びqatlassianにリダイレクトされ、qatlassianはアクセストークンをJiraから取得
4. qatlassianはアクセストークンを用いてJiraのデータへアクセス

ここで一つ気をつけなければならないのは、Jiraから返されたアクセストークンはユーザに結びつけられた情報なのでセッションごとに保持しなければならないということ。アプリレベルで保持してしまうとログインしていないユーザは他のユーザの権限でJiraにアクセスしたりということが起きてしまいます。

より詳細なステップは以下のシーケンス図にあります。
<img width="507" alt="Screen Shot 2018-12-22 at 14.52.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/30342/39bcfee0-c888-883f-256d-ecf2b927546e.png">

これを見ると複雑なステップに見えますが、自動的にリダイレクトされたりして進むのでブラウザの前に座るユーザ的にはそれほど複雑でもないです。ユーザ視点ではこんな感じ。なお、テストのためにqatlassianはローカル環境で動かしています。
1. 初期状態で `http://localhost:5000/`にアクセスするとloginへのリンクが表示されます。
![Screen Shot 2018-12-22 at 8.33.37.png](https://qiita-image-store.s3.amazonaws.com/0/30342/5dcb7998-7778-83e3-e0eb-0741654e3e6e.png)
2. それをクリックすると少し待たされますが次に表示されるのはJiraのログイン画面 (実際にはAtlassian共通のログインですが)
![Screen Shot 2018-12-22 at 8.34.35.png](https://qiita-image-store.s3.amazonaws.com/0/30342/90fa64c5-a678-d0b4-c15f-1d467a7ac2f4.png)
3. ユーザ名・パスワードを入れてログインすると次に認可確認の画面が表示されます。
![Screen Shot 2018-12-22 at 8.35.08.png](https://qiita-image-store.s3.amazonaws.com/0/30342/d2fe9429-8919-dea6-8135-7adc7043999c.png)
4. それで許可をするとまた少し待たされますが、qatlassianがJIRAから取得したデータを表示します（このサンプルではユーザの emailアドレスとアクセス権のあるプロジェクトの課題のリスト）
![Screen Shot 2018-12-22 at 8.35.30.png](https://qiita-image-store.s3.amazonaws.com/0/30342/7d33a999-4a9e-fda4-5821-442aae887d84.png)

別のブラウザからアクセスすると毎回ログインを促されますし、別のアカウントでログインするとその権限の違いによって取れるデータにも違いがあるのがわかります。ここでは以下のような権限設定を行なっています。

|アカウント|PROJ|RED|BLUE|
|-------|:-:|:-:|:-:|
|qiita.atlassian@gmail.com|OK|OK|OK|
|qiita.atlassian+1@gmail.com|OK|OK|NG|
|qiita.atlassian+2@gmail.com|OK|NG|OK|

![Screen Shot 2018-12-22 at 8.37.12.png](https://qiita-image-store.s3.amazonaws.com/0/30342/1c551074-4442-4f63-f794-f60ca8e9e5cf.png)
![Screen Shot 2018-12-22 at 8.37.49.png](https://qiita-image-store.s3.amazonaws.com/0/30342/53d2b068-5260-fb10-6cfc-f7bc1e077e9c.png)

なお動作させるには`.env`ファイルに以下の環境変数を設定してください。

|変数|値|
|---|---|
|JIRA\_SERVE_URL|利用するJIRAサーバのURL|
|CONSUMER_KEY|上記のセットアップ時に設定したコンシューマーキー| 
|PRIVATE_KEY|上記で生成したプライベート鍵(jira.pemの中身)|
|SECRET_KEY|なんでも良いです。Sessionを利用するために必要です|


#最後に
最新版のAPIでは2.0もサポートしているようなので、機会があれば今後そちらも試してみたいと思います。