PowerApps/CDS の WebAPI で OAuthブラウザログインが不要な接続を行う方法
先日開催した Office365 Graph APIに関する勉強会で Graph APIを使うためのアクセス許可のお話をしたんですが、その時に PowerApps・CDSでもこのアプリケーションアクセス許可が使えるよーと、ちらっとだけお話しました。
実はかなり前に Dynamics CRM版ではこのアプローチを紹介していたのですが、今回改めて PowerApps・CDSとして最新環境で書き直してみたものを紹介したいと思います。
アプリ認証のポイント
今回は以下のReferenceで紹介されている「アプリとして接続」を実施します。
このポイントはGraphAPI勉強会でもお話した「ClientCredentials 」で行う点と、この接続のための実行ユーザーを利用するという点にあります。
ClientCredentials つまるところアプリケーションのアクセス許可によるAPIの利用には、通常のOAuthプロセスで行われるユーザーログインの要求から、アクセス許可、アクセストークンの取得といったプロセスが存在しません。
そのため、明示的なアクセスユーザーが存在しない、というのが特徴なのですが、PowerApps CDSはそのアーキテクチャやAPIの特性上、必ずアクセスユーザーというものが存在する前提で構成されています。
そのため、Azure ADアプリ登録までは基本的に同じプロセスなのですが、アプリとして接続するためのユーザーの作成と、そのユーザーとアプリを紐づける、という特殊な作業が発生します。
ここを抑えておくことが、アプロとしての接続を行う場合に重要なポイントとなります。
1. アプリの登録
何はともあれ、まずは Azure ADにアプリを登録しましょう。基本的な手順は今までの記事に書いてあるのと一緒です。
Azure Active Directory 管理センターに移動し、「アプリの登録」→「+新規登録」をクリック。
任意のアプリケーション名を入力して、シングルテナントアプリケーションとして作成します。リダイレクトURIはローカルホストでかまいません。今回は特に使うシチュエーションがないためです。
アプリを作成したら、クライアントIDを控えておきます。
次に、「証明書とシークレット」の画面に移動して、クライアントシークレットを作成します。任意の名称と有効期間で登録し
生成されたクライアントシークレットを同じように控えておきます。
最後にAPIのアクセス許可を設定します。
PowerApps・CDSと言いながら、アクセス許可的にはDynamics CRMを付与します。「PowerApps Runtime Service」というものもありますが、関係ありません。
ここでちょっと注意なのですが、今回はClientCredentials で行うため、GraphAPIの文脈で行くとアプリケーションの許可になるのですが、PowerApps/CDSの性質上、それが使えません。そのため、「委任されたアクセス許可」を付与してあげます。
2. PowerApps・CDS側 アプリ認証用ユーザーの作成
次にこのアプリ認証用のためだけのユーザーというものを作成します。このユーザーを先ほど作成したAzure ADアプリと紐づけることで、OAuth ClientCredentials 時に明示的なユーザーを指定しなくても、アクセスができるようになります。
まず、PowerAppsのCDS環境にアクセスし、設定アイコンから「詳細設定」の画面に移動します。
私の環境が英語版で作ってしまったので英語ですが、日本語でもあまりかわらないです。設定一覧から「セキュリティ」に移動し
ユーザーをクリックします。
さて、ここでユーザーを作成するのですが、単純に「+新規」ボタンをクリックしてはいけません。
ここ「重要!」です。必ずビューの一覧から「アプリケーション ユーザー」を選択してください。
「アプリケーション ユーザー」を選択した後に「+新規」をクリックすることで新規作成画面に移動できます。
さらに、移動した後も気が抜けません。必ずフォームを「アプリケーション ユーザー」に切り替えてください。
これで初めてアプリケーション ユーザーの作成を行うことができるようになります。
この画面では、「UserName」「Applicaiton Id」「FullName」「PrimaryEmail」の4種類の項目を設定しますが、「Applicaiton Id」以外は自由に指定して大丈夫です。
「Applicaiton Id」のみ、先ほど作成したAzure ADアプリのClientIdを指定する必要があります。これを以下のように入力したら、保存をクリックします。
保存した後、仕上げにこのユーザーに対してセキュリティロールを設定します。これがAPIアクセス時におけるアクセス許可範囲に相当します。今回はあまり気にせずSystemAdminを付与しました。
3. AccessTokenを取得してみる
それでは設定した情報を使って、AccessTokenを取得してみます。私が使用したPostmanのコレクションも公開しておいたので、参考にしてみてください。
PostmanにCollectionとEnvironmentをインポート後、以下のように各環境変数を設定します。(AccessTokenは取得後)
それでは実際に、生のHTTP Requestを確認します。
なお、今回試していてわかったのですが、どうやらまだAzureADV2のOAuth2エンドポイントには今回のアプローチが対応していないようでした。なので、以下のコードはV1で実施したものを記載しています。
Azure ADのAccessToken要求URLは「POST:https://login.microsoftonline.com/{{TenantId}}/oauth2/token」です。
ここにパラメータとして「grant_type=client_credentials」とともに、取得したClientId・ClientSecretを渡します。併せてResourceとして対象となるPowerApps・CDS環境のURLを渡すのがポイントですね。
AzureテナントIDとCDSテナント名は私が使っているものをそのまま記載していますので、適宜差し替えてRequestしてみてください。Postmanのコレクションでは環境変数にそのまま設定すればOKです。
POST /bb003c28-bf32-425d-b5cc-58dc86690470/oauth2/token HTTP/1.1 Host: login.microsoftonline.com Content-Type: application/x-www-form-urlencoded grant_type=client_credentials&client_id=a98cea62-98f0-4b64-9d9d-b1265feb8ee5&client_secret=K2muHFy5VrBy1MW2_krbCB%3F%40vXXryLE%40&resource=https%3A%2F%2Forg61d4c5f1.api.crm.dynamics.com%2F
すると以下のようにAccessTokenを取得できます。
{ "token_type": "Bearer", "expires_in": "3600", "ext_expires_in": "3600", "expires_on": "1571059373", "not_before": "1571055473", "resource": "https://org61d4c5f1.api.crm.dynamics.com/", "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImFQY3R3X29kdlJPb0VOZzNWb09sSWgydGlFcyIsImtpZCI6ImFQY3R3X29kdlJPb0VOZzNWb09sSWgydGlFcyJ9.eyJhdWQiOiJodHRwczovL29yZzYxZDRjNWYxLmFwaS5jcm0uZHluYW1pY3MuY29tLyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0L2JiMDAzYzI4LWJmMzItNDI1ZC1iNWNjLTU4ZGM4NjY5MDQ3MC8iLCJpYXQiOjE1NzEwNTU0NzMsIm5iZiI6MTU3MTA1NTQ3MywiZXhwIjoxNTcxMDU5MzczLCJhaW8iOiI0MlZnWU9DOHZuQXpvNURwOWRrdWF3OEsyTG1yQUFBPSIsImFwcGlkIjoiYTk4Y2VhNjItOThmMC00YjY0LTlkOWQtYjEyNjVmZWI4ZWU1IiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvYmIwMDNjMjgtYmYzMi00MjVkLWI1Y2MtNThkYzg2NjkwNDcwLyIsIm9pZCI6IjQzMDQ2ZTI3LTM3MTUtNDE5NS05Yjk4LTE4MTVjYjkyYTFkMiIsInN1YiI6IjQzMDQ2ZTI3LTM3MTUtNDE5NS05Yjk4LTE4MTVjYjkyYTFkMiIsInRpZCI6ImJiMDAzYzI4LWJmMzItNDI1ZC1iNWNjLTU4ZGM4NjY5MDQ3MCIsInV0aSI6IlMwSG96RmVOUVVHRUszYml4NGRiQUEiLCJ2ZXIiOiIxLjAifQ.VEP12K8B3L2_GXMIKhdPPzndfh4iYn1WicJxNBnNIP8TAFTNZW7_39gtuGu4MMmYoa9yJnOXLmNp2TiW6mwxJRaALgmtl2FN-sb6xl01-j1lGOs-oVD_mPQwT7Ca_Y8Idql0Mj1OXIxqK93dEj4a-xNamRSF7iGzfdKh2uhi4CQVszg7iS_jU9zylp-gPi3aujJUuadTyDUlD_hevYfIJ0AHNuv9zotpvk5I7TjNfEwmEUQfnnYbIdr6KV13knw_0cAHzaQqjVpwMlww-_Y3dP7dFTGyu-rSW3c8HNm3tYVRc73tSY1_bExYrSFf8DWM47R7JY8mUph2zC_QNIkJfQ" }
これを使って、CDSのWeb APIにアクセスしてみると、以下のように取引先企業一覧が取得できました。
GET /api/data/v9.1/accounts HTTP/1.1 Host: org61d4c5f1.api.crm.dynamics.com Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc~~~~
ちなみに、WhoAmI を実行すると、作成したアプリケーションユーザーで実行していることが確認できます。
注意点
上記内容を見てもらえればわかると思いますが、ClientCredentialsを利用する以上、ClientIdとClientSecretが漏洩するとそのままPowerApps/CDSのWeb APIにアクセスができてしまいます。
通常のOAuthですとアプリに付与して公開とかあるかと思いますが、それとはまったく扱いが異なるものとなりますので、取り扱いにはご注意ください。