Morning Girl

Web API, Windows, C#, .NET, Dynamics 365/CRM etc..

PowerApps/CDS の WebAPI で OAuthブラウザログインが不要な接続を行う方法

先日開催した Office365 Graph APIに関する勉強会で Graph APIを使うためのアクセス許可のお話をしたんですが、その時に PowerApps・CDSでもこのアプリケーションアクセス許可が使えるよーと、ちらっとだけお話しました。

kageura.hatenadiary.jp

実はかなり前に Dynamics CRM版ではこのアプローチを紹介していたのですが、今回改めて PowerApps・CDSとして最新環境で書き直してみたものを紹介したいと思います。

アプリ認証のポイント

今回は以下のReferenceで紹介されている「アプリとして接続」を実施します。

アプリとして接続

このポイントはGraphAPI勉強会でもお話した「ClientCredentials 」で行う点と、この接続のための実行ユーザーを利用するという点にあります。

ClientCredentials つまるところアプリケーションのアクセス許可によるAPIの利用には、通常のOAuthプロセスで行われるユーザーログインの要求から、アクセス許可、アクセストークンの取得といったプロセスが存在しません。

そのため、明示的なアクセスユーザーが存在しない、というのが特徴なのですが、PowerApps CDSはそのアーキテクチャAPIの特性上、必ずアクセスユーザーというものが存在する前提で構成されています。

そのため、Azure ADアプリ登録までは基本的に同じプロセスなのですが、アプリとして接続するためのユーザーの作成と、そのユーザーとアプリを紐づける、という特殊な作業が発生します。

ここを抑えておくことが、アプロとしての接続を行う場合に重要なポイントとなります。

1. アプリの登録

何はともあれ、まずは Azure ADにアプリを登録しましょう。基本的な手順は今までの記事に書いてあるのと一緒です。

Azure Active Directory 管理センターに移動し、「アプリの登録」→「+新規登録」をクリック。

f:id:sugimomoto:20191014223210p:plain

任意のアプリケーション名を入力して、シングルテナントアプリケーションとして作成します。リダイレクトURIはローカルホストでかまいません。今回は特に使うシチュエーションがないためです。

f:id:sugimomoto:20191014223215p:plain

アプリを作成したら、クライアントIDを控えておきます。

f:id:sugimomoto:20191014223221p:plain

次に、「証明書とシークレット」の画面に移動して、クライアントシークレットを作成します。任意の名称と有効期間で登録し

f:id:sugimomoto:20191014223230p:plain

生成されたクライアントシークレットを同じように控えておきます。

f:id:sugimomoto:20191014223236p:plain

最後にAPIのアクセス許可を設定します。

f:id:sugimomoto:20191014223244p:plain

PowerApps・CDSと言いながら、アクセス許可的にはDynamics CRMを付与します。「PowerApps Runtime Service」というものもありますが、関係ありません。

f:id:sugimomoto:20191014223252p:plain

ここでちょっと注意なのですが、今回はClientCredentials で行うため、GraphAPIの文脈で行くとアプリケーションの許可になるのですが、PowerApps/CDSの性質上、それが使えません。そのため、「委任されたアクセス許可」を付与してあげます。

f:id:sugimomoto:20191014223259p:plain

2. PowerApps・CDS側 アプリ認証用ユーザーの作成

次にこのアプリ認証用のためだけのユーザーというものを作成します。このユーザーを先ほど作成したAzure ADアプリと紐づけることで、OAuth ClientCredentials 時に明示的なユーザーを指定しなくても、アクセスができるようになります。

まず、PowerAppsのCDS環境にアクセスし、設定アイコンから「詳細設定」の画面に移動します。

f:id:sugimomoto:20191014223306p:plain

私の環境が英語版で作ってしまったので英語ですが、日本語でもあまりかわらないです。設定一覧から「セキュリティ」に移動し

f:id:sugimomoto:20191014223313p:plain

ユーザーをクリックします。

f:id:sugimomoto:20191014223319p:plain

さて、ここでユーザーを作成するのですが、単純に「+新規」ボタンをクリックしてはいけません。

ここ「重要!」です。必ずビューの一覧から「アプリケーション ユーザー」を選択してください。

f:id:sugimomoto:20191014223324p:plain

「アプリケーション ユーザー」を選択した後に「+新規」をクリックすることで新規作成画面に移動できます。

f:id:sugimomoto:20191014223331p:plain

さらに、移動した後も気が抜けません。必ずフォームを「アプリケーション ユーザー」に切り替えてください。

f:id:sugimomoto:20191014223336p:plain

これで初めてアプリケーション ユーザーの作成を行うことができるようになります。

f:id:sugimomoto:20191014223342p:plain

この画面では、「UserName」「Applicaiton Id」「FullName」「PrimaryEmail」の4種類の項目を設定しますが、「Applicaiton Id」以外は自由に指定して大丈夫です。

「Applicaiton Id」のみ、先ほど作成したAzure ADアプリのClientIdを指定する必要があります。これを以下のように入力したら、保存をクリックします。

f:id:sugimomoto:20191014223348p:plain

保存した後、仕上げにこのユーザーに対してセキュリティロールを設定します。これがAPIアクセス時におけるアクセス許可範囲に相当します。今回はあまり気にせずSystemAdminを付与しました。

f:id:sugimomoto:20191014223354p:plain

3. AccessTokenを取得してみる

それでは設定した情報を使って、AccessTokenを取得してみます。私が使用したPostmanのコレクションも公開しておいたので、参考にしてみてください。

github.com

PostmanにCollectionとEnvironmentをインポート後、以下のように各環境変数を設定します。(AccessTokenは取得後)

f:id:sugimomoto:20191014223401p:plain

それでは実際に、生の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"
}

f:id:sugimomoto:20191014223426p:plain

これを使って、CDSのWeb APIにアクセスしてみると、以下のように取引先企業一覧が取得できました。

GET /api/data/v9.1/accounts HTTP/1.1
Host: org61d4c5f1.api.crm.dynamics.com
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGc~~~~

f:id:sugimomoto:20191014223436p:plain

ちなみに、WhoAmI を実行すると、作成したアプリケーションユーザーで実行していることが確認できます。

f:id:sugimomoto:20191014223445p:plain

注意点

上記内容を見てもらえればわかると思いますが、ClientCredentialsを利用する以上、ClientIdとClientSecretが漏洩するとそのままPowerApps/CDSのWeb APIにアクセスができてしまいます。

通常のOAuthですとアプリに付与して公開とかあるかと思いますが、それとはまったく扱いが異なるものとなりますので、取り扱いにはご注意ください。