認証付きCAPプロジェクトを使ってUIを開発するには

##はじめに
認証が必要なCAPのODataサービスを開発し、それをDestinationに登録してUI5アプリから使いたいというケースがあると思います。この際、次のような課題があります。
①データソースを選択したときに、認証を聞かれて先に進めない
②UI5アプリをローカルで実行しようとする際に権限がないためエラーになる

この記事では、上記の課題を解決し、認証付きのCAPプロジェクトを使ってUI5アプリの開発、ローカルでの実行ができるようにする方法について説明します。

###前提
- 開発環境はBASを使用する（考え方はVS Codeなどでも同じ）
- UIとCAPでは別々のXSUAAサービスインスタンスを使用する

###サマリ
BASで開発するUI5アプリから認証付きCAPサービスを利用する方法は2つあります。

1. BTPにデプロイされたCAPサービスを使う ⇒ 開発者にCAPサービスを利用する権限が必要
2. ローカルのCAPサービスを使う ⇒ 開発者にCAPサービスを利用する権限は不要

##前提となるCAPプロジェクト
ベースとなるプロジェクトは[GitHub](https://github.com/miyasuta/cap-auth-qiita)からダウンロードできます。
このサービスは、全体的にauthenticated-user（認証されたユーザ）しかアクセスできなくなっており、Booksエンティティを使用するにはAdminロールが必要です。

```
using my.bookshop as my from '../db/data-model';

@requires: 'authenticated-user'
service CatalogService {
     @(restrict: [
            { grant: ['READ', 'WRITE'], to: 'Admin' }
        ])    
    @readonly entity Books as projection on my.Books;
}
```

※認証付きのCAPの開発方法については以下も参照
[【CAP】XSUAAによる認証を追加・ローカルでテスト](https://qiita.com/tami/items/77f8cad68cfd6975c9c0)

###ローカル開発用の設定を確認
.cdrc.jsonというファイルにローカル開発用の権限の設定があります。この設定により、ローカル実行時は"admin"というユーザでアクセスすることで、認証をパスすることができます。

```json
{
    "auth": {
        "passport": {
            "strategy": "mock", 
            "users": {
                "admin": { 
                    "password": "dummy", 
                    "ID": "admin",
                    "roles": ["Admin", "authenticated-user"]
                }                        
            }
        }
    }    
}

```

###Destinationの登録
CAPプロジェクトをCloud Foundryにデプロイし、Destinationを登録します。UIとCAPでXSUAAのサービスインスタンスが分かれるため、Authenticationのタイプを[OAuthUserTokenExchange](https://help.sap.com/viewer/cca91383641e40ffbe03bdc78f00f681/Cloud/en-US/e3c333f9de6245fca326993f2397c13a.html)としています。
Client ID, Client Secret, Token Service URLはCAPのサービスにバインドされたXSUAAのサービスキーから取得したものです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/47cb33e7-ebcd-be68-c45e-13d9129510be.png)

**設定値**

|項目  |設定値  |
|---|---|
|Name  |任意の名前  |
|Type  |HTTP  |
|URL  |CAPサービスのURL  |
|Proxy Type  |Internet  |
|Authentication  |OAuth2UserTokenExchange  |
|Client ID  |サービスキーの"clientid"  |
|Client Secret  |サービスキーの"clientsecret"  |
|Token Service URL  |サービスキーの"url" + /oauth/token  |

**Additional Properties**

|項目  |設定値  |
|---|---|
|HTML5.DynamicDestination  |true  |
|WebIDEEnabled  |true  |
|WebIDEUsage  |odata_gen  |


**サービスキーの作り方**

コマンド：`cf create-service-key <uaa_instance_name> <service_key_name>`
例：`cf create-service-key cap-auth-uaa dest-key`

**サービスキーの表示**

コマンド：`cf service-key <uaa_instance_name> <service_key_name>`
例：`cf service-key cap-auth-uaa dest-key`


##CAPを参照してUIを登録する
###1. BTPにデプロイされたCAPサービスを使う場合
BASを使うとき、すでにBTPのユーザとして認証が行われているため、開発者（自分）に権限をつけるだけで認証が必要なCAPサービスにアクセスすることができます。

####1.1. ロールの割り当て
BASに入る前に、開発者にCAPサービスを実行するのに必要な"Admin"のロールを割り当てておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/c36eb0c0-47a0-f96b-6c2b-5890c0f4cb1a.png)
※すでにBASにログイン済の場合、ロール割当の後で一旦ログアウトしてログオン画面から入り直す必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/ef57e516-0536-6b30-b9a7-c3ff79364d7b.png)

####1.2. プロジェクトの生成
テンプレートを起動し、"SAP Fiori application"を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/62e67362-a7f5-f463-2e22-2ca59b651ab2.png)
データソースに"Connect to a System"を選択し、Systemに1.で登録したDestinationを選択します。Service Pathに`/catalog`と選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/918c2937-4753-89a8-ca0a-d4c638cfe411.png)
BASにBTPユーザとしてログインしているので、認証を聞かれずに次画面に遷移しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/7d631021-dec5-e175-488b-b81eea443f02.png)
Deployment configurationを追加する設定にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/908de23f-c4e6-9dec-7be3-8660e56796f8.png)
Destination nameに、1. で登録したDestinationを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/48503df6-6866-60d4-7fc5-add76ad89b59.png)
あとはウィザードに従って進めると、プロジェクトが生成されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/3c0a28ce-2d27-a777-652f-db0238b8efaf.png)

####1.3. 実行してみる
実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/dad94d0c-1dee-cdc9-53e5-2304553ee4c8.png)
"Go"ボタンを押すとデータが表示されます。権限つきのユーザでアクセスできているということです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/3a0f6d93-f304-281e-41bf-458ec7073826.png)

###2. ローカルのCAPサービスを使う場合
開発者に権限をつけたくない場合には、CAPサービスをローカルで動かして、UIからそこにアクセスすることができます。このためには、CAPのプロジェクトをUIと同じBASのスペースにクローンしておく必要があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/94b2538c-2457-e2b1-1b90-953be4c89156.png)

ウィザードを起動する前に、ローカルのCAPプロジェクトを`cds watch`で起動しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/e7919d92-3641-b9d7-71b5-71c591ffaff5.png)

####2.1. プロジェクトの生成
以下では、プロジェクトの生成で1.と変わる部分について説明します。

データソースに"Connect to an OData Service"を選択し、OData service URLにローカルで実行中のODataサービスのURLを入力します。この際、認証を聞かれるので、.cdrc.jsonファイルにあるユーザ、パスワードを入力してログインします。今回の場合だとユーザが"admin"、パスワードが"dummy"となります。

```json
            "users": {
                "admin": { 
                    "password": "dummy", 
                    "ID": "admin",
                    "roles": ["Admin", "authenticated-user"]
                }                        
            }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/3ce0dac0-6eb3-30e7-c038-73362c897964.png)
認証をパスすると次画面に進むことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/6b589954-5fe9-32c2-493d-bfd25dd35842.png)
Deployment Configurationでは1.と同じように登録済のDestinationを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/10026bf1-8f17-adab-fcf8-269d4c51fbf3.png)

####2.2. 実行してみる
実行すると、認証を聞かれます。ここでも"admin"のユーザIDとパスワードを入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b7f93cf6-a785-ec54-c94a-b9a294f4d647.png)
データが表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/18c8cdfd-0ebe-b352-7bf3-631bacaaf805.png)

※ユーザID、パスワードをui5.yamlのfiori-tools-proxyのconfigに設定したかったのですが、現在のところそれはサポートされていないようです。
https://answers.sap.com/questions/13196438/feature-request-2.html

```yaml
  customMiddleware:
  - name: fiori-tools-proxy
    afterMiddleware: compression
    configuration:
      ignoreCertError: false # If set to true, certificate errors will be ignored. E.g. self-signed certificates will be accepted
      backend:
      - path: /catalog
        url: http://localhost:4004
```

####2.3. デプロイのための設定
生成されたxs-app.jsonファイルを見たところ、なぜかCAPサービスへのルートが定義されていませんでした。

```xs-app.json
{
  "welcomeFile": "/index.html",
  "authenticationMethod": "route",
  "routes": [
    {
      "source": "^/resources/(.*)$",
      "target": "/resources/$1",
      "authenticationType": "none",
      "destination": "ui5"
    },
    {
      "source": "^/test-resources/(.*)$",
      "target": "/test-resources/$1",
      "authenticationType": "none",
      "destination": "ui5"
    },
    {
      "source": "^(.*)$",
      "target": "$1",
      "service": "html5-apps-repo-rt",
      "authenticationType": "xsuaa"
    }
  ]
}
```
このままでは当然、Launchpadではバックエンドにアクセスできずにエラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/92752346-a138-c3df-ba8b-9f1442e818c7.png)
そこで、以下のルートを追加します。

```
    {
      "source": "^/catalog/(.*)$",
      "target": "/catalog/$1",
      "destination": "cap-auth-srv",
      "authenticationType": "xsuaa",
      "csrfProtection": false
    },
```
この事象はジェネレーターの不具合の可能性もあり、いずれ直るとは思いますが、それまではルートがちゃんと入っているか確認が必要です。

##まとめ
この記事では、2つの方法で認証が必要なCAPのODataサービスをローカルのUI5アプリから利用する方法を説明しました。

1. BTPにデプロイされたCAPサービスを使う
2. ローカルのCAPサービスを使う

2.の場合、CAPプロジェクトをローカルに持ってくるので、CAP側が更新された場合にローカルのCAPプロジェクトも更新する必要があります。また、デプロイ用に設定を追加する必要があります。これらより、開発者に権限を付与できるのであれば、1.の方がスムーズだと思います。