BEACHSIDE BLOG

Azure と GitHub と C# が好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

Azure Static Web Apps を Azure AD B2C で認証

Azure Static Web Apps での認証は、以前は Azure AD, Apple, Facebook, GitHub, Google, Twitter の設定はできました (Apple は新しい気がするな)。

そして2021年5月の GA の頃に OpenID Connect をサポートしてる IdP なら認証の設定ができるようになりました。OIDC サポートの IdP といえば、Auth0, Okta, AWS の Cognito や LINE とかぱっと浮かぶところでしょうか。

そして Azure AD B2C もですね。

今回は知名度も人気もいまだまだ低いであろう Azure AD B2C での設定方法をやっていきましょう。

ちなみに B2C 以外のOIDC プロバイダーを使った場合でも、SWA の設定やコードのは後半の SWA の認証設定を見れば、応用して設定できるはずです。

SWA の準備

SWA 作成における注意点

SWA のリソース作成時に最も重要な注意点として、今回は Custom Authentication の機能を使うため、Standard プランにする必要があります。 Free プランだと動きません (404 になる)。Plan による機能の違いは現時点ではざっくり以下図ですが、必要に応じて最新の情報をドキュメントで確認しましょう。

SWA のリソース作成

上記で書いたStandard プランの設定に注意点を考慮しつつ (←私がハマったので何度も書く...)、事前に SWA (Static Web Apps) のリソースを Azure 上に作っておきましょう。というのはこの後 Azure AD B2C をセットアップする際に SWA の URL が必要になるからです。

f:id:beachside:20210804173853p:plain

初めてやるって方は、以下のチュートリアルにあるようにコードをクローンして Azure にデプロイするまでをやってみると簡単です。クローンするコードも Vue や React のテンプレートが選べていい感じです。今回私は Vue のテンプレートで作って話を進めますが、コードは数行程度しか書かないのでほかのテンプレートでやっても問題ないです。

クイック スタート:Azure portal で Azure Static Web Apps を使用して静的 Web アプリを初めてビルドする | Microsoft Docs

チュートリアルをやったりして SWA のリソースを作ったら Azure ポータルで SWA のリソースを開き URL をメモしておきましょう。

f:id:beachside:20210521133722p:plain

Azure AD B2C の設定

B2C の設定しながら"3つ"の値をメモして、次の SWA の設定につなげていきます。

テナントの作成

Azure AD B2C のテナントがない場合はテナントの作成からですね。これは Azure Portal からポチポチやるだけなのでドキュメントのリンクを貼っておくだけとします。

docs.microsoft.com

アプリケーションの設定

アプリケーションの登録

B2Cとは」とか「B2Cのアプリケーションとは」とかの話すと長くなるので一切せずガンガン進みます。

Azure ポータルで Azure AD B2C のリソースを開き アプリの登録 (①) > 新規登録 (②) をクリックします。

f:id:beachside:20210521134519p:plain:w720

アプリケーションの登録は以下図のように設定します。四角でかこってない部分はデフォルトでよいです。名前は適当につけときます。

リダイレクト URI が重要なポイントの一つです。
前述でメモした SWA の URL の後ろに /.auth/login/b2c/callback をつけた値を入力します。例えば SWA の URL が https://abcd1234.azurestaticapps.net/ だったら以下のようになります。

https://abcd1234.azurestaticapps.net/.auth/login/b2c/callback

f:id:beachside:20210521152022p:plain:w720

リダイレクト URI の値は公式ドキュメントにある通りにセットする感じですが、"/.auth/login.." を見ると、App Service Authentication の機能を使ったことがある方だとそっちの機能ねーと感じることがあるかもしれませんね。
/.auth/login/b2c/callback の "/callback" も前についてる "b2c" という値は、今回は私自身が勝手に決めたものでカスタム可能な要素です。あとで SWA で staticwebapp.config.json を構成する際に設定する値と合わせる必要があるってことだけ覚えておくとよいです。

登録ボタンをクリックしましょう。登録したアプリケーションのメニューになります。

認証 をクリックすると先ほど入力した内容が確認できます。フロントチャネルのログアウト URL を入力しておきましょう。値は、前述でメモした SWA の URL の後ろに /.auth/logout をつけた値を入力します。入力したら画面上部の保存ボタンを忘れずクリックしましょう。

f:id:beachside:20210521151748p:plain:w720

シークレットの登録

次に 証明書とシークレット をクリックし (①) 、新しいシークレット をクリックして ます(②) 。シークレットの作成画面がでますので、作成しましょう。有効期限は適宜決めていただければよいです。余談として期限が切れるともちろん無効になりますが、期限が切れる前にメール通知がきます。

作成したシークレットは後から値の確認ができませんので、メモしておきましょう。メモすべき1つ目の値です。

f:id:beachside:20210521140351p:plain:w720

アプリのクライアントID の確認

ここでこの B2C のアプリのクライアント ID を確認しておきます。概要 をクリックすると表示されますのでメモしておきます。 これは SWA で staticwebapp.config.json を構成するのに必要になります。これがメモすべき2つ目の値です。

f:id:beachside:20210521140945p:plain:w600

ユーザーフローの設定

ユーザーフローの作成

B2C のメニューは慣れないとわかりにくいのですが、今開いてるのが B2C のアプリのメニューなので、B2C 自体のメニューに戻るために画面上部の Azure AD B2C をクリックします。

f:id:beachside:20210521141519p:plain:w600

ユーザーフロー ついては詳しく語りませんが進めていきましょう。

ユーザーフロー をクリックし (①)、新しいユーザーフローをクリックします (②) 。

f:id:beachside:20210521141640p:plain:w600

ユーザーフローのタイプは サインアップとサインイン を選択します。バージョンを選べますので、推奨 のやつを選び、画面下部の作成ボタンをクリックします。

f:id:beachside:20210521141952p:plain:w600

作成の画面を設定していきましょう。

  • 名前: ドキュメントでも susi とか入ってる例が多いですが、これは寿司が好きなわけではなく "Sign Up Sign In" の略として使わています。
  • ローカルアカウント: Email signup にチェックを入れておきます。これで Email でサインアップできるようになります。ここの設定次第では GitHub や Facebook といったソーシャル ID プロバイダーでサインアップやサインインさせることも可能な便利な B2C なんですが、今回は EMail のみにします。

f:id:beachside:20210521142222p:plain:w600

画面を下にスクロールするとユーザー属性とトークン要求 があります。詳細を表示 をクリックします。

f:id:beachside:20210521142706p:plain:w600

作成画面が表示されます。ここで表示名のチェックボックス2つにチェックを入れておきましょう。

今回は B2C の細かい話はするつもりはないのですが、

  • 表示名属性を収集する のチェックをつけるとサインアップ時にその情報を入力できるようになります。表示名 できれば認証後に入力した表示名が取得できるようになります。
  • 表示名要求を返すのチェックは必須です。この後 SWA でこのトークンを読み取る設定をするのでトークンがないとエラーになるためです。

この画面の OK ボタンをクリックし、前の画面に戻ったら 作成ボタンをクリックして作成を完了させます。

f:id:beachside:20210521162419p:plain:w480

metadata の URL を確認

ユーザーフローが作成出来たらそのフローをクリックします。

f:id:beachside:20210521143549p:plain

ユーザーフローを実行します をクリックすると、 OpenID Connect metadata 情報の URL が出てきます。これもあとで使うのでメモしておきましょう。メモすべき3つ目の値です。

f:id:beachside:20210521143923p:plain

ここまでで、

  • クライアント ID
  • シークレット
  • metadata の URL

の3つをメモしました。これを使って残りの設定を進めていきましょう。

SWA の認証設定

SWA の環境変数をセット

まずは、先ほどメモした値のうちクライアント ID とシークレットを環境変数にセットしていきましょう。

Azure ポータルで SWA のリソースを開き、構成 をクリックし (①) 、追加をクリックします (②) 。

f:id:beachside:20210521145141p:plain

前述でメモしたクライアント ID とシークレットの2つをセットします。

今回クライアント ID の名前は B2C_CLIENT_ID、シークレットの名前は B2C_SECRET とつけて値をセットしました。こんな感じです。

f:id:beachside:20210804172125p:plain

プログラムを変更 1: staticwebapp.config.json

さて、ようやくプログラムをいじるときがきました(たいしたことしませんが)。

まず、プロジェクトのルートに staticwebapp.config.json というファイルがなければ追加します。

あとは、以下の json を追加します。

{
    "auth": {
        "identityProviders": {
            "customOpenIdConnectProviders": {
                "b2c": {
                    "registration": {
                        "clientIdSettingName": "B2C_CLIENT_ID",
                        "clientCredential": {
                            "clientSecretSettingName": "B2C_SECRET"
                        },
                        "openIdConnectConfiguration": {
                            "wellKnownOpenIdConfiguration": ""
                        }
                    },
                    "login": {
                        "nameClaimType": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
                        "scopes": ["openid", "profile"]
                    }
                }
            }
        }
    }
}

wellKnownOpenIdConfiguration の値が空にしてますので、ここに B2C の設定でメモしたmetadata の URL を貼り付けます。

ちょっとだけ解説しておくと、

  • customOpenIdConnectProviders の下に b2c ってあります。これは自分で定義した値で、B2C のアプリのリダイレクト URI の後ろの方の /.auth/login/b2c/callback の login の後ろの値と合わせることで、この json の情報を読み取る仕組みになってます。ほかの名称を付けるときはこの部分とリダイレクト URL の部分の値を合わせて設定してあげましょう。
  • clientIdSettingNameclientSecretSettingName : ここは実際の値ではなく環境変数のキーをセットする必要がるので注意が必要です。今回だと、先ほど Azure の SWA の環境変数をセットしたときの名前をセットしています。
  • nameClaimType: これをセットしているため、B2C のユーザーフローの ユーザー属性とトークン要求表示名 の設定をしました。B2C 側で設定を忘れるとトークンにこれが含まれていないためエラーになります。

この json の設定のドキュメントの記載は以下になります。

https://docs.microsoft.com/ja-jp/azure/static-web-apps/authentication-custom?tabs=aad#register-your-application-with-the-identity-provider

ただ、2021年5月21日時点だと、この公式ドキュメントの Json に誤りがあるためドキュメント通りだと動かず、このブログの Json で動作します。
今後どう更新されるか(ドキュメントを直すのかまたは SWA の内部を直すのか?)で動向がかわるためチェックしていく必要がありそうです。変更の仕方次第ではこのブログでやってる現時点での正しい方法が動かなくなることもありそうですね。

正しいスキーマについては、JSON Schema StoreAzure Static Web Apps configuration file を開くと確認が可能なので、しばやんの以下リンクのブログの「staticwebapp.config.json の設定ミスを減らす」って部分で書かれているように設定すると、入力補完がきかせてミスを減らせます。これは必ずやった方がよいですね。

blog.shibayan.jp

プログラムを変更2: リンクをつけてみる

私の場合は Vue のテンプレートでプロジェクトを作成したので、App.vue を開いて 公式ドキュメントを参考にこんな変更を加えてみました。リンクつけずに手で URL をたたいてもいいんですがねw。

  • login の url をたたくリンクをつけました。 url は /.auth/login/b2c です。
  • login したら自分の情報を確認するためのリンクをつけました。url は /.auth/me です。
<template>
  <div>
    Hello {{ value }}
    <div>
      <a href="/.auth/login/b2c">Login</a>
    </div>
    <div>
      <a href="/.auth/me">My Info</a>
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      value: "World",
    };
  },
};
</script>

では、実際に認証を動作するには、App Service の認証機能をつかっているため Azure のリソースにデプロイする必要があります。コードを GitHub の repo に push すると自動で CI/CD が動作します。main ブランチは Pull Request 必須がついてるので、PR 作って merge する必要ありますが、そこの説明は省きます。

デプロイできたら動作確認してみましょう。

動作確認

動作確認は、Firefox ならプライベートウインドウ、Microsoft Edge なら InPrivate とかでやった方が無難です。私は Chrome なのでシークレットウインドウを開いて、SWA の URL を開いてみます。

シンプルで味気の無い画面が表示されます。

f:id:beachside:20210521155310p:plain

My Info をクリックすると、認証してないので clientPrincipal の値がありません。リンクを作ってない場合は .auth/me をたたけば見れます。

f:id:beachside:20210521155229p:plain

Login してみましょう。設定が間違っていなければ Azure AD B2C のログイン画面が表示されます。はじめての場合は、下の方の Sign up now をクリックしてサインアップできます。

f:id:beachside:20210521155445p:plain

サインアップのフローではデフォルトでメールアドレスの verification がついてるので、メールアドレスを入力したら Send verification code をクリックしてそのメールに送られきたコードを入力してverification する必要があります。

f:id:beachside:20210521163412p:plain

設定ミスエラーとか起こして何度かトライしててユーザーができてんのか不明な場合は Azure AD B2C のユーザーのメニューをみると確認できます。

B2C の余談として、このサインイン画面はそこそこ自在にカスタマイズできます。

サインインすると、トップ画面に戻ります。My Info を見るとログイン前に null だった情報が取得できているが確認できます。

f:id:beachside:20210521162836p:plain

ログアウトしたい場合は、./auth/logout を手入力すればできます(リンクつければよかったか)

トラブルシュート

認証でエラーになった場合は、設定するポイントは数個ですが値のミスによることがほとんどだと思うので、ひとつずつ確認していきましょう。場合によってはブラウザの DevTools でネットワークをみると URL のパラメーターでエラーの内容がわかることもあるので、ネットワークの遷移を見て確認するのもひとつの手です。

ありがちなエラーと改善方法を書いておきます。

ログインの URL で 404

B2Cの設定は正しいのにログインしようとして B2C の画面に遷移せず404 のエラーが出るときは、Hosting Plan が Free になっているからかもしれません。冒頭でも書きましたが、Custom Authentication を利用するには Standard plan じゃないと動かないのでチェックしましょう。

認証後に 403 Forbidden

ログイン画面が表示してログインはできたっぽいのに .auth/complete の画面が表示されて 403 Forbidden ...この原因のひとつとして、トークンで必要な値を読み込めなくてしんでる可能性があります。前述の ユーザーフローの作成表示名 の設定を紹介してますが、それをしてないときにはなります。または、metadata の URL の設定 (staticwebapp.config.json でのwellKnownOpenIdConfiguration の設定)で、URL の最後にユーザーフロー名がついているんですが、そこを確認してみるとうっかり間違っているなんてこともあるでしょうか。

参考

docs.microsoft.com