Power Automate for desktop「OAuth2.0認可フローのPKCEをやってみる」

## 概要

OAuth2.0認可フローの拡張仕様PKCE（Proof Key for Code Exchange）をPower Automate for desktopを使用して手組しDataverseからデータを取得します。アプリにクライアントクレデンシャルやユーザーパスワードを保存せず、都度生成されるcode_verifierで検証しアクセストークンを要求できます。

```mermaid
sequenceDiagram
autonumber
    rect rgb(191, 223, 255)
    Actor O as ユーザー
    participant C as クライアント
    participant M as Azure AD
    participant R as リソース（今回はDataverse)
    Note Right of O :ブラウザー
    O->>C: OAuth開始
    C->>M: Authorization Request
    Note over C,M: リソース情報各種+code_challenge
    M-->>O:身元確認
    O->>M:ログイン情報
    M-->>O:権限委任要求
    O->>M:権限委任許可
    M-->>O:リダイレクト先にAuthorizationCode
    Note over O,M: AuthorizationCode
    end
    C->>M:Access Token Request
    Note over C,M:AuthorizationCode+redirect_uri+code_verifier+client_id
    M-->>C:Access Token
    C->>R:Access Token +リソース要求   
```

![ConvertedOAuthDataverse2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/e17c12e4-c59c-8a35-17ef-133912fbf3a6.gif)
※ブラウザに認証を記憶させてある状態での動きです。

## 参考

https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow

https://www.rfc-editor.org/rfc/rfc7636

https://auth0.com/docs/get-started/authentication-and-authorization-flow/call-your-api-using-the-authorization-code-flow-with-pkce#1-create-a-code-verifier

## 注意事項など

* 挙動を確認するための学習目的です。
* Dataverseのセキュリティ ロール設定とエンティティ設定については触れません。
* リソースに応じて適切に設定する必要があります。
* ブラウザにアカウント登録されていない場合、手動で認証する必要があります。
* ブラウザに複数アカウントが登録されている場合、選択するフローを追加する必要があります。
* 初回、権限委任する必要があります。
* 簡素化するためidなどの設定情報はハードコーディングしています。
* code_verifierは英数字のほか "-" "."  "_"  "~"が使えますが、PADのランダムテキスト生成アクションでは記号の一部だけの利用はできないため今回は使用していません。
* 自己責任でお願いいたします。
* 2022年10月の情報です。

## Azureでアプリの登録

- リダイレクトURIは`http://localhost/myapp`としました。
    ![CleanShot 2022-10-02 at 18.19.49@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/2cc86254-60eb-2ee8-07c7-ad2828838336.png)

- APIアクセス許可の要求でDynamics CRM user_impersonationを許可します。
    ![CleanShot 2022-10-02 at 18.27.57@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/051c8f43-5276-3424-0efd-280190ea9f90.png)

概要からクライアントIDやテナントID、エンドポイントなど各種情報はコピペできます。

## フロー全体図

![01CleanShotflow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/cbfa4578-8edf-c2d1-e28b-4762c8ce295a.png)

## 詳細

### 1~6 各種設定情報

resourceはDataverseのWebAPIエンドポイントです。過去記事に書きました。

https://qiita.com/sys_zero/items/e2b3381eac50705bc488#3power-platform%E7%AE%A1%E7%90%86%E3%82%BB%E3%83%B3%E3%82%BF%E3%83%BC

### 8~10 S256 code_challengeの生成

PKCE　S256の仕様

  >S256
      code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

#### 8.code_verifierを生成

「ランダム テキストの作成」アクションを使って43~128文字のcode_verifierを生成します。
code_verifierの仕様では本来、英数字のほか "-" "."  "_"  "~"が使えます。しかしPADのランダムテキスト生成アクションでは記号の一部利用はできないため今回は使用していません。

![CleanShot 2022-10-02 at 20.18.44@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/ac863995-e1d2-df1c-24bf-d304eda16a6a.png)

#### 9.SHA256でハッシュ化します。

PADの「テキストをハッシュ化します」アクションはエンコードにASCIIを指定するとBase64エンコードされて出力されます。

![CleanShot 2022-10-02 at 20.21.37@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/93b9dc5f-d4d2-7523-4b49-2232ef01feb8.png)

#### 10.URL SAFEにします

Base64エンコードされたハッシュテキストの「+」を「-」に「/」を「_」に置き換え、パディングされている場合は最後尾「=」をトリムします。PADのアクションで行うことも考えましたが「PowerShellスクリプトの実行」を使うとシンプルに行えます。

![CleanShot 2022-10-02 at 20.31.14@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/7a96406f-a5de-e673-52cc-1777c093ce1f.png)

```powershell:PowerShell
"%HashedText%".TrimEnd('=').Replace('+', '-').Replace('/', '_')
```
### 12.State生成

認可エンドポイントから正しくリダイレクトされたか確認のため一応入れました。

![CleanShot 2022-10-02 at 20.36.50@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/d15af866-6c77-b0c7-e280-97acdf581d84.png)

### 13.認可エンドポイント

![CleanShot 2022-10-02 at 20.38.24@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/33b5bbee-fb6d-a300-3228-3503bd6c8569.png)
`https://login.microsoftonline.com/%tenant_id%/oauth2/authorize`

### 14.認可エンドポイントへの接続文字列生成

code_challenge_method=S256を使います。

![CleanShot 2022-10-02 at 20.40.52@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/124ff44a-9bf5-fed7-f73f-3280364d6b52.png)

```
%auth_endpoint%?resource=%resource%&response_type=code&client_id=%client_id%&state=%state%&scope=%scope%&redirect_uri=%redirect_uri%&code_challenge=%code_challenge.Trimmed%&code_challenge_method=S256
```
### 17〜23ブラウザで認証認可を実行

リダイレクトURIに戻ってくるまでループして待ちます。少し雑ですが、認証や認可が手動で求められた場合を考えてこのようにしています。
![CleanShot 2022-10-02 at 20.49.12@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/2b803a7e-e4ac-f25b-1624-9a8ac10775a7.png)

### 25.temporary codeの取得

正規表現を使ってURLからtemporary codeを取得します。

![CleanShot 2022-10-02 at 20.50.44@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/1e28b21e-e394-1628-4bfd-54227cf753c0.png)


### 26〜30.stateをチェック

あらかじめ生成したstateがリダイレクトURIに戻ってきたstateと一致するかチェックします。一致しない場合はフローを終了します。

![CleanShot 2022-10-02 at 20.54.19@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/73f7e28d-b595-2a5b-f534-57a4392ba045.png)

### 31〜36.Accsess Tokenの取得

![CleanShot 2022-10-02 at 20.59.10@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/378e8fd9-8286-0942-c74f-eca2381509a1.png)

#### 34.「Webサービスを呼び出します」アクション
![CleanShot 2022-10-02 at 21.01.29@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/09bb284c-4af3-9942-e77a-2500936aef86.png)

ハッシュ化前のcode_verifierをbodyに含めPOSTします。

```
grant_type=authorization_code&code=%code%&redirect_uri=%redirect_uri%&code_verifier=%code_verifier%&client_id=%client_id%
```
コンテンツタイプを`application/x-www-form-urlencoded`にします。

要求本文をエンコードしない設定にします。

デフォルトでオンになっているので注意が必要です。

### 38.取得できたアクセストークンを使ってらDataverseに試してみます。

ヘッダーにAuthorization:Bearer %token%としてトークンを含めます。

![CleanShot 2022-10-02 at 21.17.10@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/680039/d375a7dc-30a4-133a-4520-12fdc8c4bf0b.png)


## まとめ

- OAuth2.0認可フローのPKCEをPower Automate for desktopでやってみました。
- PKCEはs256 code_challengeを認可に、トークン取得時に元のcode_verifierを含めることで検証していることがわかりました。
- このことでアプリにパスワードやクライアントクレデンシャルを持たせずになりすましや横取りを防ぐことができます。
- Power Automate for desktopの「テキストをハッシュ化します」アクションがPKCEに使えることがわかりました。
- 手組は大変ですが勉強になりました。