Status | Site URL |
---|---|
https://maremare.github.io/try-blazorwasm-standalone-singleOrg | |
https://polite-moss-0d2a72510.2.azurestaticapps.net | |
https://try-blazorwasm-standalone-singleorg.pages.dev |
GitHub Pages あるいは Azure Static Web Apps へ発行し、実際にサイトへアクセスすると次のエラーが表示される。
There was an error trying to log you in: 'Cannot read properties of undefined (reading 'toLowerCase')'
対処方法は *.csproj に以下を追加すれば良いらしい。
<ItemGroup>
<TrimmerRootAssembly Include="Microsoft.Authentication.WebAssembly.Msal" />
</ItemGroup>
詳細は以下:
デプロイしたサイトへアクセスすると次のエラーが表示される。
There was an error trying to log you in: '"undefined" is not valid JSON'
対処方法は *.csproj に以下を追加すれば良いらしい。
<ItemGroup>
<TrimmerRootAssembly Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" />
</ItemGroup>
詳細は以下:
ユーザシークレットには以下のキーでクライアントシークレットを設定しているので、発行先の環境変数へ追加する必要がある。
{
"AzureAD:ClientSecret": "<<Client Secret>>"
}
無料版ではダメだった:
Azure AD 認証と承認のエラー コード - Microsoft Entra | Microsoft Learn
- AADSTS90043
NationalCloudAuthCodeRedirection - 機能が無効になっています。
もしかして無料のホスティングプランだと設定できない?
以下が参考になりそう…
-
Azure Static Web Apps の認証と承認 | Microsoft Learn (マネージド認証)
1 構成済みの Azure Active Directory プロバイダーは、Microsoft アカウントでサインインを許可します。
特定の Active Directory テナントにログインを制限するには、[カスタム Azure Active Directory プロバイダー] を構成します。
-
Azure Static Web Apps でのカスタム認証 | Microsoft Learn (カスタム認証)
カスタム認証は、Azure Static Web Apps Standard プランでのみ使用できます。
-
Azure Static Web AppsのアプリにAzure ADカスタム認証機能を追加
Azure AD のどのテナントでもログインできてしまいます。
やりたいことは「カスタム認証」が適している。が、しかし無料が良かった… ここで断念。
Deploy a Blazor Site · Cloudflare Pages docs
- ビルドの構成
- ビルドコマンド
curl -sSL https://dot.net/v1/dotnet-install.sh > dotnet-install.sh; chmod +x dotnet-install.sh; ./dotnet-install.sh -c 8.0 -InstallDir ./dotnet8; ./dotnet8/dotnet --version; ./dotnet8/dotnet workload install wasm-tools; ./dotnet8/dotnet publish "src/blazorwasm-standalone-singleOrg" -c Release -o output; rm $(ls output/wwwroot/_framework/*.wasm);
- ビルド出力ディレクトリ
/output/wwwroot
- ビルドコマンド
Microsoft.Graph Version="4.54.0" → Microsoft.Graph Version="5.1.0" は破壊的変更がある模様。
- Microsoft Graph .NET SDK v5 changelog and upgrade guide
- Upgrade 4.50 sdk to 5.0 - Reference to type IAuthenticationProvider claims it is defined in Microsoft.Graph.Core, but cannot be found · Issue #1695 · microsoftgraph/msgraph-sdk-dotnet
- AzureAD/microsoft-identity-web#2097
- microsoftgraph/msgraph-sample-aspnet-core#84
参考:
GraphClientExtensions.cs:
using Microsoft.AspNetCore.Components.WebAssembly.Authentication;
using Microsoft.Authentication.WebAssembly.Msal.Models;
using Microsoft.Graph;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Kiota.Abstractions;
using Microsoft.Kiota.Abstractions.Authentication;
using IAccessTokenProvider = Microsoft.AspNetCore.Components.WebAssembly.Authentication.IAccessTokenProvider;
/// <summary>
/// Adds services and implements methods to use Microsoft Graph SDK.
/// </summary>
internal static class GraphClientExtensions
{
public static IServiceCollection AddGraphClient(this IServiceCollection services, string? baseUrl, List<string>? scopes)
{
if (string.IsNullOrEmpty(baseUrl) || scopes.IsNullOrEmpty())
{
return services;
}
services.Configure<RemoteAuthenticationOptions<MsalProviderOptions>>(
options =>
{
scopes?.ForEach(scope =>
{
options.ProviderOptions.DefaultAccessTokenScopes.Add(scope);
});
});
services.AddScoped<IAuthenticationProvider, GraphAuthenticationProvider>();
services.AddScoped(
sp =>
new GraphServiceClient(
new HttpClient(),
sp.GetRequiredService<IAuthenticationProvider>(),
baseUrl));
return services;
}
/// <summary>
/// Implements IAuthenticationProvider interface.
/// Tries to get an access token for Microsoft Graph.
/// </summary>
private class GraphAuthenticationProvider : IAuthenticationProvider
{
private readonly IConfiguration _config;
public GraphAuthenticationProvider(IAccessTokenProvider tokenProvider, IConfiguration config)
{
this.TokenProvider = tokenProvider;
this._config = config;
}
public IAccessTokenProvider TokenProvider { get; }
public async Task AuthenticateRequestAsync(
RequestInformation request,
Dictionary<string, object>? additionalAuthenticationContext = null,
CancellationToken cancellationToken = default)
{
var result = await this.TokenProvider.RequestAccessToken(
new AccessTokenRequestOptions
{
Scopes = this._config.GetSection("MicrosoftGraph:Scopes").Get<string[]>(),
});
if (result.TryGetToken(out var token))
{
request.Headers.Add("Authorization", $"{CoreConstants.Headers.Bearer} {token.Value}");
}
}
}
}
Program.cs:
var baseUrl = builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"];
var scopes = builder.Configuration.GetSection("MicrosoftGraph:Scopes").Get<List<string>>();
builder.Services.AddGraphClient(baseUrl, scopes);
appsettings.json:
"MicrosoftGraph": {
"BaseUrl": "https://graph.microsoft.com/v1.0",
"Scopes": [ "user.read" ]
}
詳細:
@inject NavigationManager Navigation
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@code {
protected override void OnInitialized()
{
Navigation.NavigateTo(
$"authentication/login?returnUrl={Uri.EscapeDataString(Navigation.Uri)}");
}
}
👇
@inject NavigationManager Navigation
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Microsoft.Extensions.Options
@inject IOptionsSnapshot<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>> Options
@code {
protected override void OnInitialized()
{
Navigation.NavigateToLogin(Options.Get(Microsoft.Extensions.Options.Options.DefaultName).AuthenticationPaths.LogInPath);
}
}
dotnet publish .\src\blazorwasm-standalone-singleOrg\ -c release -o output
rm output/wwwroot/_framework/Microsoft.Graph.wasm
dotnet serve -o -S -p:7117 -b -d:.\output\wwwroot
- ASP.NET Core Blazor WebAssembly のホストと展開 | Microsoft Learn
wwwroot\js\decode.js
/wwwroot\js\decode.min.js
- google/brotli · GitHub から
decode.js
とdecode.min.js
をダウンロード wwwroot\js
へコピー
- google/brotli · GitHub から
*.csproj
<PropertyGroup> <BlazorEnableCompression>true</BlazorEnableCompression> </PropertyGroup> <ItemGroup> <Content Update="wwwroot\js\decode.js"> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> </ItemGroup>
wwwroot\index.html
<body> <!-- 👇 ここから --> <!-- NOTE: https://learn.microsoft.com/ja-jp/aspnet/core/blazor/host-and-deploy/webassembly?view=aspnetcore-8.0#compression --> <script src="_framework/blazor.webassembly.js" autostart="false"></script> <script type="module"> import { BrotliDecode } from './js/decode.min.js'; Blazor.start({ loadBootResource: function (type, name, defaultUri, integrity) { if (type !== 'dotnetjs' && location.hostname !== 'localhost' && type !== 'configuration') { return (async function () { const response = await fetch(defaultUri + '.br', { cache: 'no-cache' }); if (!response.ok) { throw new Error(response.statusText); } const originalResponseBuffer = await response.arrayBuffer(); const originalResponseArray = new Int8Array(originalResponseBuffer); const decompressedResponseArray = BrotliDecode(originalResponseArray); const contentType = type === 'dotnetwasm' ? 'application/wasm' : 'application/octet-stream'; return new Response(decompressedResponseArray, { headers: { 'content-type': contentType } }); })(); } } }); </script> <!-- 👆 ここまで --> </body>