IBM Cloud App IDのOAuthフローを手動でやってみる

# はじめに

以前、[IBM CloudのApp IDのNode.js クイックスタートの記事](https://qiita.com/yo24/items/2bf97da7a31b364ff4f6)を投稿しました。

この際、OAuth2.0の認可コードフローに沿った処理を実施していますが、この処理はAppIDのSDKで実施しているため、内容はブラックボックスになっていました。

そこで、ブラックボックス化された処理内容を理解するために、認可コードフローを手動でやってみたいと思います。

# 前提条件

[本リンクのAppIDの設定](https://qiita.com/yo24/items/2bf97da7a31b364ff4f6#app-id%E3%81%AE%E8%A8%AD%E5%AE%9A) が実施済みであることを前提とします。

# 認可コードフロー(再掲)
記事理解促進のため、前回記事のフロー図を再掲しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/e6760385-81d6-1535-3dee-b7b26bf68ba3.png)

# 認可コードの取得
まずは認可コード取得まで解説します。

## 認可コードリクエスト(フロー図 ２)
まずは、認可コードの取得を行います。
下記形式のURLを、ブラウザのアドレスバーに貼り付けます。

```console:oauth/v4/authorization
https://jp-tok.appid.cloud.ibm.com/oauth/v4/<tenantId>/authorization?response_type=code&client_id=<アプリケーションのclientId>&redirect_uri=<appIdに登録したリダイレクトURL>&scope=openid
```

tenantId,clientIdに設定する内容は、AppIDのコンソールでアプリケーション登録時に取得できる値です。画面例は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/ef5587e4-eef4-a409-5b3f-a57f488ed2a8.png)

redirect_uriに設定する内容は、AppID コンソールの認証の管理タブで登録した値です。
画面例は以下の通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/4f078c48-54f0-3fb8-b66e-1c9923818257.png)

上記を踏まえパラメーターを実値化したURLは以下の通りです。

```console:oauth/v4/authorization<実値つき>
https://jp-tok.appid.cloud.ibm.com/oauth/v4/fa5ca1dd-xxxxx/authorization?response_type=code&client_id=2d83374f-xxxxx&redirect_uri=http://localhost:3000/appid/callback&scope=openid
```

## 認証画面へリダイレクト(フロー図 3から4)
URLを入力後、認証画面へリダイレクトされます。 登録済みのEmail、Passwordを入力し[Sing in]を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/9c2eaeef-9e87-6179-5f22-4bd9538b9f00.png)

## 認可コード リダイレクト(フロー図 5)
Sign in押下後、認可コードがリダイレクトで伝達されます。
今回、このリダイレクトを処理するアプリケーションを起動していないため、エラー画面になりますが、アドレスバーから認可コードが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135384/a55c8d4a-1744-29d2-802d-821872e5eeae.png)

# トークンの取得(フロー図 6から7)

次にtoken取得APIを実行し、アクセストークン、IDトークンを取得します。
下記形式のcurlコマンドを実行します。

```:/oauth/v4/token
curl -i -X POST \
 -u '<client_id>:<secret>' \
 -d 'grant_type=authorization_code&redirect_uri=<redirect_uri>&code=<認可コード>' \
 https://jp-tok.appid.cloud.ibm.com/oauth/v4/<tenant_id>/token
```

ここでsecretのパラメーターは、AppIDのコンソールでアプリケーション登録時に取得できる値です(画面例は既出)。
secret以外のパラメータは、認可コード取得時の値を設定します。

```:/oauth/v4/token(実値付き)
curl -i -X POST \
 -u "2d83374f-xxxxx:NmJjYWIxxxxx" \
 -d 'grant_type=authorization_code&redirect_uri=http://localhost:3000/appid/callback&code=ZsKFwqxxxxx' \
 https://jp-tok.appid.cloud.ibm.com/oauth/v4/fa5ca1dd-xxxxx/token
```

レスポンス例は以下の通りです。
トークンは長いので省略しています。

```json:/oauth/v4/token-response
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5c<省略>",
  "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6I<省略>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "openid appid_default appid_readuserattr appid_readprofile appid_writeuserattr appid_authenticated"
}
```

# IDトークンに含まれる情報

取得したIDトークンを復号化してみます。
このトークンはJSON Web Token形式であり、復号化することでユーザー情報が取得できます。
(記載は割愛しますが、アクセストークンも復号化できます)

なお、復号化には[jwt.io](https://jwt.io/)を利用しました。

```json:id-token(Header)
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "appId-fa5ca1dd-xxxxx-2019-12-05T01:19:36.538",
  "ver": 4
}
```
```json:id-token(Payload)
{
  "iss": "https://jp-tok.appid.cloud.ibm.com/oauth/v4/fa5ca1dd-xxxxx",
  "aud": [
    "2d83374f-xxxxx"
  ],
  "exp": 1579233921,
  "tenant": "fa5ca1dd-xxxxx",
  "iat": 1579230321,
  "email": "hogehoge@fuga.com",
  "name": "hoge hoge",
  "sub": "96d17139-ae58-4964-af19-80dbc8c62405",
  "email_verified": true,
  "given_name": "hoge",
  "family_name": "hoge",
  "identities": [
    {
      "provider": "cloud_directory",
      "id": "e188eeca-9b78-44ca-95ce-f207a48981ed"
    }
  ],
  "amr": [
    "cloud_directory"
  ]
}
```

# (おまけ)アクセストークンを使ってAPIを実行

おまけとして取得したアクセストークンを使ってAPIを実行してみます。
ここでは、より詳細なユーザー情報を取得するためのAPIを実行します。

アクセストークンは、Authorizationヘッダーに指定します。

```console:/oauth/v4/userinfo
curl -i \
 -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cxxxxx" \
 https://jp-tok.appid.cloud.ibm.com/oauth/v4/fa5ca1dd-xxxxx/userinfo
```

```json:/oauth/v4/userinfo(response)
{
  "sub": "96d17139-ae58-4964-af19-80dbc8c62405",
  "name": "hoge hoge",
  "email": "hogehoge@fuga.com",
  "given_name": "hoge",
  "family_name": "hoge",
  "identities": [{
    "provider": "cloud_directory",
    "id": "e188eeca-9b78-44ca-95ce-f207a48981ed",
    "idpUserInfo": {
      "displayName": "hoge hoge",
      "active": true,
      "mfaContext": {},
      "emails": [{
        "value": "hogehoge@fuga.com",
        "primary": true
      }],
      "meta": {
        "lastLogin": "2020-01-17T04:41:41.052Z",
        "created": "2019-12-05T05:43:53.507Z",
        "location": "/v1/fa5ca1dd-xxxxx/Users/e188eeca-9b78-44ca-95ce-f207a48981ed",
        "lastModified": "2020-01-17T04:41:41.061Z",
        "resourceType": "User"
      },
      "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
      "name": {
        "givenName": "hoge",
        "familyName": "hoge",
        "formatted": "hoge hoge"
      },
      "id": "e188eeca-9b78-44ca-95ce-f207a48981ed",
      "status": "CONFIRMED",
      "idpType": "cloud_directory"
    }
  }]
}
```

# 参考

[AppIDのAPIリファレンス](https://us-south.appid.cloud.ibm.com/swagger-ui/#/)