Azure Functions などのコンピュートリソースを用意しないで、直接 Blob Storage にファイルをアップロードする方法は無いものか、と考えていたのですが、Microsoft Entra ID で認可できればいいみたいでした。
別解として SAS トークンを利用した認可もありますが、どうやら Entra ID が推奨みたいです。
というわけで、今回は Microsoft Entra ID で認可して Blob Storage にファイルをアップロードするところまでやってみます。
構成
Microsoft Entra ID での認証自体は Remix で作った Web アプリを経由させ、Blob Storage へのアップロードはブラウザから直接行うようにします。登場人物をまとめた図は以下です。
Microsoft Entra 管理センター
Microsoft Entra 管理センターにて、アプリの登録を行います。アプリの登録ではアプリを一意に示す ID とアプリ自体の認証情報を作成します。
手順は以下を参考に。
アプリの登録が完了すると、概要ペインからクライアント ID とテナント ID を取得できます。
今回は Web アプリ (機密クライアント) を選択したので、クライアントシークレットを生成します。
ログイン正常完了時にトークンを送る先をリダイレクト URI として追加します。
ストレージアカウント
Microsoft Entra ID で認証されたユーザーに Blob Storage の操作を許可します。これを実現するには、ロールベースのアクセス制御である Azure RBAC を利用します。
まずは Azure portal から任意のストレージアカウントとコンテナーを作成します。作成できたら、アクセス制御 (IAM) ペインから 追加タブ > ロールの割り当ての追加 を選択します。
データ共同作業者を選択して次へを選択します。
許可するユーザーを追加してレビューと割り当てを選択します。
Remix アプリ
Remix で作ったものは GitHub にアップしてます。
以降はポイントのみを説明していきます。
ライブラリ
Remix で Microsoft Entra ID を使った認証と Blob Storage の操作を行うのに以下のライブラリを利用しています。
- remix-auth: 3.7.0
- remix-auth-microsoft: 2.0.1
- remix-auth-oauth2: 1.11.0
- @azure-storage-blob: 12.26.0
環境変数
.env
を作成し、取得した ID とクライアントシークレット、ストレージアカウント名などを記載します。
ENTRA_CLIENT_ID={クライアントID}
ENTRA_CLIENT_SECRET={クライアントシークレット}
ENTRA_REDIRECT_URI=http://localhost:5173/auth/microsoft/callback
ENTRA_TENANT_ID={テナントID}
AZURE_STORAGE_ACCCOUNT={ストレージアカウント名}
AZURE_BLOB_CONTAINER={コンテナー名}
認証・認可
remix-auth-microsoft
では MicrosoftStrategy が用意されているので、それを利用した Authenticator を作っています。
ここでは、scope に Azure Storage のアクセストークンの要求を追加しています。
let microsoftStrategy = new MicrosoftStrategy(
{
clientId: process.env.ENTRA_CLIENT_ID || "",
clientSecret: process.env.ENTRA_CLIENT_SECRET || "",
redirectUri: process.env.ENTRA_REDIRECT_URI || "",
tenantId: process.env.ENTRA_TENANT_ID || "",
scope: [
"openid",
"email",
"profile",
"https://storage.azure.com/user_impersonation",
], // optional
prompt: "login", // optional
},
取得したアクセストークンはセッション情報に書き出されるので、loader でフロントエンドに渡しています。
export async function loader({ request }: LoaderFunctionArgs) {
const user = await authenticator.isAuthenticated(request);
if (!user) {
throw new Response("Unauthorized", { status: 403 });
}
const accessToken = user.accessToken;
const storageAccount = process.env.AZURE_STORAGE_ACCCOUNT;
const blobContainer = process.env.AZURE_BLOB_CONTAINER
return { accessToken, storageAccount, blobContainer };
}
アクセストークンは @azure-storage-blob
が提供するクライアントの生成に利用しています。
const blobServiceClient = createBlobServiceClient(
accessToken,
storageAccount,
);
const containerClient = blobServiceClient.getContainerClient(blobContainer);
const blockBlobClient = containerClient.getBlockBlobClient(file.name);
try {
const response = await blockBlobClient.uploadBrowserData(file, {
blobHTTPHeaders: { blobContentType: file.type },
});
console.log("upload succeeded", response.requestId);
alert("ファイルアップロード完了");
} catch (error) {
console.error("upload failed", error);
alert("ファイルアップロード失敗");
}
動作確認
アプリを起動します。
# npm run dev
ブラウザで http://localhost:5173/login
にアクセスします。
ログインボタンを押下すると、Microsoft のサインイン画面にリダイレクトされます。
ストレージアカウントで許可したユーザーの認証情報を入力してログインに成功すると、ダッシュボード画面にリダイレクトします。初回ログイン時にのみ、Azure Storage へのアクセス許可を聞かれます。
任意のファイルを選択して Upload ボタンを押下します。ファイルのアップロードが成功するとメッセージが表示されます。
Blob コンテナーを見てみると、ファイルがアップロードされていることが確認できます。
おわりに
普段、Azure や Entra ID を利用する機会が無いので、難易度が高かったです。次は SAS トークンを試してみようと思います。