{"id":52234,"date":"2020-04-27T08:00:19","date_gmt":"2020-04-27T06:00:19","guid":{"rendered":"https:\/\/code-maze.com\/?p=52234"},"modified":"2022-01-11T16:53:50","modified_gmt":"2022-01-11T15:53:50","slug":"identityserver4-authorization","status":"publish","type":"post","link":"https:\/\/code-maze.com\/identityserver4-authorization\/","title":{"rendered":"IdentityServer4 Authorization and Working with Claims"},"content":{"rendered":"<p>We can use claims to show identity-related information in our application but, we can use it for the authorization process as well. In this article, we are going to learn how to modify our claims and add new ones. Additionally, we are going to learn about the IdentityServer4 Authorization process and how to use Roles to protect our endpoints.<\/p>\n<div style=\"padding: 20px; border-left: 5px #dc2323 solid; display: block; margin-bottom: 20px; box-shadow: 1px 1px 5px 0px lightgrey;\">To download the source code for the client application, you can visit the <a href=\"https:\/\/github.com\/CodeMazeBlog\/identity-server-4-aspnetcore\/tree\/identityserver4-authorization-claims\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">IdentityServer4 Authorization<\/a> repository.<\/div>\n<p>To navigate through the entire series, visit the\u00a0<a href=\"https:\/\/code-maze.com\/identityserver-4-series\/\" target=\"_blank\" rel=\"noopener noreferrer\">IdentityServer4 series\u00a0<\/a>page.<\/p>\n<p>So, let\u2019s get down to business.<\/p>\n<h2 id=\"modifying-claims\">Modifying Claims<\/h2>\n<p>If we inspect our decoded <code>id_token<\/code> with the claims on the Privacy page, we are going to find some naming differences:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/35-Different-claims.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-52235\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/35-Different-claims.png\" alt=\"Different claims - IdentityServer4 Authorization\" width=\"1034\" height=\"429\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/35-Different-claims.png 1034w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/35-Different-claims-300x124.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/35-Different-claims-1024x425.png 1024w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/35-Different-claims-768x319.png 768w\" sizes=\"auto, (max-width: 1034px) 100vw, 1034px\" \/><\/a><\/p>\n<p>So, what we want to do is to ensure that our claims stay the same as we define them, instead of being mapped to different claims. For example, the nameidentifier claim is mapped from the sub claim, and we want it to stay the sub claim. To do that, we have to slightly modify the constructor in the client\u2019s <code>Startup<\/code> class:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"4\" data-enlighter-title=\"\">public Startup(IConfiguration configuration)\r\n{\r\n    Configuration = configuration;\r\n    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();\r\n} \r\n<\/pre><\/p>\n<p>For this to work, we have to add the <code>System.IdentityModel.Tokens.Jwt<\/code> using statement.<\/p>\n<p>Now, we can start our application, log out from the client, log in again, and check the Privacy page:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/36-Correct-mapping-claims.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-52236\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/36-Correct-mapping-claims.png\" alt=\"Correct mapping claims - IdentityServer4 Authorization\" width=\"272\" height=\"388\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/36-Correct-mapping-claims.png 272w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/36-Correct-mapping-claims-210x300.png 210w\" sizes=\"auto, (max-width: 272px) 100vw, 272px\" \/><\/a><\/p>\n<p>We can see our claims are the same as we defined them at the IDP (Identity Provider) level.<\/p>\n<p>If there are some claims we don\u2019t want to have in the token, we can remove them. To do that, we have to use the <code>ClaimActions<\/code> in the OIDC configuration:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"11-12\" data-enlighter-title=\"\">.AddOpenIdConnect(&quot;oidc&quot;, opt =&gt;\r\n{\r\n    opt.SignInScheme = &quot;Cookies&quot;;\r\n    opt.Authority = &quot;https:\/\/localhost:5005&quot;;\r\n    opt.ClientId = &quot;mvc-client&quot;;\r\n    opt.ResponseType = &quot;code id_token&quot;;\r\n    opt.SaveTokens = true;\r\n    opt.ClientSecret = &quot;MVCSecret&quot;;\r\n    opt.GetClaimsFromUserInfoEndpoint = true;\r\n\r\n    opt.ClaimActions.DeleteClaim(&quot;sid&quot;);\r\n    opt.ClaimActions.DeleteClaim(&quot;idp&quot;);\r\n});\r\n<\/pre><\/p>\n<p>The <code>DeleteClaim<\/code> method exists in the <code>Microsoft.AspNetCore.Authentication<\/code> namespace. As a parameter, we pass a claim we want to remove. Now, if we start our client again and navigate to the Privacy page, these claims will be missing for sure (Log out and log in prior to checking the Privacy page).<\/p>\n<p>If you don\u2019t want to use the <code>DeleteClaim<\/code> method for each claim you want to remove, you can always use the <code>DeleteClaims<\/code> method:<\/p>\n<p><code>opt.ClaimActions.DeleteClaims(new string[] { \"sid\", \"idp\" });<\/code><\/p>\n<p>Let\u2019s move on.<\/p>\n<h2 id=\"additional-claims\">Adding Additional Claims<\/h2>\n<p>If we want to add additional claims to our token (address, for example),\u00a0 we can do that with a few simple steps. The first step is to support a new identity resource in the <code>InMemoryConfig<\/code> class in the IDP project :<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"6\" data-enlighter-title=\"\">public static IEnumerable&lt;IdentityResource&gt; GetIdentityResources() =&gt;\r\n    new List&lt;IdentityResource&gt;\r\n    {\r\n        new IdentityResources.OpenId(),\r\n        new IdentityResources.Profile(),\r\n        new IdentityResources.Address()\r\n    };\r\n<\/pre><\/p>\n<p>Then, we have to add it to our client\u2019s allowed scopes:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"11\" data-enlighter-title=\"\">new Client\r\n{\r\n    ClientName = &quot;MVC Client&quot;,\r\n    ClientId = &quot;mvc-client&quot;,\r\n    AllowedGrantTypes = GrantTypes.Hybrid,\r\n    RedirectUris = new List&lt;string&gt;{ &quot;https:\/\/localhost:5010\/signin-oidc&quot; },\r\n    AllowedScopes =\r\n    { \r\n       IdentityServerConstants.StandardScopes.OpenId, \r\n       IdentityServerConstants.StandardScopes.Profile, \r\n       IdentityServerConstants.StandardScopes.Address \r\n    },\r\n    ClientSecrets = { new Secret(&quot;MVCSecret&quot;.Sha512()) },\r\n    PostLogoutRedirectUris = new List&lt;string&gt; { &quot;https:\/\/localhost:5010\/signout-callback-oidc&quot; }\r\n}\r\n<\/pre><\/p>\n<p>And lastly, we have to add the address claim for our users:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"10,22\" data-enlighter-title=\"\">new TestUser\r\n{\r\n    SubjectId = &quot;a9ea0f25-b964-409f-bcce-c923266249b4&quot;,\r\n    Username = &quot;Mick&quot;,\r\n    Password = &quot;MickPassword&quot;,\r\n    Claims = new List&lt;Claim&gt;\r\n    {\r\n        new Claim(&quot;given_name&quot;, &quot;Mick&quot;),\r\n        new Claim(&quot;family_name&quot;, &quot;Mining&quot;),\r\n        new Claim(&quot;address&quot;, &quot;Sunny Street 4&quot;)\r\n    }\r\n},\r\nnew TestUser\r\n{\r\n    SubjectId = &quot;c95ddb8c-79ec-488a-a485-fe57a1462340&quot;,\r\n    Username = &quot;Jane&quot;,\r\n    Password = &quot;JanePassword&quot;,\r\n    Claims = new List&lt;Claim&gt;\r\n    {\r\n        new Claim(&quot;given_name&quot;, &quot;Jane&quot;),\r\n        new Claim(&quot;family_name&quot;, &quot;Downing&quot;),\r\n        new Claim(&quot;address&quot;, &quot;Long Avenue 289&quot;)\r\n    }\r\n}\r\n<\/pre><\/p>\n<p>And, that\u2019s all it takes regarding the <code>InMemoryConfig<\/code> class.<\/p>\n<p>One more thing. If we want to see the consent page for a specific client, we can enable that in the Client configuration in the OAuth project:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"3\" data-enlighter-title=\"\">ClientSecrets = { new Secret(&quot;MVCSecret&quot;.Sha512()) },\r\nPostLogoutRedirectUris = new List&lt;string&gt; { &quot;https:\/\/localhost:5010\/signout-callback-oidc&quot; },\r\nRequireConsent = true<\/pre><\/p>\n<p>Now, we have to modify the Client application, by adding the new scope to the OIDC configuration:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"8\" data-enlighter-title=\"\">.AddOpenIdConnect(&quot;oidc&quot;, opt =&gt;\r\n{\r\n    \/\/previous code\r\n\r\n    opt.ClaimActions.DeleteClaim(&quot;sid&quot;);\r\n    opt.ClaimActions.DeleteClaim(&quot;idp&quot;);\r\n\r\n    opt.Scope.Add(&quot;address&quot;);\r\n});\r\n<\/pre><\/p>\n<p>If we log out and log in again, we are going to see a new scope in the <code>Consent<\/code> screen:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/07\/Additional-claim-in-the-consent-screen.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-53345 size-full\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/07\/Additional-claim-in-the-consent-screen.png\" alt=\"Additional claim in the consent screen\" width=\"652\" height=\"599\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/07\/Additional-claim-in-the-consent-screen.png 652w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/07\/Additional-claim-in-the-consent-screen-300x276.png 300w\" sizes=\"auto, (max-width: 652px) 100vw, 652px\" \/><\/a><\/p>\n<p>But, if we inspect the Privacy page, we won\u2019t be able to find the address claim there. That\u2019s because we didn\u2019t map it to our claims. Of course, we can inspect the console logs to make sure the IdentityServer returned our new claim:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/38-Address-claim.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-52238\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/38-Address-claim.png\" alt=\"Address claim\" width=\"839\" height=\"68\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/38-Address-claim.png 839w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/38-Address-claim-300x24.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/38-Address-claim-768x62.png 768w\" sizes=\"auto, (max-width: 839px) 100vw, 839px\" \/><\/a><\/p>\n<p>But if we want to include it, we can modify the OIDC configuration:<\/p>\n<p><code>opt.ClaimActions.MapUniqueJsonKey(\"address\", \"address\");<\/code><\/p>\n<p>After we log in again, we can find the address claim on the Privacy page.<\/p>\n<p>We just want to mention if you don\u2019t need all the additional claims for your entire application but just for one part of it, the best practice is not to map all the claims. You can always get them with the <code>IdentityModel<\/code> package by sending the request to the <code>\/userinfo<\/code> endpoint. By doing that, we ensure our cookies are small in size and that we get always up-to-date information from the userinfo endpoint.<\/p>\n<h2 id=\"userinfo-endpoint\">Getting Claims Manually from the UserInfo Endpoint<\/h2>\n<p>So, let\u2019s see how we can extract the address claim from the <code>\/userinfo<\/code> endpoint. The first thing we have to do is to remove the <code>MapUniqueJsonKey(\u201eaddress\u201c, \u201eaddress\u201c)<\/code> statement from the OIDC configuration.<\/p>\n<p>Then, let\u2019s install the required package:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/39-IdentityModel-package.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-52239\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/39-IdentityModel-package.png\" alt=\"IdentityModel package\" width=\"446\" height=\"57\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/39-IdentityModel-package.png 446w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/39-IdentityModel-package-300x38.png 300w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/39-IdentityModel-package-440x57.png 440w\" sizes=\"auto, (max-width: 446px) 100vw, 446px\" \/><\/a><\/p>\n<p>After that, let\u2019s modify the <code>Privacy<\/code> action in the <code>Home<\/code> controller:<\/p>\n<pre  class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public async Task&lt;IActionResult&gt; Privacy()\r\n{\r\n    var client = new HttpClient();\r\n    var metaDataResponse = await client.GetDiscoveryDocumentAsync(\"https:\/\/localhost:5005\");\r\n\r\n    var accessToken = await HttpContext.GetTokenAsync(OpenIdConnectParameterNames.AccessToken);\r\n\r\n    var response = await client.GetUserInfoAsync(new UserInfoRequest\r\n    {\r\n        Address = metaDataResponse.UserInfoEndpoint,\r\n        Token = accessToken\r\n    });\r\n\r\n    if(response.IsError)\r\n    {\r\n        throw new Exception(\"Problem while fetching data from the UserInfo endpoint\", response.Exception);\r\n    }\r\n\r\n    var addressClaim = response.Claims.FirstOrDefault(c =&gt; c.Type.Equals(\"address\"));\r\n\r\n    User.AddIdentity(new ClaimsIdentity(new List&lt;Claim&gt; { new Claim(addressClaim.Type.ToString(), addressClaim.Value.ToString()) }));\r\n\r\n    return View();\r\n}\r\n<\/pre>\n<p>So, we create a new client object and fetch the response from the IdentityServer with the <code>GetDiscoveryDocumentAsync<\/code> method. This response contains our required <code>\/userinfo<\/code> endpoint\u2019s address. After that, we extract the access token and use the <code>UserInfo<\/code> address and extracted token to fetch the required user information. If the response is successful, we extract the address claim from the claims list and just add it to the <code>User.Claims<\/code> list (this is the list of Claims we iterate through in the Privacy view).<\/p>\n<p>Now, if we log in again, and navigate to the Privacy page, we are going to see the address claim again. But this time, we extracted it manually. So basically, we can use this code only when we need it in our application.<\/p>\n<h2 id=\"authorization\">IdentityServer4 Authorization<\/h2>\n<p>Authorization is the process of determining what you are allowed to do once authenticated. The <code>id_token<\/code> helps us with the authentication process while the <code>access_token<\/code> helps us with the authorization process because it authorizes a web client application to communicate with the web api.<\/p>\n<p>So, let\u2019s start with the <code>InMemoryConfig<\/code> class modification, by adding roles to our users:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"13,25\" data-enlighter-title=\"\">public static List&lt;TestUser&gt; GetUsers() =&gt;\r\n    new List&lt;TestUser&gt;\r\n    {\r\n        new TestUser\r\n        {\r\n\t     \/\/previous code\r\n\r\n            Claims = new List&lt;Claim&gt;\r\n            {\r\n                new Claim(&quot;given_name&quot;, &quot;Mick&quot;),\r\n                new Claim(&quot;family_name&quot;, &quot;Mining&quot;),\r\n                new Claim(&quot;address&quot;, &quot;Sunny Street 4&quot;),\r\n                new Claim(&quot;role&quot;, &quot;Admin&quot;)\r\n            }\r\n        },\r\n        new TestUser\r\n        {\r\n            \/\/previous code\r\n\r\n            Claims = new List&lt;Claim&gt;\r\n            {\r\n                new Claim(&quot;given_name&quot;, &quot;Jane&quot;),\r\n                new Claim(&quot;family_name&quot;, &quot;Downing&quot;),\r\n                new Claim(&quot;address&quot;, &quot;Long Avenue 289&quot;),\r\n                new Claim(&quot;role&quot;, &quot;Visitor&quot;)\r\n            }\r\n        }\r\n   };\r\n<\/pre><\/p>\n<p>We have to create a new identity scope in the <code>GetIdentityResources<\/code> method:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"7\" data-enlighter-title=\"\">public static IEnumerable&lt;IdentityResource&gt; GetIdentityResources() =&gt;\r\n    new List&lt;IdentityResource&gt;\r\n    {\r\n        new IdentityResources.OpenId(),\r\n        new IdentityResources.Profile(),\r\n        new IdentityResources.Address(),\r\n        new IdentityResource(&quot;roles&quot;, &quot;User role(s)&quot;, new List&lt;string&gt; { &quot;role&quot; })\r\n    };\r\n<\/pre><\/p>\n<p>And, we have to add roles scope to the allowed scopes for our MVC Client:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"6\" data-enlighter-title=\"\">AllowedScopes =\r\n{ \r\n    IdentityServerConstants.StandardScopes.OpenId, \r\n    IdentityServerConstants.StandardScopes.Profile, \r\n    IdentityServerConstants.StandardScopes.Address,\r\n    &quot;roles&quot;\r\n},\r\n<\/pre><\/p>\n<p>With this, we have finished the modification of the IDP application. Let\u2019s continue with the client application by modifying the OIDC configuration to support roles scope:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"8-9\" data-enlighter-title=\"\">.AddOpenIdConnect(&quot;oidc&quot;, opt =&gt;\r\n{\r\n    \/\/previous code\r\n\r\n    opt.Scope.Add(&quot;address&quot;);\r\n    \/\/opt.ClaimActions.MapUniqueJsonKey(&quot;address&quot;, &quot;address&quot;);\r\n\r\n    opt.Scope.Add(&quot;roles&quot;);\r\n    opt.ClaimActions.MapUniqueJsonKey(&quot;role&quot;, &quot;role&quot;);\r\n});\r\n<\/pre><\/p>\n<p>So, we want to allow Create, Edit, Details, and Delete actions only to users with the Admin role. To do that, we are going to modify the <code>Index<\/code> view:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xhtml\" data-enlighter-highlight=\"1-6,17-24\" data-enlighter-title=\"\">@if (User.IsInRole(&quot;Admin&quot;))\r\n{\r\n    &lt;p&gt;\r\n        &lt;a asp-action=&quot;Create&quot;&gt;Create New&lt;\/a&gt;\r\n    &lt;\/p&gt;\r\n}\r\n&lt;table class=&quot;table&quot;&gt;\r\n    \r\n    \/\/previous code\r\n\r\n    &lt;tbody&gt;\r\n        @foreach (var item in Model)\r\n        {\r\n            &lt;tr&gt;\r\n                \/\/previous code\r\n\r\n                @if (User.IsInRole(&quot;Admin&quot;))\r\n                {\r\n                    &lt;td&gt;\r\n                        @Html.ActionLink(&quot;Edit&quot;, &quot;Edit&quot;, new { \/* id=item.PrimaryKey *\/ }) |\r\n                        @Html.ActionLink(&quot;Details&quot;, &quot;Details&quot;, new { \/* id=item.PrimaryKey *\/ }) |\r\n                        @Html.ActionLink(&quot;Delete&quot;, &quot;Delete&quot;, new { \/* id=item.PrimaryKey *\/ })\r\n                    &lt;\/td&gt;\r\n                }\r\n            &lt;\/tr&gt;\r\n        }\r\n    &lt;\/tbody&gt;\r\n&lt;\/table&gt;\r\n<\/pre><\/p>\n<p>We use the <code>IsInRole<\/code> method to allow only Admin users to see these links.<\/p>\n<p>Finally, we have to state where our framework can find the user\u2019s role:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"8-11\" data-enlighter-title=\"\">.AddOpenIdConnect(&quot;oidc&quot;, opt =&gt;\r\n{\r\n    \/\/previous code\r\n\r\n    opt.Scope.Add(&quot;roles&quot;);\r\n    opt.ClaimActions.MapUniqueJsonKey(&quot;roles&quot;, &quot;role&quot;);\r\n\r\n    opt.TokenValidationParameters = new TokenValidationParameters\r\n    {\r\n        RoleClaimType = &quot;role&quot;\r\n    };\r\n});\r\n<\/pre><\/p>\n<p>The <code>TokenValidationParameters<\/code> class exists in the <code>Microsoft.IdentityModel.Tokens<\/code> namespace.<\/p>\n<p>Now, we can start our applications and login with Jane&#8217;s account:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/40-Roles-added.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-52241\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/40-Roles-added.png\" alt=\"Roles added - IdentityServer4 Authorization\" width=\"534\" height=\"268\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/40-Roles-added.png 534w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/40-Roles-added-300x151.png 300w\" sizes=\"auto, (max-width: 534px) 100vw, 534px\" \/><\/a><\/p>\n<p>We can see an additional scope in the Consent screen. Once we allow this, we can see the Index view but without additional actions. That\u2019s because Jane is in the Visitor role. If we log out and log in with Mick, we are going to see those links for sure.<\/p>\n<p>Excellent.<\/p>\n<p>But can we protect our endpoints with roles as well? Of course, we can. Let\u2019s see how to do it.<\/p>\n<h2 id=\"using-roles\">Using Roles to Protect Endpoints<\/h2>\n<p>Le\u2019s say, for example, only the Admin users can access the Privacy page. Well, with the same action we did in a previous part, we can show the Privacy link in the <code>_Layout<\/code> view:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xhtml\" data-enlighter-highlight=\"5-10\" data-enlighter-title=\"\">&lt;ul class=&quot;navbar-nav flex-grow-1&quot;&gt;\r\n    &lt;li class=&quot;nav-item&quot;&gt;\r\n        &lt;a class=&quot;nav-link text-dark&quot; asp-area=&quot;&quot; asp-controller=&quot;Home&quot; asp-action=&quot;Index&quot;&gt;Home&lt;\/a&gt;\r\n    &lt;\/li&gt;\r\n    @if (User.IsInRole(&quot;Admin&quot;))\r\n    {\r\n        &lt;li class=&quot;nav-item&quot;&gt;\r\n            &lt;a class=&quot;nav-link text-dark&quot; asp-area=&quot;&quot; asp-controller=&quot;Home&quot; asp-action=&quot;Privacy&quot;&gt;Privacy&lt;\/a&gt;\r\n        &lt;\/li&gt;\r\n    }\r\n    @if (User.Identity.IsAuthenticated)\r\n    {\r\n        &lt;li class=&quot;nav-item&quot;&gt;\r\n            &lt;a class=&quot;nav-link text-dark&quot; asp-area=&quot;&quot; asp-controller=&quot;Authentication&quot; asp-action=&quot;Logout&quot;&gt;Logout&lt;\/a&gt;\r\n        &lt;\/li&gt;\r\n    }\r\n&lt;\/ul&gt;\r\n<\/pre><\/p>\n<p>If we log in as Jane, we won\u2019t be able to see the Privacy link:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/41-Hidden-Link-Privacy.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-52244\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/41-Hidden-Link-Privacy.png\" alt=\"Hidden Link Privacy - IdentityServer4 Authorization\" width=\"465\" height=\"109\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/41-Hidden-Link-Privacy.png 465w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/41-Hidden-Link-Privacy-300x70.png 300w\" sizes=\"auto, (max-width: 465px) 100vw, 465px\" \/><\/a><\/p>\n<p>Even though we can\u2019t see the Privacy link, we still have access to the Privacy page by entering a valid URI address:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/42-Accessed-Privacy-over-URI.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-52245\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/42-Accessed-Privacy-over-URI.png\" alt=\"Accessed Privacy over URI\" width=\"680\" height=\"204\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/42-Accessed-Privacy-over-URI.png 680w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/42-Accessed-Privacy-over-URI-300x90.png 300w\" sizes=\"auto, (max-width: 680px) 100vw, 680px\" \/><\/a><\/p>\n<p>So, what we have to do is to protect our Privacy endpoint with the user\u2019s role:<\/p>\n<p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-highlight=\"1\" data-enlighter-title=\"\">[Authorize(Roles = &quot;Admin&quot;)]\r\npublic async Task&lt;IActionResult&gt; Privacy()\r\n<\/pre><\/p>\n<p>Now, if we log out, log in again as Jane, and try to use the URI address to access the privacy page, we won\u2019t be able to do that:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/43-Access-Denied.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-52246\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/43-Access-Denied.png\" alt=\"IdentityServer4 Authorization Access Denied\" width=\"631\" height=\"286\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/43-Access-Denied.png 631w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/43-Access-Denied-300x136.png 300w\" sizes=\"auto, (max-width: 631px) 100vw, 631px\" \/><\/a><\/p>\n<p>The application redirects us to the \/Account\/AccessDenied page, but we get 404 because we don\u2019t have that page.<\/p>\n<p>So, let\u2019s create it.<\/p>\n<p>The first thing we are going to do is to add a new action in the Account controller:<\/p>\n<pre  class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public IActionResult AccessDenied()\r\n{\r\n    return View();\r\n}\r\n<\/pre>\n<p>And, let\u2019s create a view for this action:<\/p>\n<pre  class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">@{\r\n    ViewData[\"Title\"] = \"AccessDenied\";\r\n}\r\n\r\n&lt;h1&gt;AccessDenied&lt;\/h1&gt;\r\n\r\n&lt;h3&gt;You are not authorized to view this page.&lt;\/h3&gt;\r\n\r\n&lt;p&gt;\r\n    You can always &lt;a asp-controller=\"Account\" asp-action=\"Logout\"&gt;log in as someone else&lt;\/a&gt;.\r\n&lt;\/p&gt;\r\n<\/pre>\n<p>After these changes, we can log out and log in as Jane. Once we navigate to the \/Home\/Privacy URI, we are going to be redirected to the AccessDenied page:<\/p>\n<p><a href=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/44-Access-Denied-Page.png\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone size-full wp-image-52247\" src=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/44-Access-Denied-Page.png\" alt=\"Access Denied Page - IdentityServer4 Authorization\" width=\"549\" height=\"232\" srcset=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/44-Access-Denied-Page.png 549w, https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/44-Access-Denied-Page-300x127.png 300w\" sizes=\"auto, (max-width: 549px) 100vw, 549px\" \/><\/a><\/p>\n<p>So, this works as we expect it to do.<\/p>\n<p>We want to mention one more thing. If you create this action in a controller with a different name, you have to add additional mapping in the <code>AddCookie<\/code> method in the Client application:<\/p>\n<pre  class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">.AddCookie(\"Cookies\", (opt) =&gt; \r\n{\r\n    opt.AccessDeniedPath = \"\/ControllerName\/AccessDenied\";\r\n})\r\n<\/pre>\n<p>With this configuration, we are adding a different address for the AccessDenied action.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>Let\u2019s sum up everything.<\/p>\n<p>We have learned:<\/p>\n<ul>\n<li>How to modify claims and add additional ones<\/li>\n<li>The way to get claims manually from the <code>\/userinfo<\/code> endpoint<\/li>\n<li>How to setup authorization<\/li>\n<li>And how to use roles for authorization purposes<\/li>\n<\/ul>\n<p>In the next article, we are going to learn <a href=\"https:\/\/code-maze.com\/securing-webapi-hybrid-flow\/\" target=\"_blank\" rel=\"noopener noreferrer\">how to protect our Web API with the Hybrid flow<\/a>.<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We can use claims to show identity-related information in our application but, we can use it for the authorization process as well. In this article, we are going to learn how to modify our claims and add new ones. Additionally, we are going to learn about the IdentityServer4 Authorization process and how to use Roles [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":52264,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":"","footnotes":""},"categories":[12],"tags":[74,416,658,661,676,415],"class_list":["post-52234","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-csharp","tag-authorization","tag-claims","tag-identityserver4","tag-oauth2","tag-oidc","tag-roles","et-has-post-format-content","et_post_format-et-post-format-standard"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v24.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>IdentityServer4 Authorization and Working with Claims - Code Maze<\/title>\n<meta name=\"description\" content=\"We are going to learn how to add new claims and modify existing ones. Additionally, we are going to learn about the IdentityServer4 Authorization process.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/code-maze.com\/identityserver4-authorization\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"IdentityServer4 Authorization and Working with Claims - Code Maze\" \/>\n<meta property=\"og:description\" content=\"We are going to learn how to add new claims and modify existing ones. Additionally, we are going to learn about the IdentityServer4 Authorization process.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/code-maze.com\/identityserver4-authorization\/\" \/>\n<meta property=\"og:site_name\" content=\"Code Maze\" \/>\n<meta property=\"article:published_time\" content=\"2020-04-27T06:00:19+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-01-11T15:53:50+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1100\" \/>\n\t<meta property=\"og:image:height\" content=\"620\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Marinko Spasojevi\u0107\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@https:\/\/twitter.com\/CodeMazeBlog\" \/>\n<meta name=\"twitter:site\" content=\"@CodeMazeBlog\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Marinko Spasojevi\u0107\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"11 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":[\"Article\",\"BlogPosting\"],\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/\"},\"author\":{\"name\":\"Marinko Spasojevi\u0107\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/person\/d6fa06e66820968d19b39fb63cff2533\"},\"headline\":\"IdentityServer4 Authorization and Working with Claims\",\"datePublished\":\"2020-04-27T06:00:19+00:00\",\"dateModified\":\"2022-01-11T15:53:50+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/\"},\"wordCount\":1372,\"commentCount\":16,\"publisher\":{\"@id\":\"https:\/\/code-maze.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png\",\"keywords\":[\"authorization\",\"claims\",\"IdentityServer4\",\"OAuth2\",\"OIDC\",\"roles\"],\"articleSection\":[\"C#\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/code-maze.com\/identityserver4-authorization\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/\",\"url\":\"https:\/\/code-maze.com\/identityserver4-authorization\/\",\"name\":\"IdentityServer4 Authorization and Working with Claims - Code Maze\",\"isPartOf\":{\"@id\":\"https:\/\/code-maze.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png\",\"datePublished\":\"2020-04-27T06:00:19+00:00\",\"dateModified\":\"2022-01-11T15:53:50+00:00\",\"description\":\"We are going to learn how to add new claims and modify existing ones. Additionally, we are going to learn about the IdentityServer4 Authorization process.\",\"breadcrumb\":{\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/code-maze.com\/identityserver4-authorization\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/#primaryimage\",\"url\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png\",\"contentUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png\",\"width\":1100,\"height\":620,\"caption\":\"IdentityServer4 Authorization\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/code-maze.com\/identityserver4-authorization\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/code-maze.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"IdentityServer4 Authorization and Working with Claims\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/code-maze.com\/#website\",\"url\":\"https:\/\/code-maze.com\/\",\"name\":\"Code Maze\",\"description\":\"Learn. Code. Succeed.\",\"publisher\":{\"@id\":\"https:\/\/code-maze.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/code-maze.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/code-maze.com\/#organization\",\"name\":\"Code Maze\",\"url\":\"https:\/\/code-maze.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png\",\"contentUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png\",\"width\":3511,\"height\":3510,\"caption\":\"Code Maze\"},\"image\":{\"@id\":\"https:\/\/code-maze.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/x.com\/CodeMazeBlog\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/person\/d6fa06e66820968d19b39fb63cff2533\",\"name\":\"Marinko Spasojevi\u0107\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/code-maze.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/marinko-1x1-3-150x150.jpg\",\"contentUrl\":\"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/marinko-1x1-3-150x150.jpg\",\"caption\":\"Marinko Spasojevi\u0107\"},\"description\":\"Hi, my name is Marinko Spasojevic. Currently, I work as a full-time .NET developer and my passion is web application development. Just getting something to work is not enough for me. To make it just how I like it, it must be readable, reusable, and easy to maintain. Prior to being an author on the CodeMaze blog, I had been working as a professor of Computer Science for several years. So, sharing knowledge while working as a full-time developer comes naturally to me.\",\"sameAs\":[\"https:\/\/www.linkedin.com\/in\/marinko-spasojevic\/\",\"https:\/\/x.com\/https:\/\/twitter.com\/CodeMazeBlog\"],\"url\":\"https:\/\/code-maze.com\/author\/marinko\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"IdentityServer4 Authorization and Working with Claims - Code Maze","description":"We are going to learn how to add new claims and modify existing ones. Additionally, we are going to learn about the IdentityServer4 Authorization process.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/code-maze.com\/identityserver4-authorization\/","og_locale":"en_US","og_type":"article","og_title":"IdentityServer4 Authorization and Working with Claims - Code Maze","og_description":"We are going to learn how to add new claims and modify existing ones. Additionally, we are going to learn about the IdentityServer4 Authorization process.","og_url":"https:\/\/code-maze.com\/identityserver4-authorization\/","og_site_name":"Code Maze","article_published_time":"2020-04-27T06:00:19+00:00","article_modified_time":"2022-01-11T15:53:50+00:00","og_image":[{"width":1100,"height":620,"url":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png","type":"image\/png"}],"author":"Marinko Spasojevi\u0107","twitter_card":"summary_large_image","twitter_creator":"@https:\/\/twitter.com\/CodeMazeBlog","twitter_site":"@CodeMazeBlog","twitter_misc":{"Written by":"Marinko Spasojevi\u0107","Est. reading time":"11 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":["Article","BlogPosting"],"@id":"https:\/\/code-maze.com\/identityserver4-authorization\/#article","isPartOf":{"@id":"https:\/\/code-maze.com\/identityserver4-authorization\/"},"author":{"name":"Marinko Spasojevi\u0107","@id":"https:\/\/code-maze.com\/#\/schema\/person\/d6fa06e66820968d19b39fb63cff2533"},"headline":"IdentityServer4 Authorization and Working with Claims","datePublished":"2020-04-27T06:00:19+00:00","dateModified":"2022-01-11T15:53:50+00:00","mainEntityOfPage":{"@id":"https:\/\/code-maze.com\/identityserver4-authorization\/"},"wordCount":1372,"commentCount":16,"publisher":{"@id":"https:\/\/code-maze.com\/#organization"},"image":{"@id":"https:\/\/code-maze.com\/identityserver4-authorization\/#primaryimage"},"thumbnailUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png","keywords":["authorization","claims","IdentityServer4","OAuth2","OIDC","roles"],"articleSection":["C#"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/code-maze.com\/identityserver4-authorization\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/code-maze.com\/identityserver4-authorization\/","url":"https:\/\/code-maze.com\/identityserver4-authorization\/","name":"IdentityServer4 Authorization and Working with Claims - Code Maze","isPartOf":{"@id":"https:\/\/code-maze.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/code-maze.com\/identityserver4-authorization\/#primaryimage"},"image":{"@id":"https:\/\/code-maze.com\/identityserver4-authorization\/#primaryimage"},"thumbnailUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png","datePublished":"2020-04-27T06:00:19+00:00","dateModified":"2022-01-11T15:53:50+00:00","description":"We are going to learn how to add new claims and modify existing ones. Additionally, we are going to learn about the IdentityServer4 Authorization process.","breadcrumb":{"@id":"https:\/\/code-maze.com\/identityserver4-authorization\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/code-maze.com\/identityserver4-authorization\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/code-maze.com\/identityserver4-authorization\/#primaryimage","url":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png","contentUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/04\/Authorization-and-Claims.png","width":1100,"height":620,"caption":"IdentityServer4 Authorization"},{"@type":"BreadcrumbList","@id":"https:\/\/code-maze.com\/identityserver4-authorization\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/code-maze.com\/"},{"@type":"ListItem","position":2,"name":"IdentityServer4 Authorization and Working with Claims"}]},{"@type":"WebSite","@id":"https:\/\/code-maze.com\/#website","url":"https:\/\/code-maze.com\/","name":"Code Maze","description":"Learn. Code. Succeed.","publisher":{"@id":"https:\/\/code-maze.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/code-maze.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/code-maze.com\/#organization","name":"Code Maze","url":"https:\/\/code-maze.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/code-maze.com\/#\/schema\/logo\/image\/","url":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png","contentUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/Code-Maze-Only-Logo-Transparent-HRez.png","width":3511,"height":3510,"caption":"Code Maze"},"image":{"@id":"https:\/\/code-maze.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/CodeMazeBlog"]},{"@type":"Person","@id":"https:\/\/code-maze.com\/#\/schema\/person\/d6fa06e66820968d19b39fb63cff2533","name":"Marinko Spasojevi\u0107","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/code-maze.com\/#\/schema\/person\/image\/","url":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/marinko-1x1-3-150x150.jpg","contentUrl":"https:\/\/code-maze.com\/wp-content\/uploads\/2020\/01\/marinko-1x1-3-150x150.jpg","caption":"Marinko Spasojevi\u0107"},"description":"Hi, my name is Marinko Spasojevic. Currently, I work as a full-time .NET developer and my passion is web application development. Just getting something to work is not enough for me. To make it just how I like it, it must be readable, reusable, and easy to maintain. Prior to being an author on the CodeMaze blog, I had been working as a professor of Computer Science for several years. So, sharing knowledge while working as a full-time developer comes naturally to me.","sameAs":["https:\/\/www.linkedin.com\/in\/marinko-spasojevic\/","https:\/\/x.com\/https:\/\/twitter.com\/CodeMazeBlog"],"url":"https:\/\/code-maze.com\/author\/marinko\/"}]}},"_links":{"self":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts\/52234","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/comments?post=52234"}],"version-history":[{"count":1,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts\/52234\/revisions"}],"predecessor-version":[{"id":63283,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/posts\/52234\/revisions\/63283"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/media\/52264"}],"wp:attachment":[{"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/media?parent=52234"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/categories?post=52234"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/code-maze.com\/wp-json\/wp\/v2\/tags?post=52234"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}