Imgur APIで学ぶOAuth2.0認証

# はじめに
## 概要
- Imgurとは…`オンライン画像共有と画像管理サービス`です。
- つまりは、画像をアップロードしオンラインに保存・共有することができるサービスです。
- ImgurにはAPI（認証・アップロード・削除etc...）が用意されています。
- 今回はこのImgur APIを使って`OAuth2.0認証`および`画像のアップロード`を行います。

## 対象
- `OAuth2.0認証`の実例を確認したい方
- Imgur APIを使用したい方

# OAuth2.0認証の流れ
## 概要
- まず初めに、ImgurAPIにおける`OAuth2.0認証`の流れの概要を示します。
- 下記を見ると一般的な理解が深まるかと思います。（正直なところ、複雑で理解が大変ですね…）
    - [OAuth 2\.0 全フローの図解と動画 \- Qiita](https://qiita.com/TakahikoKawasaki/items/200951e5b5929f840a1f)
    - [一番分かりやすい OAuth の説明 \- Qiita](https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be)

## 使用するパラメータ
- 今回`OAuth2.0認証`で使用するパラメータは以下の4つです。
    - ※役割の記述は筆者の意訳のため不適切かもしれません。その際はご指摘下さい。

| 名称            | 役割                                  | 補足       |
|---------------|-------------------------------------|----------|
| Client ID     | ImgurAPIサービスから各アプリに割り振られたID         |          |
| Client Secret | ImgurAPIサービスから各アプリに割り振られたパスワード      |          |
| Refresh Token | Access Tokenを更新するためのキー              |          |
| Access Token  | アプリがユーザの情報を使用できるよう要求するためのキー<br>意訳すると…アプリ内限定で有効な、ユーザIDとパスワードの代わりとなる認証キー | 一定期間後に失効 |

## OAuth2.0認証のフロー図

```
【凡例】
赤字：使用する情報
青字：更新された情報
```

### 1. ユーザ認証
- Imgur APIサービスにアプリの利用登録は済んでいる段階から話を始めます。
- アプリでユーザ情報を利用するため、認証用URLに飛ばして許可を求めます。

![](https://i.imgur.com/8S68AZj.jpg)

### 2. Access Tokenの更新

- `Access Token`は使用期限があります。
- そのため画像をアップロードする前に`Refresh Token`を使って更新します。
- その際`Refresh　Token`も新しいものが発行されます。

![](https://i.imgur.com/9KIoeSG.jpg)

### 3. 画像のアップロード
- `Access Token`を使って画像のアップロードを行います。
 
![](https://i.imgur.com/2WxnOlX.jpg)

# Imgur APIのユーザ登録
- では実際にImgur APIを利用してみます。
- [Imgur API を使ってサクッと画像アップロードしてみた](https://pisuke-code.com/web-basic-usage-of-imgur-api/)
    - この辺を参考に、ユーザ登録とAPI登録を行います。
- `Callback URL`は後ほど登録します。
    - 個人的に、CallBack URLに`Client ID`を含めたいからです。

![-w688](https://i.imgur.com/vo3NZcA.jpg)

- API登録後、`ClientID`と`Client secret`が表示されるので、書き留めておきます。
- 画像の認証キー類は失効させているのでそのまま載せておきます。

![-w742](https://i.imgur.com/NVHAym6.jpg)

- `Callback URL`を下記より設定します。

![](https://i.imgur.com/R67HixA.jpg)

- 特に決まりはありませんが、今回は下記の形式で登録します。
    - `{アプリ名}-{Client ID}://oauth-callback`
    - 例: `qiita-demo-a89ca891c652c84://oauth-callback`
- 本記事では扱いませんが、例えばMacアプリを作る場合、プロジェクト設定でこのコールバックURLを設定し、認証後のコールバックURLをアプリ側で受け取れるように設定します。

![-w440](https://i.imgur.com/o7nUNIq.jpg)

- Web側の設定は以上で、ImgurAPIの使用準備が完了しました。

# 非認証の画像アップロード
- 次に実際にターミナルからAPIを利用してみます。
- ImgurAPIには非認証のアップロード機能も用意されています。
    - 非認証アップロードの場合、削除用のハッシュ値を忘れてしまうと、アップロードした画像の削除ができなくなるので注意が必要です。
- 認証アップロードの場合、画像がユーザに紐づきます。
    - 例えばユーザ画面から過去にアップロードした画像の確認や削除が可能です。
- `OAuth2.0認証`は一旦横に置いて、これを使用して画像をアップロードしてみましょう。
    - ここでリクエストのフォーマットや、レスポンスの形式の概要を掴みます。
- まずはAPIのドキュメントを確認します。
    - [POST Image Upload](https://apidocs.imgur.com/?version=latest#c85c9dfc-7487-4de2-9ecd-66f727cf3139)

![-w1656](https://i.imgur.com/fHjMmdf.jpg)

- サンプルに沿ってターミナルで実行してみます。
- まずは画像をbase64文字列化します
    - 数MBの画像を使用したところbase64文字列が長いとエラーになってしまいました。
    - なのでサイズの小さいスクリーンショットを撮ってこれを使用しました。

![qiita_demo](https://i.imgur.com/wGe6lAd.png)

```sh
my_image=`base64 "/Users/<UserName>/Desktop/images/qiita_demo.png"`
```

- あとはこれをサンプル通り投げましょう。

```sh
curl --location --request POST 'https://api.imgur.com/3/image' \
--header 'Authorization: Client-ID a89ca891c652c84' \
--form "image=${my_image}"           
```

- 成功すれば（ステータスが200）下記の通りのJSON文字列が返ってきます。

``` JavaScript
{
	"data": {
		"id": "r5ZYjPi",
		"title": null,
		"description": null,
		"datetime": 1595926930,
		"type": "image/png",
		"animated": false,
		"width": 200,
		"height": 60,
		"size": 9789,
		"views": 0,
		"bandwidth": 0,
		"vote": null,
		"favorite": false,
		"nsfw": null,
		"section": null,
		"account_url": null,
		"account_id": 0,
		"is_ad": false,
		"in_most_viral": false,
		"has_sound": false,
		"tags": [],
		"ad_type": 0,
		"ad_url": "",
		"edited": "0",
		"in_gallery": false,
		"deletehash": "fhZe90irhTZKfg6",
		"name": "",
		"link": "https://i.imgur.com/r5ZYjPi.png"
	},
	"success": true,
	"status": 200
}
```

- URLにアクセスすると、実際にアップロードされていることが確認出来ます。

``` JavaScript
"link": "https://i.imgur.com/r5ZYjPi.png"
```

- 返ってきたJSONを見てみましょう。
- [下記のリンク先](https://api.imgur.com/models/basic)に、APIで返ってくるJSONの形式が確認できます。

![-w324](https://i.imgur.com/tQ7NisU.jpg)

![-w682](https://i.imgur.com/3f49ATH.jpg)

- 基本的にAPIの返り値は、下記のフォーマットで結果が返ってくるということですね
    - 前述の例では`data`に画像の詳細情報が入っていますね。

``` JavaScript
{
    "data"    : <mixed>,
    "status"  : 200,
    "success" : true
}
```

- 以上が非認証の画像アップロードです。
- イメージがつかめたでしょうか。

# 認証の画像アップロード
## OAuth2.0認証
- では本題の`OAuth2.0認証`です。
- `RefreshToken`と`AccessToken`を含んだコールバックURLを取得する所まで実装します。
- まずはAPIのドキュメントを確認します。
    - [Authorization and OAuth](https://apidocs.imgur.com/?version=latest#authorization-and-oauth)

```
https://api.imgur.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=REQUESTED_RESPONSE_TYPE&state=APPLICATION_STATE
```

- 上記のフォーマットのURLを作成して、ブラウザで開けば良さそうです。
    - stateは`hoge`をとりあえず入れています。この記事では特に使用しません。
- では下記のURLをブラウザで開きます。

```
https://api.imgur.com/oauth2/authorize?client_id=a89ca891c652c84&response_type=token&state=hoge
```

- ログインしているユーザに対して、APIを使用許可を求める画面が表示されます。

![-w745](https://i.imgur.com/m5PWAl6.jpg)

- リダイレクトURLを確認するために、下記を事前に設定しておきます。
    - [WebサイトのリダイレクトをChromeで調べるときのコツ：Tech TIPS \- ＠IT](https://www.atmarkit.co.jp/ait/articles/1708/21/news028.html)

>Google Chromeの「デベロッパーツール」を使えば簡単に確認できる。Windowsなら［F12］キー、Macなら［option］＋［command］＋［I］キーを押してデベロッパーツールを起動し、［Network］タブを選んでから対象のページを開く。すると、その読み込みの過程（ログ）が記録・表示される。

- `Allow`を選択すると、`Refresh Token`や`Access Token`などのユーザ情報を含んだURLにリダイレクトされます。
    - スキーム・ホストはもちろんコールバックURLで指定したものです。

![-w1683](https://i.imgur.com/yowOfV1.jpg)


```
qiita-imgur-api-demo-a89ca891c652c84://oauth-callback
?state=hoge
#access_token=188e32bbb697b7f5f8b00a8e9f81b3440571bdd0
&expires_in=315360000
&token_type=bearer
&refresh_token=7b8c109852078dce30b18333f93c222e0a1ca9a2
&account_username=IKEH1024
&account_id=104356397
```

- 以上で`Refresh Token`や`Access Token`が取得できました。
- これで認証の画像アップロードが可能な状態になりました。

## Access Tokenの更新
- 先に話した通り、`Access Token`には利用期限があります。
- そのためAPI使用前に`Refresh Token`を使って`Access Token`の更新が必要です。
- APIのドキュメントを確認します。
    - [POST Generate Access Token](https://apidocs.imgur.com/?version=latest#3f80c836-8f49-4fb1-95a7-a4b058265d72)
- 先程取得した`Refresh Token`を使って`Access Token`を更新します。

```sh
curl --location --request POST 'https://api.imgur.com/oauth2/token' \
--form 'refresh_token=7b8c109852078dce30b18333f93c222e0a1ca9a2' \
--form 'client_id=a89ca891c652c84' \
--form 'client_secret=602541ff9d75c7ca7bf79bc958ea834c80542ac2' \
--form 'grant_type=refresh_token'
```

- 更新された`Refresh Token`や`Access Token`を含んだレスポンスが返ってきます。

``` JavaScript
{
	"access_token": "d4468f3948766331f2ef5137ccdd8ca1faf5831d",
	"expires_in": 315360000,
	"token_type": "bearer",
	"scope": null,
	"refresh_token": "59f96cacae28879354a788ab6eb02125d45e5f3a",
	"account_id": 104356397,
	"account_username": "IKEH1024"
}
```

## 画像のアップロード
- 認証の画像アップロードは非認証のときとほとんど同じです。
- ヘッダの`Client ID`の代わりに`AccessToken`を指定するだけですね。
    - なぜかドキュメントに`POST Image Upload(Authed)`が無いですが、他の箇所を参考にして発言しています。

![-w347](https://i.imgur.com/71IqV6D.jpg)

```sh
my_image=`base64 "/Users/<UserName>/Desktop/images/qiita_demo.png"`
curl --location --request POST 'https://api.imgur.com/3/image' \
--header 'Authorization: Bearer d4468f3948766331f2ef5137ccdd8ca1faf5831d' \
--form "image=${my_image}"  
```

- 返ってくるJSON文字列は以下の通りです。

``` JavaScript
{
	"data": {
		"id": "mL1yuNg",
		"title": null,
		"description": null,
		"datetime": 1595953444,
		"type": "image/png",
		"animated": false,
		"width": 200,
		"height": 60,
		"size": 9789,
		"views": 0,
		"bandwidth": 0,
		"vote": null,
		"favorite": false,
		"nsfw": null,
		"section": null,
		"account_url": null,
		"account_id": 104356397,
		"is_ad": false,
		"in_most_viral": false,
		"has_sound": false,
		"tags": [],
		"ad_type": 0,
		"ad_url": "",
		"edited": "0",
		"in_gallery": false,
		"deletehash": "USDnDO9hoGqZtt5",
		"name": "",
		"link": "https://i.imgur.com/mL1yuNg.png"
	},
	"success": true,
	"status": 200
}
```

# おわりに
- 私自身`OAuth2.0認証`仕組みの理解に苦しんだので、理解の一助になれば幸いです。
- 私が他に使用したことがある`OAuth2.0認証`を伴うサービスは`Google Photo`のみですが、基本的に同じ仕組みだったので、一度理解しておくと後々も役立ちそうです。