はまったりひらめいたり…とか…

Angularや.NETやAzureやその他色々。

AADSTS501051 <ClientId> is not assigned to a role for the application への対処

出るたびに対応を忘れて調べているのでメモ。

Microsoft.Identity.ClientConfidentialClientApplicationBuilderでシークレットを使用したアクセストークンの取得を行いたいときに発生する場合の対応です。

はじめに

2021年7月時点の情報です。

参照時期によっては記事内の画面キャプチャや設定内容が異なっている可能性があるのでご留意ください。

何が起きているのか

エラーは下記のような内容です。

MsalUiRequiredException: AADSTS501051: Application 'Client Id'(AAD App name) is not assigned to a role for the application 'Scopes'(AAD App name).

僕がよくやる構成の話です。

Entiprise Applications上で認証に使用しているAzure ADアプリケーションの設定で

ユーザー割当必須にしている場合があります。

f:id:TakasDev:20210711111246p:plain

雑にサインインの制御を行いたい場合(ゲストユーザーのみとかその逆とか)の設定で

シークレット認証の場合ユーザーの情報を伴わない状態なのでエラーになる。というわけです。

対処

App Roleの作成

認証を行いたいAzure ADアプリケーションのApp rolesでアプリケーションのRole作成を行います。

項目 設定内容
Display name 任意な名前
Allowed member types Applications
Value 任意な値
Description Roleの説明文

APIのアクセス許可の設定

次にシークレット認証用のAzure ADアプリケーションを用意します。

このアプリケーションはユーザー割当必須の設定は行いません。

API permissionsから認証を行いたいAzureADアプリケーションへのアクセス許可の設定を行います。

先程設定を行った認証を行いたいAzure ADアプリケーションを指定します。

Appliaction permissionsで先程作成したApp Roleを指定します。

f:id:TakasDev:20210711113440p:plain

シークレットによる認証が可能となる

シークレット認証用のAzure ADアプリケーションでシークレットを生成し確認を行います。

f:id:TakasDev:20210711114527p:plain

モザイクだらけでアレですが、audが適切な形となっていること

rolesは指定したアプリケーションロールが設定されていることが確認できます。

おわりに

ちょいちょい発生しては対処方法を忘れているAADSTS501051のメモでした。

Azure Web PubSub ServiceのイベントをトリガーにAzure Funcrtionsを実行する

はじめに

2021年7月時点の内容です。

また、AzureWebPubSubServiceはプレビュー版ということもあり

記載している画面やソースコードや設定内容は参照時点によっては異なっている可能性が高いので

参照される際はご留意ください。

今回は.NETラボのセッション資料中で時間の関係上セッションに落とせなかった部分のメモ書きをこちらに落としておきます。

基本などはイベントでお話しました。

まず基本的なところから抑えたいという方は、資料などをご覧いただければと思います。

PubSubイベントをトリガーにFunctionsを実行したい

Azure Web PubSub Serviceの設定

Azure Web PubSub Serviceで起きたイベントをFunctionsで処理したいと思います。

ドキュメントはこちらから確認できます。

Azure Web PubSub ServiceのSettingsから接続先のURLを指定することで

イベントをトリガーにFunctionsを実行できるようになります。

URLはFunctionsのExtensionを使う場合(エンドポイント + /runtime/webhooks/webpubsub)となります。

設定内容は大まかに下図のとおりです。

f:id:TakasDev:20210705220630p:plain

とりあえず動きを確かめたい程度なら、SystemEventsは何も設定しなくてOKです。

Azure Functionsの実装

NuGetでMicrosoft.Azure.WebJobs.Extensions.WebPubSubを取得します。

Functionsでは下記を実装することでメッセージの送信をトリガーにした処理が可能になります。

Hub=…で指定された箇所が上図のHub nameと対応する箇所です。

[FunctionName("<Function名>")]
public static async Task<MessageResponse> Broadcast(
    [WebPubSubTrigger(WebPubSubEventType.User, "message")] BinaryData message,
    [WebPubSub(Hub = "SampleHub")] IAsyncCollector<WebPubSubOperation> operations
)
{
    // なんやかんや
}

PubSubServiceで使用されるwsswss://<resource name>/client/hubs/<hub name>?access_token=...の形式で

↑のWSSで指定されるhub nameに対する通信のイベントがトリガーとなる。といった流れです。

System Eventsをトリガーにする

設定のSystem Eventsの各項目にチェックを入れた場合は下図のコードでトリガー実行可能となります。

WebPubSubTriggerの第3引数がそれぞれ、connect/connectedのいずれかになります。

チェックを入れた方(あるいは両方)を実装します。

[FunctionName("<Function名>")]
public static ServiceResponse Connect(
    [WebPubSubTrigger("SampleHub", WebPubSubEventType.System, "connect")] ConnectionContext connectionContext,
    ILogger log
)
{
  // なんやかんや
}

[FunctionName("<Function名>")]
public static async Task Connected(
    [WebPubSubTrigger(WebPubSubEventType.System, "connected")] ConnectionContext connectionContext,
    [WebPubSub] IAsyncCollector<WebPubSubOperation> operations
)
{
  // なんやかんや
}

気をつけなければならないこと

当然といえば当然なのですが、message含めSettingsでURLを指定した場合

トリガーで実行される関数がない場合エラーとなり、Connectやメッセージの送信が失敗するようになります。

さいごに

Azure Web PubSub Serviceで発生する各イベントをトリガーにFunctionsで実行することができ

Connect前後にフローを追加したり、Messageに情報を付加して返却したりなどが簡単にできるようになっていることが確認できました。

発表や今回の記事で使用したサンプルソースは下記となります。

github.com

メモ書き-msal.jsでSSOしたらiOSやChromeでエラーが発生したので対応した話

はじめに

下記のライブラリのバージョンで発生したエラーに対する対応となります。

参照される時期によっては対応方法や対応不要になったりと状況が変わっている可能性がありますのでご留意ください。

  • @azure/msal-browser: 2.13.1

(状況変わっているのが一番うれしい

iOSChromeでSSOしたときにエラーが発生する

すでにadal.jsを使用しているWebアプリケーションやほかシステムでログインが行われている前提であった場合msal.jsの ssoSilent はよく使用する機能かもしれません。

SSO SilentはセッションCookieを利用しているのですが

iOSChromeのシークレットブラウザを使用した場合サードパーティCookieを参照できないため ssoSilentで下記のようなエラーとなってしまいます。

InteractionRequiredAuthError: login_required: AADSTS50058: A silent sign-in request was sent but no user is signed in. The cookies used to represent the user's session were not sent in the request to Azure AD. This can happen if the user is using Internet Explorer or Edge, and the web app sending the silent sign-in request is in different IE security zone than the Azure AD endpoint (login.microsoftonline.com).

シングル サインオン (MSAL.js) - Microsoft identity platform | Microsoft Docs

この問題に対する対応

対応策1:ChromeiOSの設定を変更する

てっとりばやいのはChromeの下記設定をOffにする

f:id:TakasDev:20210404153527p:plain

iOSの下記設定をOffにすることです。

f:id:TakasDev:20210404154042p:plain

しかし、あえてセキュリティ上守られているものを解除する方に寄せていくのもよろしくありません。

ここの設定を変更するのは原因の切り分けを行うときくらいにとどめておいたほうが良いと思っています。

対応策2:エラー発生時にLoginRedirect/LoginPopupを行うように対応する

エラーの内容としては「ちゃんとログインしてくださいね」ということなので、別途ログインするだけで良さそうです。

UserAgentなどで特定ブラウザだけloginRedirectすることも考えましたが

対応しなければいけないブラウザが増える可能性もありますし

ssoSilentしたときでAADSTS50058が出たときに対応したい。と状況は限定的なので

ssoSilentでエラーをキャッチしそこでloginRedirectなりloiginPopupを行うように対応してみました。

Popupを使用する場合は↓な感じです。

  async login(): Promise<AuthenticationResult> {
    const res = await this.client.ssoSilent({ loginHint: loginhint }).catch((err: AuthError) => {
      if(err.errorMessage.includes('AADSTS50058')) {
        return this.client.loginPopup();
      }
      throw err;
    });
    this.account = res.account;
    return res;
  }

AADSTS50058発生しているときだけloginPopupするようにしています。

Redirectを使用する場合は少し手間です。

RedirectされたときにキャッチするhandleRedirectPromiseをエラー発生時にだけ使用したいからです。

  async login(): Promise<AuthenticationResult | null> {
    const errFlow = sessionStorage.getItem('errflowaction');
    if (errFlow) {
      const res = await this.client.handleRedirectPromise();
      if (res) {
        this.account = res.account;
      }
      sessionStorage.removeItem('errflowaction');
      return res;
    }
    const res = await this.client.ssoSilent({ loginHint: loginhint }).catch((err: AuthError) => {
      if(err.errorMessage.includes('AADSTS50058')) {
        sessionStorage.setItem('errflowaction', 'dummy');
        this.client.loginRedirect();
        return null;
      }
      throw err;
    });
    if (res) {
      this.account = res.account;
      return res;
    }
    return null;
  }

少し不格好ですが、Redirect前にsessionStorageにFlow状態を記録しておき

それによって動作を変える方法で逃げることにしました。

さいごに

実験に使ったコードは以下に格納しています。

github.com

メモ書き - AzureDevOpsで別プロジェクトのCIをトリガーにパイプラインを実行する

はじめに

このメモは2021年1月17日時点のAzure DevOpsを使用したメモになっています。

参照されるタイミングによっては、記事内で称しているキャプチャ・設定内容が変更されている可能性がありますのでご注意ください。

やりたいこと

Docsみててこんがらがったので自分用メモ・・・

Azure DevOpsで複数のプロジェクトを構築。

各プロジェクトの構成は下な感じで。

  • 統合プロジェクト
    • サブプロジェクト1
    • サブプロジェクト2
    • ...

サブプロジェクトでライブラリ作成→統合プロジェクトでサブプロジェクトのライブラリを使用してクライアントアプリの開発。といった構成。

サブプロジェクトxのCIが成功→統合プロジェクトのCIを実行 な感じで動作させたい。

統合プロジェクトのパイプラインの構築

サブプロジェクト側のパイプラインは今まで通りの構成で問題なし。

統合プロジェクト側のyamlの頭に👇の構成。

trigger: none

resources:
  pipelines:
  - pipeline: subProjectPipeline  # パイプラインの名称。このCIからアクセスするときの識別子
    source: 'integration-pipeline-two (1)'  # トリガーするパイプライン
    project: integration-pipeline-two  # 別プロジェクトにあるCIの場合はこれを指定
    trigger:
      branches:
      - master

サブプロジェクトでPublisしたものをDownloadしたいときは👇でDLできる。

- download: subProjectPipeline  # pipelineで指定した識別子
  artifact: drop  # Artifact名

補足

Releasesを使用する場合、👇の2つをOffにしないと権限なしでサブプロジェクトでPublishされたリソースにアクセスできないとかあったけど、Pipelinesではそういうものはなさげ。

(まぁReleases Pipeline特有の設定っぽいからそらそうなんだけど、PipelineのほうはこういうのOffにする必要ないんだ。楽だなぁ。と。)

f:id:TakasDev:20210117135717p:plain

参考にしたサイト等

実験で使用したAzure DevOpsプロジェクト

Azure DevOpsのPipelineでOWASP ZAPを実行してみる

はじめに

2020年12月時点の情報で記事を作成しています。

参照される時期によっては、記事内で使用されているコマンド、画面キャプチャが使用できなくなっている可能性がありますのでご留意ください。

Azure PipelineでOWASP ZAPを実行したい

だいぶ前にGitHubActionsでOWASP ZAPのScanができるようになりました

例のごとくAzurePipelineでは使用できません。

Azure Pipeline上でOWASP ZAPのスキャンを使用してみましょう。

ZAP Dockerを使用する

OWASP ZAPがDockerファイルを提供しています。

Pipeline上でDockerコマンドが実行できるのでそれを使用してPipeline上で脆弱性チェックを行います。

と、いってもZAP Dockerのコマンド等の類の説明は山程あると思いますので省きます。

今回はDocker内で提供されているFullScanを使用しますが

細かい機能を使用したい場合はPowerShell経由でZAP APIを叩いて

Context作成→ContextにURL追加→Spider実行ないつもの流れを行えばいいと思います。

OWASP ZAP2.7でzap-API を使ってSpiderの実行 - 備忘録/にわかエンジニアが好きなように書く

WebAPIをPowerShellからテストする - Qiita

Azure Pipeline上で実行する

すでにPipeline上で実行する記事を書かれている方がいるのでそれを参考にしてみます。

How to run OWASP ZAP Security Tests Part of Azure DevOps CI/CD Pipeline

この記事ではReleaseパイプライン上で実行されているので、MultiStagePipeline上で実行できるようにいじってみます。

また、結果レポートはAzure Artifactsに格納されていますが、すこしアクセスしづらいのでBLOB上に格納してみます。

Pipeline構成

下記な構成のymlとなります

  1. SPAのWebApplicationをビルド
  2. Angularアプリケーションをビルドします
  3. WebAppsにデプロイ 1, デプロイ先のWebAppsの脆弱性調査+Report出力
trigger:
  branches:
    include:
    - master

stages:
- stage: build
  jobs:
  - job: build_job
    displayName: Build Angular
    pool:
      vmImage: ubuntu-latest
    steps:
    - task: npm@1
      displayName: npm ci
      inputs:
        command: custom
        customCommand: 'ci'
    - task: npm@1
      displayName: npm build
      inputs:
        command: custom
        customCommand: 'run build:ci'
    - task: ArchiveFiles@2
      displayName: 'Archive dist/pipeline-learn-front'
      inputs:
        rootFolderOrFile: 'dist/pipeline-learn-front'
        includeRootFolder: false
        archiveFile: '$(Build.ArtifactStagingDirectory)/drop.zip'
    - task: PublishBuildArtifacts@1
      displayName: 'Publish Artifact: drop'

- stage: deploy
  dependsOn: build
  jobs:
    - deployment: deploy_webapp
      displayName: Deploy WebApp
      environment: deploy
      strategy:
        runOnce:
          preDeploy:
            steps:
              - download: current
                artifact: drop
          deploy:
            steps:
              - task: AzureRmWebAppDeployment@4
                inputs:
                  ConnectionType: 'AzureRM'
                  azureSubscription: '***'
                  appType: 'webApp'
                  WebAppName: '***'
                  packageForLinux: '$(Pipeline.Workspace)/**/*.zip'

- stage: security_test
  dependsOn: deploy
  jobs:
  - job: security_test
    displayName: SecurityTest
    pool:
      vmImage: ubuntu-latest
    steps:
    - task: DockerInstaller@0
      inputs:
        dockerVersion: '17.09.0-ce'
    - task: Bash@3
      inputs:
        targetType: 'inline'
        script: |
          chmod -R 777  ./
          docker run --rm -v $(pwd):/zap/wrk/:rw -t owasp/zap2docker-stable zap-full-scan.py -t https://okawa-test-webapp.azurewebsites.net/ -j -g gen.conf -x OWASP-ZAP-Report.xml -r scan-report.html
          true
    - task: PowerShell@2
      inputs:
        targetType: 'inline'
        script: |
          $XslPath = "$($Env:SYSTEM_DEFAULTWORKINGDIRECTORY)/OWASPToNUnit3.xslt"
          $XslPath
          $XmlInputPath = "$($Env:SYSTEM_DEFAULTWORKINGDIRECTORY)/OWASP-ZAP-Report.xml"
          $XmlInputPath
          $XmlOutputPath = "$($Env:SYSTEM_DEFAULTWORKINGDIRECTORY)/Converted-OWASP-ZAP-Report.xml"
          $XmlOutputPath
          $XslTransform = New-Object System.Xml.Xsl.XslCompiledTransform
          $XslTransform.Load($XslPath)
          $XslTransform.Transform($XmlInputPath, $XmlOutputPath)
    - task: PublishTestResults@2
      inputs:
        testResultsFormat: 'NUnit'
        testResultsFiles: 'Converted-OWASP-ZAP-Report.xml'
        searchFolder: '$(System.DefaultWorkingDirectory)'
    - task: AzurePowerShell@5
      inputs:
        azureSubscription: '***'
        ScriptType: 'InlineScript'
        azurePowerShellVersion: latestVersion
        Inline: |
          $storage = Get-AzStorageAccount -ResourceGroupName "vse-sandbox" -Name "***"
          $ctx = $storage.Context
          $containerName = "zap-result"
          Set-AzStorageBlobContent -File "$($Env:SYSTEM_DEFAULTWORKINGDIRECTORY)/scan-report.html" -Container $containerName -Blob "scan-report.html" -Context $ctx

結果

AzureのCIレポートでテスト結果を確認できるように、レポート出力されたXMLファイルをNUnit形式に変換しています。

結果、下図のようにCIのレポートで発見された脆弱性のレポートを確認できるようになっている感じです。

f:id:TakasDev:20201230132000p:plain

HTMLで出力されたレポートを見たい場合はBLOBからですね。

f:id:TakasDev:20201230132611p:plain

Azure Artifactsに上げる場合のハマりどころ

今回はBLOBにあげてみましたが、参考サイトにある通りAzure Artifactsにあげようとした場合にハマったポイントがありました。

準備段階で、AzureArtifactsにaz artifacts universalを使用してArtifactsを作っているのですが

azコマンドからは403が出てしまいPublishできないといった現象がおきました。

vsts CLIを使用した場合にはエラーは発生しなかったので、azコマンドでエラーが発生した場合はvsts CLIを使用してみるといいかもしれません。

まとめ

今回は雑にCIに組み込めるか程度のレベルで試してみました。

AngularアプリのようなSPA構成のアプリの場合は単純なSpiderではなくAjaxスパイダーを使用したり

試行時間を決定しないと永遠に終わらなかったり…と考慮することは多そうなので

実際にしっかり運用するとなったら直接APIを叩いてガリガリ組んでいくしかないかなと思います。

と言っても、知らん間にガチ脆弱性チェックを行ってデータが壊れるのも色々嫌な感じですし

サーバーの設定レベルだけチェックするような現在の構成のほうが、自動実行するレベルとしては扱いやすいのかもしれないですね。

SubscriptionのLifecycleNotificationUrlをいじってみた

Subsriptionのライフサイクル通知が存在するようです。

サブスクリプションと変更通知の消失を減らす

Docsの履歴を見る感じだと機能自体は2020年の7月くらいに使用できるようになってたみたいなのですが

Microsoft Graphの変更ログに上がってきていなかったため気づくことができなかったようです。無念。

さて、その機能のおかげでSubscriptionの通知のライフサイクルの管理問題が解決することができるかも…?と思ったので試してみました。

(通知のライフサイクルが切れたり…な管理を楽にできれば良いなー。。。的な

詳細な使い方を見ていこうと思います。

ライフサイクル管理を行う

Subsriptionを作成する際にライフサイクル通知の通知先としてlifecycleNotificationUrlを指定。

この際、通常の変更通知でやるのと同様に、エンドポイントの検証を実装。

加えて、通常の通知先とライフサイクル通知の通知先は同じホスト名を指定する必要があるようです。

既存のSubscriptionにPATCHメソッドを使用してライフサイクル通知先を指定することはできないようなので

既存の変更通知がある場合は作成し直すしかなさそうです。

さて、例のごとくFunctionsに通知先を作成し、動作を確認してみようと思います。

ホスト名が違う状態を指定してみる

どんなエラー出るのか試したかったんですが…できちゃったんですよねぇ…

f:id:TakasDev:20201103121706p:plain

Functionsのログを見てみます。

f:id:TakasDev:20201103122727p:plain

ResourceNotifications(通常の通知先)は問題なく通知が来ているようですので別エンドポイントでもワンチャン動くかもしれません。

ひとまず以降の処理でおかしなことになるかもなので、同一のエンドポイントにしてから検証を続行していきます。

どんなときにLifecycle通知が発生するのか色々試してみる

有効期限後のログを見てみる

"expirationDateTime": "2020-11-03T03:30:00Z",を指定しているので12:30以降にどのようなログが出力されるか確認してみます。

  1. 変更通知作成
  2. 通知時間切れまでまつ
  3. 予定情報変更

有効期限後についてはLifecycle通知は行われませんでした。

有効期限切れも通知してくれたらexpirationDateTimeの管理も楽になると思ったのですが残念ですね。

手動でSubscriptionを削除してみる

手動で作成した変更通知を削除したあと、Lifecycle通知が捕捉できるか試してみます。

  1. 変更通知作成
  2. 1.で作成した変更通知を削除
  3. 予定情報変更

手動削除もLifecycle変更通知は捕捉できませんでした。

ユーザーのパスワードを変更/削除してみる

Documentには下記の条件のときのみに発生すると記載されています。

  • ユーザーのパスワードがリセットされた場合
  • ユーザーのデバイスが準拠しなくなった場合
  • ユーザーのアカウントが取り消された場合

意図しないSubscriptionの削除を補足する。といった機能のようですね。

そこでユーザーのパスワードを変更する方法で動作を検証してみます。

変更通知は下記の通りの内容で設定します。特定ユーザーの予定作成/変更時です。

このユーザーのパスワードをリセットを行うことでLifecycleNotificationが呼び出されるか検証します。

順序としては下記のとおりです。

  1. 変更通知作成
  2. 変更通知を作成したユーザーのパスワードリセット
  3. 予定情報変更

結果、下記のようなJSONがLifecycleNotification側に通知されました。

{
  "value": [
    {
      "subscriptionId": "6482c2c2-96ba-4505-b375-4329c1aad3d4",
      "subscriptionExpirationDateTime": "2020-11-24T16:00:00-08:00",
      "clientState": "<state>",
      "tenantId": "<tenantId>",
      "lifecycleEvent": "subscriptionRemoved"
    }
  ]
}

通常のNotification側への変更通知は発生せず、Lifecycleの通知だけ発生していることも確認できました。

f:id:TakasDev:20201123145731p:plain

Microsoft365側の都合による変更通知の削除によってLifecycle通知が発生することが確認できました。

通常通知先を潰してみる

通知先を潰してmissedを捕捉できるか試してみました。

AzureFunctionsに公開しているリソースエンドポイントをリネームして公開しなおしただけです。

ただ、これは捕捉できませんでした。

Document的には、Microsoft365側で配信されなかった変更通知があった場合に呼び出されるようです。

ただ、こちらは先のように具体的な例がなかったのでどのような状況の時に実行されるかはわかりませんでした。

  1. 通知先潰す
  2. Outlookイベント変更
  3. 復活させる
  4. Outlookイベント変更

としたら通知されてない2のデータがでてくる?と思ったけど出てきませんでした。

Microsoft365内で何かしら起きて変更通知の整合性が合わなくなったときに通知されるものかもですね。

まとめ

サブスクリプションと変更通知の消失を減らすで紹介されていた動作を見てきました。

動作させてみて分かった通り、Microsoft365側からのアクションで、動作しなくなった変更通知の通知ということがわかりました。

Microsoft365側からのアクションによる変更通知未実行というのは、補足しにくいものではあるので一定の効果はあると思います。

ただ、通知の有効期限切れや通知先の消失なんかの問題は変わらず自分で管理しないといけない。という状況です。

まだまだApplication Insightなどを使用したり、定期的に変更通知の状況を監視するといった工夫は必要そうです。

実験に使ったソースコードこちらです。

VisualStudioからAzure KeyVaultにアクセスするときにえらいハマった話

はじめに

今回の記事は下記の環境で検証を行っています。

記事を参照されるタイミングによっては画面構成や設定などが変わる可能性がありますのでご留意ください。

※以降Visual StudioはVSと記載しています

発生した問題

とあるアカウントを使用した場合、VSのConnected Serviceを使用してAzure Key Vaultにアクセスできない。

結論

16.7以降のVisualStudioからAzureに接続するための認証を行う場合、認証がうまく行かない場合があるようです。

Azureのリソースは認識できてるのに、肝心の接続のときはうまくいかない!

そんな場合はNuGetでAzure.Identityのバージョンを1.2.2以上に上げて、下記のいずれかを行うことでうまくいくようです。

  • launchSettings.jsonAZURE_TENANT_IDを指定する
  • new DefaultAzureCredential(new DefaultAzureCredentialOptions { VisualStudioTenantId = "<AzureのテナントID>" }))を指定する

どえらいハマってVSアンインストールとかして悔しい感じなので、以降、解決までの試行錯誤やらの過程の話です。

ConnectedServiceでAzure KeyVaultにアクセスできなくなった

VS16.7からVisualStudioのConnectedServiceを使用した際に構成される内容が変わりました。

開発時は基本的にユーザーシークレットを使用すると思うのですがちょっと使ってみたろと思ったのがすべての始まりでした。

ひとまず、Azure KeyVaultへの接続設定をVSから行ってみます。

f:id:TakasDev:20200905132659p:plain

f:id:TakasDev:20200829111829p:plain

構成されるライブラリだけで見ると👆な感じの変更内容のようです。

Azure.Identityを使用するような変更が行われたという感じですね。

さて、これで実行を行うとエラーとなり実行ができませんでした。

f:id:TakasDev:20200905132549p:plain

正常に実行できる環境もあり、いわゆるおま環事象といった感じです。

AzureADアプリケーションを調べる

MsalServiceException: AADSTS70002: The client does not exist or is not enabled for consumers. If you are the application developer, configure a new application through the App Registrations in the Azure Portal at https://go.microsoft.com/fwlink/?linkid=2083908.

エラー内容は上記のとおりです。指定されたAzureADアプリが存在しないようです。

なるほどー。というわけでAzureADアプリを作成し、KVのアクセスポリシーに割り当てます。

f:id:TakasDev:20200905133919p:plain

Program.csも下記のように作成したClientIdを使用するように書き換えました。

.ConfigureAppConfiguration((context, config) =>
{
    var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("VaultUri"));
        config.AddAzureKeyVault(
        keyVaultEndpoint,
        new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = "<作成したADアプリID>" }));
})

しかし結果は変わりませんでした。

別の認証が通るか試してみる

Azure SDK: What’s new in the Azure Identity August 2020 General Availability Releaseという記事でライブラリについて詳細にかかれています。

f:id:TakasDev:20200905132859p:plain

ひとまずVisualStudioの認証で失敗したけど、他の認証で成功してたらおkなのでは?と短絡に考え

Azure CLIでログイン状態にしてリトライしてみましたが結果は同じでした。

ADアプリの設定の問題かな?と思ったので正常に動作する側のAzureADアプリを見てみようと考えました。

認証フローを追う

設定を見るにも認証に使用されているAzureADのClientIDを見ないと始まりません。

そこでどのような認証が行われているのか正常に動作する環境を動かしながらFiddlerで追っかけてみました。

色々アレな情報が出てくるのでキャプチャは控えますが

ここで使用されているであろうClientIDの特定はできましたが、正常に動作する環境上でもAzureADアプリは見つかりませんでした。

で、あれば問題の根本はアプリが存在しないということはないだろうな。ということで別の線を当たることにしました。

テナントを指定する

先程記載したブログの記事でテナントの指定の仕方を記載した項目があります

authenticate-to-a-specific-tenant

僕のアカウントはAD/AD B2C含め複数のテナントに所属しており、且つオーナーとなっているテナントも複数あるのでHomeテナントを誤認したのかもしれません。

まずは👆記事に記載されている内容で解決できるか確認してみます。

Azure.Identityの1.1.1のDefaultAzureCredentialOptionsVisualStudioTenantIdは存在しないので

Azure.Identityの1.2.2に上げる必要があります。

.ConfigureAppConfiguration((context, config) =>
{
    var keyVaultEndpoint = new Uri(Environment.GetEnvironmentVariable("VaultUri"));
    config.AddAzureKeyVault(
        keyVaultEndpoint,
    new DefaultAzureCredential(new DefaultAzureCredentialOptions { VisualStudioTenantId = "<Azure Tenant Id>" }));
})

これでやっと動作するようになりました。

その後に試したこと

DefaultAzureCredentialOptionsを指定せずに解決できないか試しました。

VisualStudioのAzureサービス認証のアカウント絞り込み

VisualStudioの認証で使用しているアカウントに紐づくテナントを絞り込めば同じ状態になるかな?

と思い試してみましたが結果は変わりませんでした。

f:id:TakasDev:20200905142122p:plain

lauch.settings.jsonAZURE_TENANT_IDを指定する

この方法はうまくいきました。実行ソース側をいじるのが嫌であればこちらを指定するほうが良い感じですね。

おわりに

認証まわりで沼ると解決まで時間がかかるので

ローカルでの開発時はユーザーシークレットがド安定ですね😏