Maui Blazor Hybridで認証を追加する

### 目的
自分用のメモ
### 経緯
Maui Blazor Hybridで認証を調べるとBlazor Webアプリなどが出てきてなかなかHybridでのやり方が出てこなくて苦労したため。
### 対象
超初心者向け
VisualStudio2022　＋　.net8
基本的には
https://learn.microsoft.com/ja-jp/aspnet/core/blazor/hybrid/security/?view=aspnetcore-8.0&pivots=maui
の内容と同じです。

### 内容
ネットで調べるとスキャフォールディングする前提の情報だったり、認証ログイン画面の作成とセットになっていて認証機能を使うのに最低限必要な部分がどこなのかがわからなかったりしたのでここでは本当に最低限の話をまとめておきたいと思う。
もしかすると名前空間が足りてなくてエラーになるかもしれないが、その時は随時参照先を追加してほしい。
#### 必要なライブラリ
Microsoft.AspNetCore.Components.Authorization
これをNuGetでインストールする。
#### 必要なコード
AuthenticationStateProviderを継承したクラスを作成する。
名前はなんでもよいがとりあえずMyAuthStateProviderとした。
このクラスが認証OK、NGの判定をする。DBと照合するなり、外部の認証サーバーに聞きに行くなりは自分でつくることになる。(下記ではユーザー名testでadminとuserのロールを持つユーザーとしている）
どうやらClaimsIdentityを使ってClaimsPrincipalを生成すると認証OKという扱いになるらしい。認証NGの場合はClaimsPrincipalの引数を空にすればいいみたい。最後の文字列引数はよく理解できていない。とりあえずMyAuthとしておく。ログインページからどうやって入力された情報を持ってくるのかという疑問はあるかもしれないがこれはHybridでなくても出てきそうなので後回しにした。

```MyAuthStateProvider.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;


namespace MauiAuthApp.Shared
{
	public class MyAuthStateProvider : AuthenticationStateProvider
	{
		private readonly Task<AuthenticationState> authenticationState;

		public MyAuthStateProvider(AuthenticatedUser user) =>
			authenticationState = Task.FromResult(new AuthenticationState(user.Principal));

		public override Task<AuthenticationState> GetAuthenticationStateAsync()
		{
            var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
                {
                    new Claim(ClaimTypes.Name, "test"),
                    new Claim(ClaimTypes.Role, "admin"),
                    new Claim(ClaimTypes.Role, "user")
                }, "MyAuth"));
            return Task.FromResult(new AuthenticationState(claimsPrincipal));
        }
    }

	public class AuthenticatedUser
	{
		public ClaimsPrincipal Principal { get; set; } = new();
	}
}



```
つぎにMauiProgram.csを書き換える。
ヘッダ部に
``` MauiProgram.cs ヘッダ部
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
using MyAuthStateProviderの場所
```
を追加。MyAuthStateProviderの場所は自分の環境に合わせる。
次にMauiProgram.csの修正
まず元からあるリターン部分をコメントアウト
```MauiProgram.cs 初期化部（CreateMauiApp）
 //			return builder.Build();
```
次に下記のコードを追加
MyAuthStateProviderを認証に使うという登録だと思う。
```MauiProgram.cs 初期化部（CreateMauiApp）
builder.Services.AddAuthorizationCore();
builder.Services.TryAddScoped<AuthenticationStateProvider, MyAuthStateProvider>();
builder.Services.AddSingleton<AuthenticatedUser>();
var host = builder.Build();
return host;
```
ここまででプログラムを実行するとtestというユーザーで認証されていることになっている。
#### 認証状態の使い方
認証状態の使い方はルートでする方法と、表示時にする方法がある。
##### ルートでする方法
これは例えば認証するまではログイン画面だけを表示したいというような場合に使う。
###### ルートの編集
タグの参照先を追加
```_Imports.razor
@using Microsoft.AspNetCore.Components.Authorization;
@using Microsoft.AspNetCore.Authorization;
@using System.Security.Claims;
@using Microsoft.Extensions.DependencyInjection.Extensions;
```
Components\Routes.razorを編集する。
```Routes.razor
@using Microsoft.AspNetCore.Authorization;
@inject NavigationManager Navigation;

<CascadingAuthenticationState>
<Router AppAssembly="@typeof(MauiProgram).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" >
            <NotAuthorized>
                    @{
                    Navigation.NavigateTo("/Login", false);
                    }
                </NotAuthorized>
        </AuthorizeRouteView>
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(Layout.MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>
</CascadingAuthenticationState>
```
上記は初期状態のRoutes.razorに変更を加えたものだが、ようするに全体を`<CascadingAuthenticationState>`で囲い、Foundの中の`RouteView`を`AuthorizeRouteView`に変更する。
これでその中で`<NotAuthorized>`タグが使えるようになる。上記の例ではログイン前はログイン画面に飛ばすようにしている。
###### ページを認証必須に設定する
実は上記だけでは未ログイン時に`<NotAuthorized>`タグ内が実行されない。認証必須としたいページの先頭に`@attribute [Authorize]`を追加する必要がある。。

##### ページ内でする方法
ページ内で認証状態によって処理を変えるには`<AuthorizeView>`タグを使用する。Contextで指定した文字列は変数として使って認証ユーザーの情報を取得できる。
例えば下記はログイン時にユーザー名を表示する。当然だが@attribute [Authorize]を使ってログインページに飛ばしているページの`<NotAuthorized>`タグ内の表示を見ることはない（はず）
```
<AuthorizeView Context="AuthorizeContext">
    <Authorized>
        You are logged in. user name is @AuthorizeContext!.User.Identity.Name
    </Authorized>
    <NotAuthorized>
        You are not log in.
    </NotAuthorized>
</AuthorizeView>
```
##### 役割を追加した場合
認証時にRolesを追加している場合は下記のように設定されている役割によって表示内容を変えることも可能
```
<AuthorizeView Roles="user" Context="AuthorizeContext">
<Authorized>
        You are logged in as user. user name is @AuthorizeContext!.User.Identity.Name
</Authorized>
<NotAuthorized>
    You are not log in as user.
</NotAuthorized>
</AuthorizeView>
```
他にもいろいろタグがありそうだが、自分的にはとりあえずこれで当面しのげると思う。