<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: garicchi</title>
    <description>The latest articles on DEV Community by garicchi (@garicchi).</description>
    <link>https://dev.to/garicchi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F61737%2F092b2b59-4b59-4076-a30f-1982931d0202.jpg</url>
      <title>DEV Community: garicchi</title>
      <link>https://dev.to/garicchi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/garicchi"/>
    <language>en</language>
    <item>
      <title>ASP.Net+Swagger+TypeScriptでEnumの表示名を自動生成する</title>
      <dc:creator>garicchi</dc:creator>
      <pubDate>Thu, 25 Jan 2024 08:53:11 +0000</pubDate>
      <link>https://dev.to/garicchi/aspnetswaggertypescriptdeenumnobiao-shi-ming-wozi-dong-sheng-cheng-suru-1hoh</link>
      <guid>https://dev.to/garicchi/aspnetswaggertypescriptdeenumnobiao-shi-ming-wozi-dong-sheng-cheng-suru-1hoh</guid>
      <description>&lt;p&gt;ASP.Netは&lt;a href="https://learn.microsoft.com/ja-jp/aspnet/core/tutorials/getting-started-with-swashbuckle?view=aspnetcore-7.0&amp;amp;viewFallbackFrom=aspnetcore-8.0&amp;amp;tabs=visual-studio"&gt;Swaggerをサポートしており&lt;/a&gt;、Controllerを書くだけでopenapi schemaが自動生成されて便利です。&lt;/p&gt;

&lt;p&gt;そして自動生成されたopenapi schemaから、openapi-generatorなどのツールを利用して、TypeScriptのコードを自動生成すれば、Single Page Applicationが作りやすくなります。&lt;/p&gt;

&lt;h2&gt;
  
  
  レスポンスのPayloadにEnum型があった場合
&lt;/h2&gt;

&lt;p&gt;しかし、レスポンスのPayloadにEnum型のプロパティがあった場合、あまり良いコードが得られません。&lt;/p&gt;

&lt;p&gt;例えば、こんなC#のenumがあったとして、&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;WeatherType&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Sunny&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Cloudy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Rainy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;普通にopenapi-generatorで自動生成すると、以下のTypeScriptのコードが得られます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;NUMBER_0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;NUMBER_1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;NUMBER_2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;EnumのメンバーがNUMBER_{数字} になってわかりにくいです。&lt;/p&gt;

&lt;h2&gt;
  
  
  x-enum-varnames
&lt;/h2&gt;

&lt;p&gt;メンバーのキー名を変更するには、openapi schemaにx-enum-varnamesという名前で配列を入れてやればよいです。&lt;br&gt;
&lt;a href="https://github.com/OpenAPITools/openapi-generator/issues/893#issuecomment-416617460"&gt;https://github.com/OpenAPITools/openapi-generator/issues/893#issuecomment-416617460&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ASP.NetのSwaggerジェネレータ (今回はSwashbuckle)の場合、SchemaFilterという機能があり、これを使えば、出力されるopenapi schemaをいじることができます。&lt;/p&gt;

&lt;p&gt;こんな感じのSchemaFilterを用意して&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnumSchemaFilter&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ISchemaFilter&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OpenApiSchema&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SchemaFilterContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsEnum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;varNamesArray&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OpenApiArray&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;memberVal&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;memberName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;memberVal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                &lt;span class="n"&gt;varNamesArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OpenApiString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;memberName&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-enum-varnames"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;varNamesArray&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AddSwaggerGenで指定してやります&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddSwaggerGen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SchemaFilter&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;EnumSchemaFilter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;すると、openapi schemaにはx-enum-varnamesにメンバー名が入るようになります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"WeatherType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"int32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"x-enum-varnames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Sunny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Cloudy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Rainy"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;これを使ってTypeScriptを生成すると、キーが正しくメンバー名になります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Sunny&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Cloudy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Rainy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  表示名を変えたい
&lt;/h2&gt;

&lt;p&gt;x-enum-varnamesを使えば、TypeScriptのオブジェクトのキーとして、Enumのメンバー名を使用できましたが、&lt;br&gt;
実際にUIに値を表示したい時は、もっと違う文字列を表示したいかもしれません。&lt;/p&gt;

&lt;p&gt;例えばメンバー名を &lt;code&gt;Sunny=&lt;/code&gt; とした場合、&lt;code&gt;=&lt;/code&gt; はTypeScriptのオブジェクトのキーとして使えないのでエラーになります。&lt;/p&gt;

&lt;p&gt;また、各国の言語に翻訳した名前を表示したいかもしれません。&lt;/p&gt;
&lt;h2&gt;
  
  
  EnumのAttributeを表示する
&lt;/h2&gt;

&lt;p&gt;そこで、EnumにAttributeをつけて、それを表示するようにしてみます。&lt;br&gt;
今回は &lt;code&gt;System.ComponentModel.DataAnnotations.DisplayAttribute&lt;/code&gt; を使用します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;WeatherType&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"晴れ"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Sunny&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"曇り"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Cloudy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"雨"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Rainy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;openapi schemaのテキトーなキーに、この表示名をつけれればいいので、&lt;br&gt;
EnumSchmeFilterを以下のように修正し、 &lt;code&gt;x-enum-displays&lt;/code&gt; というスキーマを追加してみます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;public class EnumSchemaFilter : ISchemaFilter
&lt;/span&gt;{
    public void Apply(OpenApiSchema model, SchemaFilterContext context)
    {
        if (context.Type.IsEnum)
        {
            var varNamesArray = new OpenApiArray();
&lt;span class="gi"&gt;+           var displayArray = new OpenApiArray();
&lt;/span&gt;            foreach (var memberVal in Enum.GetValues(context.Type))
            {
                var memberName = Enum.GetName(context.Type, memberVal);
                varNamesArray.Add(new OpenApiString(memberName));
&lt;span class="gi"&gt;+               var obj = new OpenApiObject();
+               var display = (DisplayAttribute?)memberVal.GetType().GetMember(memberVal.ToString() ?? "").FirstOrDefault()?.GetCustomAttributes(typeof(DisplayAttribute), true).FirstOrDefault();
+               obj["value"] = new OpenApiInteger(Convert.ToInt32(memberVal));
+               obj["display"] = new OpenApiString(display?.Name);
+               displayArray.Add(obj);
&lt;/span&gt;            }
&lt;span class="err"&gt;
&lt;/span&gt;            model.Extensions.Add("x-enum-varnames", varNamesArray);
&lt;span class="gi"&gt;+           model.Extensions.Add("x-enum-displays", displayArray);
&lt;/span&gt;        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;これで生成されたスキーマは以下のようになります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"WeatherType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"enum"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"integer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"int32"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"x-enum-varnames"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Sunny"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Cloudy"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Rainy"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"x-enum-displays"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"晴れ"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"曇り"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"display"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"雨"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;あとはこれを使って、TypeScriptコードを生成してやればよいのですが、標準では今回作った &lt;code&gt;x-enum-displays&lt;/code&gt; を読めないので、コード生成テンプレートを修正してやります。&lt;/p&gt;

&lt;p&gt;テンプレートはここにあるので落としてきます&lt;br&gt;
&lt;a href="https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator/src/main/resources/typescript-fetch"&gt;https://github.com/OpenAPITools/openapi-generator/tree/master/modules/openapi-generator/src/main/resources/typescript-fetch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;次に、テンプレートの中にある &lt;code&gt;modelEnumInterfaces.mustache&lt;/code&gt; に以下を追加します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/*
customized
*/
{{^stringEnums}}
{{#vendorExtensions}}
export function Get{{classname}}DisplayName(v: {{classname}}) {
    switch(v) {
{{#x-enum-displays}}
        case {{value}}:
            return "{{display}}";
{{/x-enum-displays}}
        default:
            throw new Error(`value ${v} is not supported`);
    }
}
{{/vendorExtensions}}
{{/stringEnums}}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;あとはopenapi generatorで生成する時に、 &lt;code&gt;-t&lt;/code&gt; オプションで、先ほど修正したテンプレートのディレクトリを指定します。&lt;/p&gt;

&lt;p&gt;テンプレートのカスタマイズは&lt;a href="https://openapi-generator.tech/docs/templating/"&gt;このドキュメント&lt;/a&gt;を参考にしてください。&lt;/p&gt;

&lt;p&gt;これで、生成されたTypeScriptには、enumから表示名を取得する関数が追加されます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Sunny&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Cloudy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Rainy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;keyof&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="cm"&gt;/*
customized
*/&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GetWeatherTypeDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WeatherType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;晴れ&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;曇り&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;雨&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`value &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is not supported`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;こんな感じで表示名を取得できます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 晴れ が返される&lt;/span&gt;
&lt;span class="nc"&gt;GetWeatherTypeDisplayName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;WeatherType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Sunny&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>aspdotnet</category>
      <category>csharp</category>
      <category>typscript</category>
      <category>swagger</category>
    </item>
    <item>
      <title>Azure AD B2C学習メモ</title>
      <dc:creator>garicchi</dc:creator>
      <pubDate>Sat, 06 Jan 2024 07:54:40 +0000</pubDate>
      <link>https://dev.to/garicchi/azure-ad-b2cxue-xi-memo-1l0a</link>
      <guid>https://dev.to/garicchi/azure-ad-b2cxue-xi-memo-1l0a</guid>
      <description>&lt;h2&gt;
  
  
  モチベーション
&lt;/h2&gt;

&lt;p&gt;Auth0、Firebase Authenticationなど、Webサービスの認証を、独立した外部サーバーにするID as a Serviceですが、&lt;br&gt;
Azureにも同じようなものが存在していて、それがAzure AD B2Cです。&lt;/p&gt;

&lt;p&gt;セキュリティ対策が大変なユーザー認証周りを既存のサービスやライブラリに任せることができたり、&lt;br&gt;
ソーシャルログインやMFAなどの対応が楽になるというメリットがある一方、&lt;br&gt;
使い方を把握する必要があります。&lt;/p&gt;

&lt;p&gt;参考になったリンクや情報をメモしておきます。&lt;/p&gt;
&lt;h2&gt;
  
  
  想定アーキテクチャ
&lt;/h2&gt;

&lt;p&gt;今回想定するアーキテクチャはシンプルなSPAとバックエンドAPIの構成になります。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhbq6osxweamdyxnkfhia.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhbq6osxweamdyxnkfhia.png" alt="Image description" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  ライブラリとドキュメントの場所
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ADB2Cテナントの作成
&lt;/h3&gt;

&lt;p&gt;AADB2Cでは、認証サーバーをテナントという概念で管理していて、まずはテナントを作成する必要があります。&lt;br&gt;
テナントは下記ドキュメントで作成できます。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/tutorial-create-tenant"&gt;https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/tutorial-create-tenant&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;アプリを作るとき、プラットフォームを聞かれますが、spaを選択します。&lt;/p&gt;

&lt;p&gt;テナントを作ったら、ポータルから以下のconfig情報を頑張って探してきます。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "AzureAdB2C": {
    "Instance": "https://{テナント名}.b2clogin.com",
    "ClientId": "{ポータルに記載されているClientId}",
    "Domain": "{テナント名}.onmicrosoft.com",
    "SignUpSignInPolicyId": "{サインインサインアップのポリシーID (B2C_1_*)}",
    "AllowWebApiToBeAuthorizedByACL": true
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SPAとAADB2Cとの接続
&lt;/h3&gt;

&lt;p&gt;AADB2CはOpenIdConnectというプロトコルで通信を行うので、フロントエンドJavaScriptからOpenIdConnectを使う必要があります。&lt;/p&gt;

&lt;p&gt;しかし、AADB2Cに最適なライブラリ(&lt;a href="https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-browser"&gt;msal-browser&lt;/a&gt;)が公式から提供されているのでこれを使います。&lt;br&gt;
&lt;a href="https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/configure-authentication-sample-spa-app"&gt;https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/configure-authentication-sample-spa-app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;githubにも使い方のmarkdownがあるので、これを参考にするのも良さそうです。&lt;br&gt;
&lt;a href="https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-browser#msal-basics"&gt;https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-browser#msal-basics&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MSAL-browserはPublicClientApplicationクラスにconfigを渡すのですが、&lt;br&gt;
先程テナントを作るときに作ったconfigをうまく割り当てます&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PublicClientApplication&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;aadb2cOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ClientId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;redirectUri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FrontendUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/auth-redirect`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;navigateToLoginRequestUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;authority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aadb2cOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aadb2cOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Domain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;aadb2cOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SignUpSignInPolicyId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;knownAuthorities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aadb2cOption&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Instance&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;cacheLocation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sessionStorage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// This configures where your cache will be stored&lt;/span&gt;
      &lt;span class="na"&gt;storeAuthStateInCookie&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;// Set this to "true" if you are having issues on IE11 or Edge&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ASP.NET APIとAADB2Cを接続する
&lt;/h3&gt;

&lt;p&gt;ASP.NETのバックエンドAPIと接続するには、下記ドキュメントが良さそうです。&lt;br&gt;
&lt;a href="https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/enable-authentication-web-api?tabs=csharpclient"&gt;https://learn.microsoft.com/ja-jp/azure/active-directory-b2c/enable-authentication-web-api?tabs=csharpclient&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ASP.NETとAADB2Cを接続するには、 &lt;code&gt;Microsoft.Identity.Web&lt;/code&gt; というライブラリを使います。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Microsoft.Identity.Web.UI&lt;/code&gt; というライブラリもありますが、これはControllerが自動で生えるので今回は使いません。&lt;/p&gt;

&lt;p&gt;ServiceCollectionを作るときに、AddMicrosoftIdentityWebApiというのをつけます。&lt;br&gt;
これでAuthorize属性がついたエンドポイントは、tokenの検証が入ります。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;builder.Services.AddAuthentication()
            .AddMicrosoftIdentityWebApi(builder.Configuration, "AzureAdB2C");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;AddMicrosoftIdentityWebApp&lt;/code&gt; もありますが、これはバックエンドがAPI用ではなく、普通のASP.NET MVCのrazor page用 (cookie認証) での使用が本来の用途のようなので今回は使用しません。&lt;/p&gt;

&lt;h3&gt;
  
  
  API通信をする
&lt;/h3&gt;

&lt;p&gt;あとはMSAL-browserのドキュメントに則って通信をすれば、良いです。&lt;br&gt;
せっかくなのでメモ書きを残しておきます。&lt;/p&gt;

&lt;p&gt;ログインをするには、loginRedirectを使います。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await msalClient.initialize();
await auth.client.value.loginRedirect({
      scopes: ['openid']
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;するとAADB2Cのログイン画面にリダイレクトされ、認証が成功すると、redirect-urlにリダイレクトが返ってきます。&lt;/p&gt;

&lt;p&gt;リダイレクトが返ってきたら、handleRedirectPromise();を実行して、authorization codeを受け取ります。&lt;br&gt;
さらにその中でtoken endpointにリクエストを送り、id_tokenとaccess_tokenを取得します。&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await msalClient.handleRedirectPromise();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;最後に、取得したaccess_tokenをAuthorization Headerに入れて、ASP.NETのAPIを呼び出せば成功するはずです。&lt;/p&gt;

&lt;h2&gt;
  
  
  雑メモ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  AADB2CのリダイレクトURLが変わらない
&lt;/h3&gt;

&lt;p&gt;どうやら反映に1時間ほどかかるようです。すぐに反映したい場合はテナント内のアプリケーションを作り直すとよさそうです。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/answers/questions/1181295/azure-b2c-redirect-url-takes-around-1h-before-take"&gt;https://learn.microsoft.com/en-us/answers/questions/1181295/azure-b2c-redirect-url-takes-around-1h-before-take&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azure</category>
      <category>csharp</category>
    </item>
    <item>
      <title>How to set log level in Application Insights in ASP.Net</title>
      <dc:creator>garicchi</dc:creator>
      <pubDate>Fri, 22 Dec 2023 06:19:43 +0000</pubDate>
      <link>https://dev.to/garicchi/how-to-set-log-level-in-application-insights-in-aspnet-4999</link>
      <guid>https://dev.to/garicchi/how-to-set-log-level-in-application-insights-in-aspnet-4999</guid>
      <description>&lt;p&gt;Whenever I do logging of information level in application insights, I could not see any logs within that.&lt;/p&gt;

&lt;p&gt;As the solution of that, I needed to specify log level with below configuration key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Logging:ApplicationInsights:LogLevel:Default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;if I specify log level in this key &lt;code&gt;Logging:LogLevel:Default&lt;/code&gt; , it does not affect to log of application insights because the provider name is application insights.&lt;/p&gt;

</description>
      <category>aspdotnet</category>
      <category>azure</category>
    </item>
    <item>
      <title>ReactNativeでauth0を使う時にaudienceパラメータを付与する</title>
      <dc:creator>garicchi</dc:creator>
      <pubDate>Fri, 18 Aug 2023 06:39:09 +0000</pubDate>
      <link>https://dev.to/garicchi/reactnativedeauth0woshi-ushi-niaudienceparametawofu-yu-suru-ajo</link>
      <guid>https://dev.to/garicchi/reactnativedeauth0woshi-ushi-niaudienceparametawofu-yu-suru-ajo</guid>
      <description>&lt;p&gt;通常のauth0 react sdkでは、audienceパラメータは以下のように注入することができるが&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Auth0Provider&lt;/span&gt;
        &lt;span class="na"&gt;authorizationParams&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Auth0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read:current_user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;react nativeのauth0sdkではaudienceパラメータを、Auth0Providerでは付与できない&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Auth0Provider&lt;/span&gt;
      &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Auth0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Auth0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clientId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;このまま使うと、例えばAspdotnetのAPIサーバーに通信したときに以下のようにキー複合化エラーが出る。&lt;br&gt;
これはaudienceパラメータが無いかららしい&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IDX10609: Decryption failed. No Keys tried: toke n:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ではどうするかというと、ログイン時(authorize)にaudienceを渡すことができる&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Auth0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audience&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'login'&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;この認証情報をつかって発行されたaccessTokenはaudienceパラメータが付与されている&lt;/p&gt;

</description>
    </item>
    <item>
      <title>react native expoでauth0 sdkを使うと TypeError: Cannot read property 'hasValidAuth0Instance' of null となる</title>
      <dc:creator>garicchi</dc:creator>
      <pubDate>Thu, 17 Aug 2023 22:30:00 +0000</pubDate>
      <link>https://dev.to/garicchi/react-native-expodeauth0-sdkwoshi-uto-typeerror-cannot-read-property-hasvalidauth0instance-of-null-tonaru-2lj9</link>
      <guid>https://dev.to/garicchi/react-native-expodeauth0-sdkwoshi-uto-typeerror-cannot-read-property-hasvalidauth0instance-of-null-tonaru-2lj9</guid>
      <description>&lt;p&gt;react native expoで auth0を使いたくて、&lt;a href="https://auth0.com/docs/quickstart/native/react-native-expo/00-login#show-user-profile-information"&gt;チュートリアル&lt;/a&gt; をしていると、以下のエラーが起きた&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: Cannot read property 'hasValidAuth0Instance' of null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ググってもそれらしい情報が出ず、困っていたが、どうやらauth0のネイティブモジュールが正しくビルドできていなかったようだ。&lt;/p&gt;

&lt;p&gt;チュートリアルでは&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expo prebuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;の指示があり、これでネイティブモジュールもビルドできていたのではないかと思っていたがどうやらそうではないらしい&lt;/p&gt;

&lt;p&gt;ということでandroidプロジェクトをビルドしてみるとうまく動いた&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# jdkが必要

cd ./android
./gradlew :app:build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ちなみに上記コマンドはビルド失敗するのだが、auth0のモジュールはビルドできていたのか、expoは動いた&lt;/p&gt;

&lt;p&gt;package.jsonには &lt;code&gt;expo run:android&lt;/code&gt; もあるので、こちらも試してみるとよいだろう&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Auth0のReact sampleが動かない問題の対処方法</title>
      <dc:creator>garicchi</dc:creator>
      <pubDate>Fri, 11 Aug 2023 04:48:45 +0000</pubDate>
      <link>https://dev.to/garicchi/auth0noreact-samplegadong-kanaiwen-ti-nodui-chu-fang-fa-238m</link>
      <guid>https://dev.to/garicchi/auth0noreact-samplegadong-kanaiwen-ti-nodui-chu-fang-fa-238m</guid>
      <description>&lt;p&gt;Auth0の公式React quickstartは自分の環境では動かなかった&lt;/p&gt;

&lt;h2&gt;
  
  
  サンプルでログインするとUnauthorizedになる
&lt;/h2&gt;

&lt;p&gt;サンプルアプリを動かすと、ログイン後、Unauthorizedになるが、これはAuth0の設定画面でAuthentication Methodsが &lt;code&gt;Client Secret (Post)&lt;/code&gt; になっているからだった&lt;/p&gt;

&lt;p&gt;これをNoneにすればUnauthorizedが出ずに動く&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fgindyy9jp68fwucwry.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0fgindyy9jp68fwucwry.png" alt="Image description" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原因を探ると、認証サーバーの &lt;code&gt;/oauth/token&lt;/code&gt; エンドポイントにaccess tokenを取得しに行ったときに、client secretを一緒にpostしないといけないらしい&lt;/p&gt;

&lt;h2&gt;
  
  
  External APIページで Ping APIを押すと401になる
&lt;/h2&gt;

&lt;p&gt;External APIページで Ping APIを押すと、401になって返ってこない。&lt;br&gt;
サンプルAPIサーバーのコンソールを見ると、&lt;code&gt;InvalidTokenError: Invalid Compact JWS auth0&lt;/code&gt; と言われている。&lt;/p&gt;

&lt;p&gt;これはクライントsdkの authorizationParamsにaudienceを入れていないからになる。&lt;/p&gt;

&lt;p&gt;ので以下のように追加で解決&lt;/p&gt;

&lt;p&gt;index.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;root.render(
&lt;/span&gt;  &amp;lt;Auth0Provider
    {...config}
    authorizationParams= {{
      redirect_uri: window.location.origin,
&lt;span class="gi"&gt;+     audience: config.audience
&lt;/span&gt;    }}
  &amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>auth0</category>
    </item>
  </channel>
</rss>
