<?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: ramtinmovahed</title>
    <description>The latest articles on DEV Community by ramtinmovahed (@ramtinmovahed).</description>
    <link>https://dev.to/ramtinmovahed</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%2F422074%2Fc9e88fce-1aa6-4dd4-8377-28944578004d.png</url>
      <title>DEV Community: ramtinmovahed</title>
      <link>https://dev.to/ramtinmovahed</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ramtinmovahed"/>
    <language>en</language>
    <item>
      <title>Structured Security in Blazor: Exploring Logic-Driven Authorization</title>
      <dc:creator>ramtinmovahed</dc:creator>
      <pubDate>Wed, 01 Nov 2023 06:58:43 +0000</pubDate>
      <link>https://dev.to/ramtinmovahed/structured-security-in-blazor-exploring-logic-driven-authorization-1161</link>
      <guid>https://dev.to/ramtinmovahed/structured-security-in-blazor-exploring-logic-driven-authorization-1161</guid>
      <description>&lt;p&gt;In Blazor applications, ensuring that users see only what they're authorized to is crucial. This guide will walk you through different methods of implementing authorization, both from within your markup and through procedural logic. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;1st Approach:&lt;/strong&gt; Using &lt;code&gt;AuthorizeView&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;In this approach, we display specific parts of the markup conditionally based on the user's authorization status. It supports both role-based and policy-based authorization and can be as simple or as complex as you want.&lt;br&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;
&lt;span class="nt"&gt;&amp;lt;AuthorizeView&lt;/span&gt; &lt;span class="na"&gt;Policy=&lt;/span&gt;&lt;span class="s"&gt;"NarrowedAuthenticatedUserPolicy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Authorized&amp;gt;&lt;/span&gt;
                What narrowed authenticated user sees
            &lt;span class="nt"&gt;&amp;lt;/Authorized&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;NotAuthorized&amp;gt;&lt;/span&gt;
                What unauthenticated user sees
            &lt;span class="nt"&gt;&amp;lt;/NotAuthorized&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/AuthorizeView&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;AuthorizeView&lt;/span&gt; &lt;span class="na"&gt;Policy=&lt;/span&gt;&lt;span class="s"&gt;"SpecialAuthenticatedUserPolicy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Authorized&amp;gt;&lt;/span&gt;
                What the special authenticated user sees
            &lt;span class="nt"&gt;&amp;lt;/Authorized&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/AuthorizeView&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;2nd Approach:&lt;/strong&gt; Using cascading &lt;code&gt;AuthenticationState&lt;/code&gt; and &lt;code&gt;IAuthorizationService&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you want to check for authorization as part of procedural logic in response to user interactions. In this approach, we would use the &lt;code&gt;AuthenticationState&lt;/code&gt; provided by the higher level &lt;code&gt;AuthorizeRouteView&lt;/code&gt; as a cascading parameter. Then, we would inject the IAuthorizationService to authorize against a role or authorization policy.&lt;br&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;CascadingParameter&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthenticationState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;?&lt;/span&gt; &lt;span class="n"&gt;authenticationState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;IAuthorizationService&lt;/span&gt; &lt;span class="n"&gt;AuthorizationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;Submit&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;authState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;authenticationState&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;authorizationResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthorizationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AuthorizeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"NarrowedAuthenticatedUserPolicy"&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;authorizationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Succeeded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// regular user logic.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;authorizationResult&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthorizationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AuthorizeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SpecialAuthenticatedUserPolicy"&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;authorizationResult&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Succeeded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Special user logic&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// Regular authenticated user logic.&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;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;3rd Approach:&lt;/strong&gt; using &lt;code&gt;AuthenticationStateProvider&lt;/code&gt; with &lt;code&gt;IAuthorizationService&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This approach is useful when you use a property in your razor markup and need to conditionally set that property based on the user's authorization status. In this case, we would inject &lt;code&gt;AuthenticationStateProvider&lt;/code&gt; to get notified about authorization changes, and use the injected &lt;code&gt;AuthorizationService&lt;/code&gt; to check if the user is authorized or not.&lt;br&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;IAuthorizationService&lt;/span&gt; &lt;span class="n"&gt;AuthorizationService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&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="n"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;AuthenticationStateProvider&lt;/span&gt; &lt;span class="n"&gt;AuthenticationStateProvider&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"What unauthenticated user sees"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;OnInitializedAsync&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;AuthenticationStateProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AuthenticationStateChanged&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;AuthenticationStateChangedHandler&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;authState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthenticationStateProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetAuthenticationStateAsync&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;authState&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ConstructMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;authState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;AuthenticationStateChangedHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AuthenticationState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authState&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;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;authState&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;state&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;ConstructMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;User&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="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;ConstructMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ClaimsPrincipal&lt;/span&gt; &lt;span class="n"&gt;user&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;authRes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthorizationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AuthorizeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"NarrowedAuthenticatedUserPolicy"&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;authRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Succeeded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;UserContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"What narrowed authenticated user sees"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;authRes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;AuthorizationService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AuthorizeAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SpecialAuthenticatedUserPolicy"&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;authRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Succeeded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;UserContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"What the special authenticated user sees"&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;&lt;strong&gt;Important Note&lt;/strong&gt;: here since we're using events, it is fine to do async void. The exceptions are still caught&lt;br&gt;
You may think that since &lt;a href="https://github.com/dotnet/aspnetcore/blob/f9121bc3e976ec40a959818451d126d5126ce868/src/Components/Authorization/src/CascadingAuthenticationState.razor"&gt;CascadingAuthenticationState&lt;/a&gt; uses &lt;code&gt;InvokeAsync&lt;/code&gt; internally, then you should use it too. Unless you're well-versed with Blazor's intricacies, it's recommended to avoid using &lt;code&gt;InvokeAsync&lt;/code&gt; from &lt;code&gt;componentBase&lt;/code&gt; within your event handler&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DO NOT&lt;/strong&gt; try to find a workaround for not using async void for the event handler. If you use &lt;code&gt;InvokeAsync&lt;/code&gt; without awaiting it, any code that throws an exception won't be caught by blazor. It depends on your use case too, however, you can see the effect by trying to throw an exception inside the following &lt;a href="https://learn.microsoft.com/en-us/aspnet/core/blazor/components/rendering?view=aspnetcore-7.0#receiving-a-call-from-something-external-to-the-blazor-rendering-and-event-handling-system"&gt;example&lt;/a&gt; from Microsoft docs.&lt;/p&gt;

&lt;p&gt;inside the second example, try to throw an error like so:&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;private&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnTimerCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;InvokeAsync&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&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="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;currentCount&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
        &lt;span class="nf"&gt;StateHasChanged&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;You will see that the component simply won't work, and no exception would be caught. This means you'd be in the dark about any errors, including their origins in the stack trace.&lt;/p&gt;

&lt;p&gt;Further reading: &lt;a href="https://devblogs.microsoft.com/dotnet/how-async-await-really-works/"&gt;https://devblogs.microsoft.com/dotnet/how-async-await-really-works/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With these approaches, you'll be better equipped to implement structured security in your Blazor applications. &lt;/p&gt;

&lt;p&gt;Thanks for reading, Happy Halloween :)&lt;/p&gt;

</description>
      <category>blazor</category>
      <category>aspnet</category>
      <category>cybersecurity</category>
      <category>identity</category>
    </item>
    <item>
      <title>How to configure the authorize attribute for a simpler role-based authentication</title>
      <dc:creator>ramtinmovahed</dc:creator>
      <pubDate>Tue, 22 Aug 2023 02:15:07 +0000</pubDate>
      <link>https://dev.to/ramtinmovahed/how-to-configure-the-authorize-attribute-for-a-simpler-role-based-authentication-2jg9</link>
      <guid>https://dev.to/ramtinmovahed/how-to-configure-the-authorize-attribute-for-a-simpler-role-based-authentication-2jg9</guid>
      <description>&lt;p&gt;The authorize attribute in ASP.NET is quite powerful. You can use it for simple authentication, or you can use it for role-based authentication or policy-based authentication by specifying the supplying roles or policies as parameters.&lt;/p&gt;

&lt;p&gt;Depending on the size and scope of a project, you might only need a role-based access control without authorization policy. Also, you might want a resource to be accessible for multiple roles.&lt;/p&gt;

&lt;p&gt;The way that authorize attribute is structured is that you need to pass a string to specify the role or policy. You may use constants to prevent hard coding it, but you still need to constantly concatenate a comma "," to add more roles.&lt;/p&gt;

&lt;p&gt;With a mix of a lookup dictionary, enums, and inheritance, you can create a custom strongly typed authorize attribute for role-based access control.&lt;/p&gt;

&lt;p&gt;First, define your roles in a role helper class like so:&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;RolesHelper&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;static&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;Dictionary&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;RolesDict&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&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="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FullAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"FullAdmin"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadOnly&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ReadOnly"&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="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Role&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;FullAdmin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ReadOnly&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;In the code above, we first defined the roles in an enum and then created a lookup dictionary.&lt;/p&gt;

&lt;p&gt;** You might be able to simplify that further in the future when c# adds support for discriminated unions by assigning string values directly to enums.&lt;/p&gt;

&lt;p&gt;Now its time  to implement the custom authorize attribute class like so:&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;CustomRoleAuthorizationAttribute&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AuthorizeAttribute&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;CustomRoleAuthorizationAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;params&lt;/span&gt; &lt;span class="n"&gt;Role&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;rolesFromDict&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&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="n"&gt;Role&lt;/span&gt; &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;roles&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;RolesHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RolesDict&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TryGetValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;out&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;roleFromDict&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;rolesFromDict&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="n"&gt;roleFromDict&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="n"&gt;Roles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Join&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;rolesFromDict&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;Let's go through the code. We first inherit from the Authorize attribute class. We then create the constructor. In the constructor, we pass the list of roles using the &lt;code&gt;params&lt;/code&gt; keyword.&lt;/p&gt;

&lt;p&gt;Side note: what is &lt;code&gt;params&lt;/code&gt;?&lt;br&gt;
You may recall this keyword if you started building a console application and you might have seen it in the main method. By using this keyword, you can send any number of roles that you want, without creating and putting them into an array first.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;params&lt;/code&gt; keyword is important as the attributes are evaluated at the runtime, and we cannot instantiate an array to pass to them (we still can instantiate an object inside an attribute).&lt;/p&gt;

&lt;p&gt;We would then loop through the roles and check the dictionary to get the string value of it. Finally, we assign the Roles property to those string values, joined by commas ",".&lt;/p&gt;

&lt;p&gt;I hope you find this useful. Thanks for reading!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How to have separate staging and testing token enrichment process without creating another Azure AD B2C Tenant.</title>
      <dc:creator>ramtinmovahed</dc:creator>
      <pubDate>Thu, 03 Aug 2023 05:31:39 +0000</pubDate>
      <link>https://dev.to/ramtinmovahed/how-to-have-separate-staging-and-testing-token-enrichment-process-without-creating-another-azure-ad-b2c-tenant-3fa7</link>
      <guid>https://dev.to/ramtinmovahed/how-to-have-separate-staging-and-testing-token-enrichment-process-without-creating-another-azure-ad-b2c-tenant-3fa7</guid>
      <description>&lt;h2&gt;
  
  
  Azure AD B2C tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  how to have separate staging and testing token enrichment process without creating another Azure AD B2C Tenant.
&lt;/h3&gt;

&lt;p&gt;Based on your use case, you might want a separate process to add custom domains to your user flow. Azure B2C engine needs to call your APIs to get the appropriate claims to include in the JWT token. However, sometimes (especially during the staging environment), you might still want to have two separate API endpoints for Azure to call. One is the backend that sits in the staging environment, and the other one is the endpoint at your local dev environment.&lt;br&gt;
A secure solution would require you to create different Azure tenants for testing and production. However, during the staging environment, you can make the process simpler by using a temporary custom policy that is based on the final policy to call those APIs.&lt;/p&gt;

&lt;p&gt;A secure solution would require you to create different Azure tenants for testing and production. However, during the staging environment, you can make the process simpler by using a temporary custom policy that is based on the final policy to call those APIs.&lt;/p&gt;

&lt;p&gt;The process is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ensure that the API endpoints reside in the main user flow policy.&lt;/li&gt;
&lt;li&gt;create a duplicate of the custom policy that holds the orchestration steps and the API endpoint's &lt;/li&gt;
&lt;li&gt;put the appropriate URLs for each endpoint in each file.&lt;/li&gt;
&lt;li&gt;upload it to Azure.&lt;/li&gt;
&lt;li&gt;update your application to use the new test policy in the dev environment.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  First step
&lt;/h2&gt;

&lt;p&gt;The first step is to check that the technical profile exists in the policy file that contains the user journey.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;TechnicalProfile&lt;/span&gt; &lt;span class="na"&gt;Id=&lt;/span&gt;&lt;span class="s"&gt;"EnrichToken"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;DisplayName&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/DisplayName&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Metadata&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Item&lt;/span&gt; &lt;span class="na"&gt;Key=&lt;/span&gt;&lt;span class="s"&gt;"ServiceUrl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;https://yourendpoint.com/api/.../&lt;span class="nt"&gt;&amp;lt;/Item&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/Metadata&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;InputClaims&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;InputClaim&lt;/span&gt; &lt;span class="na"&gt;ClaimTypeReferenceId=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            ...
          &lt;span class="nt"&gt;&amp;lt;/InputClaims&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;OutputClaims&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;OutputClaim&lt;/span&gt; &lt;span class="na"&gt;ClaimTypeReferenceId=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
            ...
          &lt;span class="nt"&gt;&amp;lt;/OutputClaims&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;IncludeTechnicalProfile&lt;/span&gt; &lt;span class="na"&gt;ReferenceId=&lt;/span&gt;&lt;span class="s"&gt;"...."&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/TechnicalProfile&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: create the test policy
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;create a duplicate of that policy file. &lt;/li&gt;
&lt;li&gt;Remove all parts that you don't need to change.&lt;/li&gt;
&lt;li&gt;include the main policy file as the base policy of the test policy file.

&lt;ul&gt;
&lt;li&gt;since the Identity Experience framework has an inheritance model, all information would be included.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;TrustFrameworkPolicy&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt; &lt;span class="na"&gt;xmlns:xsd=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema"&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://schemas.microsoft.com/online/cpim/schemas/2013/06"&lt;/span&gt; &lt;span class="na"&gt;PolicySchemaVersion=&lt;/span&gt;&lt;span class="s"&gt;"0.3.0.0"&lt;/span&gt; &lt;span class="na"&gt;TenantId=&lt;/span&gt;&lt;span class="s"&gt;"yourtenant.onmicrosoft.com"&lt;/span&gt; &lt;span class="na"&gt;PolicyId=&lt;/span&gt;&lt;span class="s"&gt;"B2C_1A_yourNewTestPolicyFileName"&lt;/span&gt; &lt;span class="na"&gt;PublicPolicyUri=&lt;/span&gt;&lt;span class="s"&gt;"http://yourtenant.onmicrosoft.com/B2C_1A_yourNewTestPolicyFileName"&lt;/span&gt; &lt;span class="na"&gt;TenantObjectId=&lt;/span&gt;&lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;BasePolicy&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;TenantId&amp;gt;&lt;/span&gt;yourtenant.onmicrosoft.com&lt;span class="nt"&gt;&amp;lt;/TenantId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;PolicyId&amp;gt;&lt;/span&gt;B2C_1A_NameOfPreviousPolicyFile&lt;span class="nt"&gt;&amp;lt;/PolicyId&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/BasePolicy&amp;gt;&lt;/span&gt;
  ....
&lt;span class="nt"&gt;&amp;lt;/TrustFrameworkPolicy&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: change the endpoints
&lt;/h2&gt;

&lt;p&gt;In the test policy file, replace the endpoint with the development API endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;ClaimsProviders&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ClaimsProvider&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;DisplayName&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/DisplayName&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;TechnicalProfiles&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;TechnicalProfile&lt;/span&gt; &lt;span class="na"&gt;Id=&lt;/span&gt;&lt;span class="s"&gt;"EnrichToken"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;Metadata&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;Item&lt;/span&gt; &lt;span class="na"&gt;Key=&lt;/span&gt;&lt;span class="s"&gt;"ServiceUrl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;https://yourdevendpoint.com/api/...&lt;span class="nt"&gt;&amp;lt;/Item&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/Metadata&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/TechnicalProfile&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/TechnicalProfiles&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ClaimsProvider&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/ClaimsProviders&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Upload to Azure
&lt;/h2&gt;

&lt;p&gt;Upload the created test policy to azure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final step: updating the application.
&lt;/h2&gt;

&lt;p&gt;Add the custom logic in your application to request this new test policy for authentication in the dev environment.&lt;/p&gt;

&lt;p&gt;If you're using Blazor or ASP.NET, you can use the app settings to configure that.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;next to appsettings.json, create appsettings.Development.json (case sensitive)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;override the values for the policy&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  "AzureAd": {
    "Authority": "https://yourtenant.b2clogin.com/yourtenant.onmicrosoft.com/B2C_1A_yourNewTestPolicyFileName"
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, test.&lt;/p&gt;

&lt;p&gt;Thanks for reading. Have a good day!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>identity</category>
      <category>oauth</category>
    </item>
    <item>
      <title>expandable card in angular material using button.</title>
      <dc:creator>ramtinmovahed</dc:creator>
      <pubDate>Sun, 29 Nov 2020 22:57:43 +0000</pubDate>
      <link>https://dev.to/ramtinmovahed/expandable-card-in-angular-material-using-button-1b0c</link>
      <guid>https://dev.to/ramtinmovahed/expandable-card-in-angular-material-using-button-1b0c</guid>
      <description>&lt;p&gt;In this post, I'm going to implement a simple version of the expandable card with an action button in angular using angular material.&lt;br&gt;
&lt;a href="https://i.giphy.com/media/lB088iIVTBgF9gIXky/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/lB088iIVTBgF9gIXky/giphy.gif" alt="real deal"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://material.io/components/cards#anatomy"&gt;material design&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 1: base initializations
&lt;/h4&gt;

&lt;p&gt;create a new angular project by running&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng new my-app&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;we just need the basic functionalities for this demo, so we don't need routing and strict mode.&lt;/p&gt;

&lt;p&gt;Then, install angular material by running:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ng add @angular/material&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;don't forget to enable the browser animations&lt;/p&gt;

&lt;p&gt;next, delete the content of app.component.html&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: Import the necessary modules
&lt;/h4&gt;

&lt;p&gt;Based on material design, to implement, we need three components: card, divider, and button.&lt;/p&gt;

&lt;p&gt;so in app.module.ts add them to the imports array&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BrowserModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NgModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BrowserAnimationsModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/platform-browser/animations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MatCardModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/material/card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MatButtonModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/material/button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MatDividerModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/material/divider&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;NgModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;BrowserModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;BrowserAnimationsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;MatCardModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;MatButtonModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;MatDividerModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;bootstrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;]&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;class&lt;/span&gt; &lt;span class="nx"&gt;AppModule&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;h4&gt;
  
  
  Step 3: change the template
&lt;/h4&gt;

&lt;p&gt;edit the app.component.html&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;mat-card&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;mat-card-header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;mat-card-title&amp;gt;&lt;/span&gt;Title&lt;span class="nt"&gt;&amp;lt;/mat-card-title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;mat-card-subtitle&amp;gt;&lt;/span&gt;Subtitle&lt;span class="nt"&gt;&amp;lt;/mat-card-subtitle&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/mat-card-header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;mat-card-content&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
      Lorem ipsum, dolor sit amet consectetur adipisicing elit. Exercitationem, praesentium sit tempora numquam vel odit
      dolorem qui quod sint distinctio! Quasi exercitationem tempore voluptas quam voluptatibus distinctio ex magni
      repellendus?
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="err"&gt;[@&lt;/span&gt;&lt;span class="na"&gt;bodyExpansion]=&lt;/span&gt;&lt;span class="s"&gt;"state"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"expandable-content"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      [expandable] Lorem ipsum, dolor sit amet consectetur adipisicing elit. Exercitationem, praesentium sit tempora
      numquam vel odit
      dolorem qui quod sint distinctio! Quasi exercitationem tempore voluptas quam voluptatibus distinctio ex magni
      repellendus?
    &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;mat-divider&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/mat-divider&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/mat-card-content&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;mat-card-actions&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;mat-button&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"toggle()"&lt;/span&gt; &lt;span class="na"&gt;color=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;EXPAND&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/mat-card-actions&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;/mat-card&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;the bodyExpansion is the name of the animation that we're going to implement next.&lt;br&gt;
We needed to add the mat-divider component as specified in the material design spec.&lt;br&gt;
The state is the name of the property that's responsible for the state of the animations&lt;/p&gt;

&lt;p&gt;the toggle method will change this state (as you guest)&lt;/p&gt;
&lt;h4&gt;
  
  
  Step 4: adding the animations and implementing toggle method
&lt;/h4&gt;

&lt;p&gt;change the content of app.component.ts&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;animate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/animations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./app.component.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;animations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bodyExpansion&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;collapsed, void&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;visibility&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;visible&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
      &lt;span class="nx"&gt;transition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expanded &amp;lt;=&amp;gt; collapsed, void =&amp;gt; collapsed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;animate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;225ms cubic-bezier(0.4, 0.0, 0.2, 1)&lt;/span&gt;&lt;span class="dl"&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;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;collapsed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;toggle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;collapsed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expanded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;collapsed&lt;/span&gt;&lt;span class="dl"&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;In the animations array, we defined our animation. The trigger name is bodyExpansion that should match the trigger name that we set in the template. It has two states, collapsed and expanded. The collapsed state defines how the component should look when it's not expanded. So the height is zero and it's hidden.&lt;br&gt;
The expanded state defines how it should look when it's expanded. So the height would be the actual height and it should be visible.&lt;br&gt;
The transition defines how to move between these two states. the  &lt;code&gt;void=&amp;gt;collapsed&lt;/code&gt; is there to ensure it's collapsed when the component first renders.&lt;/p&gt;

&lt;p&gt;The toggle method will change the state property that we defined above.&lt;/p&gt;
&lt;h4&gt;
  
  
  Final step: add the appropriate styles
&lt;/h4&gt;

&lt;p&gt;In the app.component.css , add the following code&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.expandable-content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&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;The thing to note here is the &lt;code&gt;expandable-content&lt;/code&gt; class. This class makes the final fixes to the smooth transition.&lt;/p&gt;

&lt;p&gt;Hope this was useful, have a nice day!&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>materialdesign</category>
    </item>
  </channel>
</rss>
