apex.oracle.comのAPIをOAuth2.0で保護する方法


# 概要

![スクリーンショット 2017-01-04 12.07.53.png](https://qiita-image-store.s3.amazonaws.com/0/26079/8e89ae2b-01b3-8ce2-2a64-7793e77514f6.png "スクリーンショット 2017-01-04 12.07.53.png")


apex.oracle.comやOracle Database CloudではREST APIによるデータアクセスが可能となっていますが、今回はそのAPIにOAuth2.0による認証を加え、APIを保護した上でアクセスする方法をみていきます。

構成はapex.oracle.comにデフォルトで用意されているデモ・データとAPIに対して、Nodeアプリからアクセスする形です。

apex.oracle.comのOAuth2.0の認証をNodeアプリに組み込むためのNPMパッケージ、[oracle-apex-oauth](https://github.com/nkjm/oracle-apex-oauth)を利用した[デモアプリ](https://github.com/nkjm/oauth-demo-for-oracle-example-hr)を利用して挙動を確認していきます。

# 環境

**ブラウザはChromeを前提に進めていきます。**もし他のブラウザでうまく表示されない箇所があれば、ブラウザをChromen最新版にして試してみてください。

# 手順

## ブラウザの優先言語を英語にする

いきなり妙なトリックですが、ブラウザの優先言語が日本語になっているとうまく設定画面が表示されません。

Chromeの場合、ナビゲーションメニューから環境設定 > 言語で英語を優先言語に設定してください。
![set_lang_to_en.gif](https://qiita-image-store.s3.amazonaws.com/0/26079/cf09f383-4c18-c31c-01dd-fc05a574788c.gif "set_lang_to_en.gif")


## RESTfulサービス権限を作成する

今回はapex.oracle.comにデモAPIとしてあらかじめ用意されているoracle.example.hrというRESTful ServiceをOAuth2.0の認証下に置くことにします。

SQLワークショップ > RESTFulサービスを選択し、右サイドバーからRESTfulサービス権限を選択します。
![click_RESTful_Service_priv.png](https://qiita-image-store.s3.amazonaws.com/0/26079/d3a507a3-32c6-7107-420a-7753c2834c19.png "click_RESTful_Service_priv.png")

作成ボタンをクリックします。

RESTfulサービス権限の詳細で下記のように新しい権限を設定し、作成ボタンをクリックします。

- 名前: **oracle.example.hr**
- ラベル:**oracle.example.hr**
- 割り当て済みグループ: **RESTful Services**
- 保護されたモジュール: **oracle.example.hr**
![RESTful Service detail.png](https://qiita-image-store.s3.amazonaws.com/0/26079/3c45b0f5-e4ee-4568-843f-15af630cda0d.png "RESTful Service detail.png")

これで新しい権限が作成され、同時にoracle.example.hrのAPIにアクセスするためにはOAuthの認証が必要になりました。

試しにoracle.example.hrのGET employees/をテストしてみてください。
![test.png](https://qiita-image-store.s3.amazonaws.com/0/26079/0b0fb4dc-4fd9-6b59-9a4f-e137f76b63ef.png "test.png")

下図のように「Unauthorized」が表示されるはずです。
![unauthorized.png](https://qiita-image-store.s3.amazonaws.com/0/26079/13fddf76-e1a1-f103-65f3-1ee6c6c4b5f7.png "unauthorized.png")

これでAPIは保護され、アクセスするには認証が必要となっていることが確認できました。

## 3rd Party Applicationをクライアントとして登録する

次にこのAPIにアクセスする3rd Partyアプリケーションを「クライアント」として登録します。OAuth2.0ではこのクライアント登録によって、OAuth2.0の認証に必要なCLIENT IDやCLIENT SECRETが発行されます。

下記URLにアクセスし、APEXワークスペースにログインするときと同じアカウントでログインします。
https://apex.oracle.com/pls/apex/あなたのワークスペース/ui/oauth2/clients/
![signin.png](https://qiita-image-store.s3.amazonaws.com/0/26079/f850eff1-663a-ae49-5a52-55ab198e0889.png "signin.png")

下図のようなOAuthクライアント登録画面に遷移します。 *＊下図に表示されているGet DBなどのクライアントは私の環境であらかじめ作成してあったものなので無視してください。*
![clients.png](https://qiita-image-store.s3.amazonaws.com/0/26079/d3e90c5a-d51a-d0c5-49eb-582d63aa4dc4.png "clients.png")

新しいクライアントを登録するため、Register Clientボタンをクリックし、下記のようにクライアントを設定してRegisterボタンをクリックします。

- Name: **oracle.example.hr**
- Description: **demo**
- Authorization Type: **code**
- Redirect URI: **http://localhost:5000/oauth**
- Support Email: **ご自身のEmail**
- Required Scopes: **oracle.example.hr**


![スクリーンショット 2017-01-04 9.29.14.png](https://qiita-image-store.s3.amazonaws.com/0/26079/fe0b3392-7e68-85f7-9c02-69153b5a15bf.png "スクリーンショット 2017-01-04 9.29.14.png")
 
下図のようにクライアント一覧画面に遷移し、oracle.example.hrが登録されているはずです。
![スクリーンショット_2017-01-04_9_34_00.png](https://qiita-image-store.s3.amazonaws.com/0/26079/91723675-d9a3-667c-4c47-d9f06c3e4e74.png "スクリーンショット_2017-01-04_9_34_00.png")

このクライアントをクリックしてCLIENT IDとCLIENT SECRETを確認します。
![スクリーンショット_2017-01-04_9_35_39.png](https://qiita-image-store.s3.amazonaws.com/0/26079/3eb40a2d-7823-186f-e850-33146d4f017a.png "スクリーンショット_2017-01-04_9_35_39.png")

CLIENT IDは「Client Identifier」というフィールドの値をそのまま利用します。
CLIENT SECRETは、Authorization URIの中に下記のようにclient_secretがQuery Stringとして埋め込まれているのでそれを抜き出して利用します。

```authorization_uri
https://apex.oracle.com/pls/apex/あなたのワークスペース/oauth2/auth?response_type=code&client_id=あなたのCLIENT_ID&client_secret=あなたのCLIENT_SECRET&state=仮のSTATE&_auth_=force
```

3rd PartyアプリケーションはこのCLIENT_ID, CLIENT_SECRETを使ってOAuth2.0の認証フローを実行できます。

## 保護されたAPIにアクセスする

今回の保護されたAPIにアクセスするNodeのデモアプリをクローンし、必要なNPMパッケージをインストールします。

```shell
$ git clone https://github.com/nkjm/oauth-demo-for-oracle-example-hr.git
$ cd oauth-demo-for-oracle-example-hr/
$ npm install
```

config.jsファイルを編集し、WORKSPACE, CLIENT_ID, CLIENT_SECRETをご自身の環境の値に置き換えます。

```
$ vi config.js
```

```javascript:config.js
'use strict';

exports.CLIENT_ID = 'あなたのCLIENT_ID';
exports.CLIENT_SECRET = 'あなたのCLIENT_SECRET';
exports.WORKSPACE = 'あなたのAPEXワークスペース';
```

Nodeアプリを起動します。

```
$ node index.js
Server is running on port 5000
```

ブラウザを起動し、 http://localhost:5000 にアクセスします。
するとOAuth2.0の認証フローが開始され、3rd Partyアプリの認可画面が表示されるのでAllow Accessボタンをクリックします。まだ認証をおこなっていない場合はログイン画面が表示されますので、apex.oracle.comにログインしたので同じアカウントでログインした後、前述の認可画面が表示されます。
![スクリーンショット 2017-01-04 10.11.30.png](https://qiita-image-store.s3.amazonaws.com/0/26079/69cfe98b-8f21-1f77-ef9a-3f3d038d732c.png "スクリーンショット 2017-01-04 10.11.30.png")


認可が成功すると、Nodeアプリにリダクレクトされ、画面にはトークンの情報が表示されます。
![スクリーンショット_2017-01-04_10_11_52.png](https://qiita-image-store.s3.amazonaws.com/0/26079/de6ba3cb-e6fc-26d6-deff-906ba938644a.png "スクリーンショット_2017-01-04_10_11_52.png")


上記トークンはサーバー側（Nodeアプリ）に保存されており、この状態で続けて http://localhost:5000/employees にアクセスすると、保護されたAPIにアクセストークンを付与してアクセスがおこなわれ、employeeの一覧がJSON形式で返され画面に表示されます。
![スクリーンショット 2017-01-04 10.12.45.png](https://qiita-image-store.s3.amazonaws.com/0/26079/3a92628c-a21a-464d-796f-1db9d1cc2aa1.png "スクリーンショット 2017-01-04 10.12.45.png")

上記APIは先ほどまでは「Unauthorized」として結果を返してくれませんでしたが、今回Authorizationヘッダーにアクセストークンを付与することでアクセスが許可されました。

## デモアプリとOAuth2.0の仕組み

まずNodeアプリのルート(http://localhost:5000) にアクセスすると、サーバーのセッション情報にトークンが保存されているかどうかを確認します。トークンがなければ下記のOAuth2.0の認証フローが開始されます。apex.oracle.comではAuthorization Codeを使った認証フローとImplicit認証フローという2タイプの認証フローが利用可能ですが、今回はAuthorization Codeフローを利用しています。

**①認証フローの開始**

認証フローはまずAuthorization URLにリダクレクトするところからはじまります。Authorization URLにリダクレクトされると、すでに認証済みであれば3rd Partyアプリケーションからのアクセスを許可するかどうかの認可画面が表示されます。未認証であれば、まずログイン画面が表示され、認証成功後に前述の認可画面が表示されます。

**②Authorization Codeの付与** 

ユーザーがアクセスを許可すると、クライアント登録の際に指定したRedirect URIにリダクレクトされ、同時にそのRedirect URIに含める形でAuthorization Codeが与えられます。

**③トークンの要求**

クライアント側（Nodeアプリ）はそのAuthorization Codeを使って、トークンプロバイダー（apex.oracle.com）にトークンを要求します。ここではPOSTリクエストが用いられます。

**④トークンの付与**

成功するとレスポンスとしてトークン情報が返されます。このトークン情報には、APIアクセス時に必要なアクセストークン、その有効期限、トークンを更新するためのリフレッシュトークンが含まれます。クライアント（Nodeアプリ）はこの情報を何らかの形で保存し、APIアクセス時にこのトークン情報を利用することで保護されたAPIにアクセスできるようになります。

--

上記の認証フローを経てトークンが入手できた場合、デモアプリはそのトークン情報をJSON形式でそのまま画面に出力します。この時点でoracle-apex-oauthパッケージがトークン情報をセッションに保存しています。

有効なトークンが存在する状態で http://localhost:5000/employees にアクセスすると、NodeアプリはアクセストークンをAuthorizationヘッダーに付与してAPIアクセスをおこないます。

OAuthフローの実装コードについてはoracle-apex-oauthのコードを参照してみてください。また、このパッケージを使うことで任意のNodeアプリにapex.oracle.comのOAuth認証を組み込むことができます。こちらも詳しくは下記パッケージのREADMEを参照してみてください。

[oracle-apex-oauth](https://github.com/nkjm/oracle-apex-oauth)

> oracle-apex-oauthはトークン情報をセッションに保存していますが、このセッションストアは開発用のcacheを利用しています。本番環境で利用する際は、このセッションストアを永続性のあるKey-Value Storeのようなストレージに保存してください。

Enjoy.