{"id":7751,"date":"2021-05-26T12:07:33","date_gmt":"2021-05-26T10:07:33","guid":{"rendered":"https:\/\/blog.redbaronofazure.com\/?p=7751"},"modified":"2021-09-21T16:23:00","modified_gmt":"2021-09-21T14:23:00","slug":"using-approles-and-azure-ad-b2c-for-rbac","status":"publish","type":"post","link":"https:\/\/blog.redbaronofazure.com\/?p=7751","title":{"rendered":"Using AppRoles and Azure AD B2C for RBAC"},"content":{"rendered":"\n<p class=\"has-small-font-size\">Update: There is a follow up blog post that builds on this post to build <a rel=\"noreferrer noopener\" href=\"https:\/\/blog.redbaronofazure.com\/?post=7816\" target=\"_blank\">Delegated App Admin<\/a> using AppRoles.<\/p>\n\n\n\n<p>The goal of this article is to be able to create an Azure AD B2C JWT Token that contains the user&#8217;s roles so you can build apps like this<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1022\" height=\"565\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-04.png\" alt=\"\" class=\"wp-image-7757\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-04.png 1022w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-04-300x166.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-04-768x425.png 768w\" sizes=\"(max-width: 1022px) 100vw, 1022px\" \/><\/figure>\n\n\n\n<p>Azure AD B2C&#8217;s Identity Experience Framework doesn&#8217;t have built in support for RBAC features like roles and groups, but that doesn&#8217;t mean you can use them. With a little help of some Graph API code in a tiny Azure Function, you can create RBAC functionality in your B2C protected app. <\/p>\n\n\n\n<h3>Register a new Application<\/h3>\n\n\n\n<p>Register a new Application in B2C the following way<\/p>\n\n\n\n<ul><li>+ New Registration.<ul><li>Name = AspnetCoreMsal-AppRole (or whatever you prefer)<\/li><li>[x] Accounts in any identity provider or organizational directory<\/li><li>Redirect Uri = Web + https:\/\/localhost:5001<\/li><li>Register<\/li><\/ul><\/li><li>Authentication (if you want to test drive your B2C policy from the portal)<ul><li>Add https:\/\/jwt.ms as redirect uri<\/li><li>Implicit grant and hybrid flows, select both [x] id and [x] access tokens<\/li><\/ul><\/li><li>Then edit the App Manifest to add the AppRole as per below. <\/li><\/ul>\n\n\n\n<p>You create AppRoles by editing the App Manifest as per the documentation <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/howto-add-app-roles-in-azure-ad-apps\" target=\"_blank\">here<\/a>. If you copy-n-paste a text like below, you must replace the <em>id<\/em> with a new guid as they need to be unique. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">\"appRoles\": [\n    {\n        \"allowedMemberTypes\": [\n            \"User\"\n        ],\n        \"description\": \"Team Managers that can manage their team\",\n        \"displayName\": \"TeamManager\",\n        \"id\": \"9653f24c-a455-44dc-aaac-b027d9a8f62a\",\n        \"isEnabled\": true,\n        \"origin\": \"Application\",\n        \"value\": \"TeamManager\"\n    },<\/code><\/pre>\n\n\n\n<h3>Azure Function to do Graph API queries<\/h3>\n\n\n\n<p>To query the defined appRoles of an application, you need to do a Graph API query on the service principal object. The <em>client_id<\/em> will be the AppId which is passed as a parameter in the OIDC authorize flow call to B2C.  <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash line-numbers\">https:\/\/graph.microsoft.com\/v1.0\/servicePrincipals?$filter=appId eq '{client_id}' &amp;$select=id,appId,appRoles<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"973\" height=\"401\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-01.png\" alt=\"\" class=\"wp-image-7752\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-01.png 973w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-01-300x124.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-01-768x317.png 768w\" sizes=\"(max-width: 973px) 100vw, 973px\" \/><\/figure>\n\n\n\n<p>The second query we need to do is on the user object to see what roles the user has assigned. The <em>resourceId<\/em> is the ObjectId of the service principal and the appRoleId is the id of the appRoles item. So given the result for this user&#8217;s appRoleAssignment, we can determind that the user is a TeamManager.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash line-numbers\">https:\/\/graph.microsoft.com\/v1.0\/users\/{objectId}\/appRoleAssignments?$select=appRoleId,resourceId\n<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"788\" height=\"177\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-02.png\" alt=\"\" class=\"wp-image-7753\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-02.png 788w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-02-300x67.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2021\/05\/approles-b2c-02-768x173.png 768w\" sizes=\"(max-width: 788px) 100vw, 788px\" \/><\/figure>\n\n\n\n<p>So if we put the two graph queries in an <a href=\"https:\/\/github.com\/cljung\/B2C-AppRoles\/blob\/main\/AzFunc\/run.csx\" target=\"_blank\" rel=\"noreferrer noopener\">Azure Function<\/a>, join and filter their results (see the <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/cljung\/B2C-AppRoles\" target=\"_blank\">github repo<\/a>),  we can return the following result, given a user trying to sign in to an app. Notice, the Azure Function also returns group membership as we might also be interested in that. We will make B2C consume this result when we come to the B2C Custom Policies further down. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">{\n  \"roles\": [\n    \"TeamManager\"\n  ],\n  \"groups\": [\n    \"B2CTeamManagers\",\n  ]\n}<\/code><\/pre>\n\n\n\n<p>For details how to deploy the Azure Functions, see the github repo <a href=\"https:\/\/github.com\/cljung\/B2C-AppRoles\/tree\/main\/AzFunc\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/cljung\/B2C-AppRoles<\/a>.<\/p>\n\n\n\n<h3>Assigning an AppRole to a user<\/h3>\n\n\n\n<p>Once we have defined an AppRole in the manifest, how do we assign it to a user? Well, there are two ways. One way is to assign the user directly and the other is to assign it to a group which the user is a member of. Assuming we are using a group, these are the powershell commands to do it. You can also do this in portal.azure.com, but then you need to find your application in the Enterprise Application blade. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell line-numbers\"># 1. get the user\n$user = (Get-AzureADUser -SearchString \"Max Peck\")\n\n# 2. create the group\n$group = New-AzureADGroup -DisplayName \"B2CTeamManagers\" -MailEnabled $false -SecurityEnabled $true -MailNickName \"NotSet\"\n\n# 3. add the user as a member of the group\nAdd-AzureADGroupMember -ObjectId $group.ObjectID -RefObjectId $user.ObjectID\n\n# 4. get the app's service principal\n$spo =(Get-AzureADServicePrincipal -Filter \"DisplayName eq 'AspnetCoreMsal-Demo'\")\n\n# 5. find the role by name\n$roleAppAdmin =($spo.AppRoles | Where {$_.DisplayName -eq \"TeamManager\"})\n\n# 6. Assign the group to the AppRole\nNew-AzureADGroupAppRoleAssignment -ObjectId $group.ObjectId -PrincipalId $group.ObjectId -ResourceId $spo.ObjectId -Id $roleAppAdmin.id<\/code><\/pre>\n\n\n\n<p>The first three commands finds the user named <em>Max Peck<\/em>, creates a group named &#8220;B2CTeamManagers&#8221; and adds the user as a member of that group. The following three commands  finds the Service Principal object, locates the AppRole &#8220;TeamManager&#8221; and assigns the group to the role. You could do this in Graph API and then the trick would be to make a PATCH call to the groups appRoleAssignment endpoint.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">PATCH  https:\/\/graph.microsoft.com\/groups\/{objectId}\/appRoleAssignments<\/code><\/pre>\n\n\n\n<h3>B2C Custom Policy<\/h3>\n\n\n\n<p>In order to create a B2C Custom Policy that can generate an JWT access_token with the roles claim in it, you start by downloading the <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/Azure-Samples\/active-directory-b2c-custom-policy-starterpack\/tree\/master\/SocialAndLocalAccounts\" target=\"_blank\">SocialAndLocalAccounts<\/a> templates from the starter pack in github. Then you make the usual modifications with replacing <em>yourtenant<\/em>.onmicrosoft.com with your tenant name and you update the AppId guids in TrustFrameworkExtensions.xml (2&#215;2 places!). This is basic if you have worked with B2C. If you need help setting up your B2C tenant, you should follow this <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory-b2c\/custom-policy-get-started?tabs=applications#custom-policy-starter-pack\" target=\"_blank\">doc page<\/a>. <\/p>\n\n\n\n<p>Then, in <em>TrustFrameworkExtensions.xml<\/em>, we need to make several additions. First, we need to define the claims <em>groups<\/em> and <em>roles<\/em>. They both have the data type stringCollection. Put this text after the &lt;BuildingBlocks&gt; xml element.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml line-numbers\">&lt;ClaimsSchema&gt;\n    &lt;ClaimType Id=\"groups\"&gt;\n    &lt;DisplayName&gt;Comma delimited list of group names&lt;\/DisplayName&gt;\n    &lt;DataType&gt;stringCollection&lt;\/DataType&gt;\n    &lt;UserInputType&gt;Readonly&lt;\/UserInputType&gt;\n    &lt;\/ClaimType&gt;\n    &lt;ClaimType Id=\"roles\"&gt;\n    &lt;DisplayName&gt;Comma delimited list of AppRoleAssignment names&lt;\/DisplayName&gt;\n    &lt;DataType&gt;stringCollection&lt;\/DataType&gt;\n    &lt;UserInputType&gt;Readonly&lt;\/UserInputType&gt;\n    &lt;\/ClaimType&gt;\n&lt;\/ClaimsSchema&gt;\n<\/code><\/pre>\n\n\n\n<p> Next, we need to add a TechnicalProfile to talk to our Azure Function. For this, locate the ending &lt;\/ClaimsProviders&gt; and add before it the following<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml line-numbers\">&lt;!-- \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ REST APIs \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ --&gt;\n&lt;ClaimsProvider&gt;\n    &lt;DisplayName&gt;REST APIs&lt;\/DisplayName&gt;\n    &lt;TechnicalProfiles&gt;\n    &lt;TechnicalProfile Id=\"GetUserAppRoleAssignment\"&gt;\n        &lt;DisplayName&gt;Retrieves security groups assigned to the user&lt;\/DisplayName&gt;\n        &lt;Protocol Name=\"Proprietary\" Handler=\"Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null\" \/&gt;\n        &lt;Metadata&gt;\n        &lt;Item Key=\"ServiceUrl\"&gt;https:\/\/yourname.azurewebsites.net\/api\/GetAppRoleAssignmentsMSGraph?code=...&lt;\/Item&gt;\n        &lt;Item Key=\"AuthenticationType\"&gt;None&lt;\/Item&gt;\n        &lt;Item Key=\"SendClaimsIn\"&gt;Body&lt;\/Item&gt;\n        &lt;Item Key=\"AllowInsecureAuthInProduction\"&gt;true&lt;\/Item&gt;\n        &lt;Item Key=\"IncludeClaimResolvingInClaimsHandling\"&gt;true&lt;\/Item&gt;\n        &lt;\/Metadata&gt;\n        &lt;InputClaims&gt;\n        &lt;InputClaim Required=\"true\" ClaimTypeReferenceId=\"objectId\" \/&gt;\n        &lt;!-- this B2C tenant id --&gt;\n        &lt;InputClaim ClaimTypeReferenceId=\"tenantId\" DefaultValue=\"{Policy:TenantObjectId}\" \/&gt;\n        &lt;!-- The App we're signing in to --&gt;\n        &lt;InputClaim ClaimTypeReferenceId=\"client_id\" PartnerClaimType=\"clientId\"  DefaultValue=\"{OIDC:ClientId}\" \/&gt;\n        &lt;!-- specify that we want both roles and groups back --&gt;\n        &lt;InputClaim ClaimTypeReferenceId=\"scope\" DefaultValue=\"roles groups\" AlwaysUseDefaultValue=\"true\" \/&gt;\n        &lt;\/InputClaims&gt;\n        &lt;OutputClaims&gt;\n        &lt;OutputClaim ClaimTypeReferenceId=\"roles\" \/&gt;\n        &lt;OutputClaim ClaimTypeReferenceId=\"groups\" \/&gt;\n        &lt;OutputClaim ClaimTypeReferenceId=\"tenantId\" DefaultValue=\"{Policy:TenantObjectId}\" AlwaysUseDefaultValue=\"true\" \/&gt;\n        &lt;\/OutputClaims&gt;\n        &lt;UseTechnicalProfileForSessionManagement ReferenceId=\"SM-Noop\" \/&gt;\n    &lt;\/TechnicalProfile&gt;\n    &lt;\/TechnicalProfiles&gt;\n&lt;\/ClaimsProvider&gt;\n<\/code><\/pre>\n\n\n\n<p>Note that we are using to so called <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory-b2c\/claim-resolver-overview\" target=\"_blank\">Claims Resolvers<\/a> as a way to pass in the id of the current tenant (<em>{Policy:TenantObjectId}<\/em>) and the AppId (<em>{OIDC:ClientId}<\/em>). We also pass in a parameter named <em>scope<\/em> which is used to indicate to the Azure Functions that we want it to query for both group membership and user appRoleAssignements. In this example, we want to have both in the JWT access_token.<\/p>\n\n\n\n<p>Then in file <em>SignupOrSignin.xml<\/em> we need to make sure the Azure Function is called and that we return the claims in the token. For this reason make the following two changes.<\/p>\n\n\n\n<p>The first change is to change step 7 in the preconfigured SignUpOrSignin UserJourney to call our Azure Function. Then we renumber the SendClaims\/JwtIssuer step to be step number 8.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml line-numbers\">  &lt;UserJourneys&gt;\n    &lt;UserJourney Id=\"SignUpOrSignIn\"&gt;\n      &lt;OrchestrationSteps&gt;\n        &lt;!-- get AppRoleAssignment --&gt;\n        &lt;OrchestrationStep Order=\"7\" Type=\"ClaimsExchange\"&gt;\n          &lt;ClaimsExchanges&gt;\n            &lt;ClaimsExchange Id=\"GetUserAppRoleAssignment\" TechnicalProfileReferenceId=\"GetUserAppRoleAssignment\" \/&gt;\n          &lt;\/ClaimsExchanges&gt;\n        &lt;\/OrchestrationStep&gt;\n\n        &lt;OrchestrationStep Order=\"8\" Type=\"SendClaims\" CpimIssuerTechnicalProfileReferenceId=\"JwtIssuer\" \/&gt;\n      &lt;\/OrchestrationSteps&gt;\n      &lt;ClientDefinition ReferenceId=\"DefaultWeb\" \/&gt;\n    &lt;\/UserJourney&gt;\n  &lt;\/UserJourneys&gt;\n<\/code><\/pre>\n\n\n\n<p>The second change is to add the claims to the JWT token, and that we do by adding the claims to OutputClaims<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"xml\" class=\"language-xml line-numbers\">&lt;OutputClaims&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"displayName\" \/&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"givenName\" \/&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"surname\" \/&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"signInName\" \/&gt;                                         &lt;!-- LocalAccount: whatever used to sign in with--&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"signInNames.emailAddress\" PartnerClaimType=\"email\" \/&gt;  &lt;!-- LocalAccount: email --&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"email\" \/&gt;                                              &lt;!-- Other IDP: email --&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"groups\" \/&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"roles\" \/&gt;        \n    &lt;OutputClaim ClaimTypeReferenceId=\"objectId\" PartnerClaimType=\"sub\" \/&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"identityProvider\" \/&gt;\n    &lt;OutputClaim ClaimTypeReferenceId=\"tenantId\" AlwaysUseDefaultValue=\"true\" DefaultValue=\"{Policy:TenantObjectId}\" \/&gt;\n&lt;\/OutputClaims&gt;\n<\/code><\/pre>\n\n\n\n<p>With those changes, you can upload the B2C policies to your tenant.<\/p>\n\n\n\n<h3>Create your ASP.Net Core webapp<\/h3>\n\n\n\n<p>If you want something ready to git clone, you can use <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/Azure-Samples\/active-directory-aspnetcore-webapp-openidconnect-v2\/tree\/master\/4-WebApp-your-API\/4-2-B2C\" target=\"_blank\">this<\/a> official sample. If you prefer to create one from scratch, this is how you do it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell line-numbers\">md AspnetCoreMsal-demo\ncd AspnetCoreMsal-demo\ndotnet new mvc --auth SingleOrg --client-id &lt;AppId&gt; --tenant-id &lt;tenantId&gt;<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>You need to update your appsettings.json to look something like this. TenantId and ClientId should be auto-filled, but Instance and Domain needs to be updated and SignupSignInPolicyId needs to be added.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">  \"AzureAdB2C\": {\n    \"Instance\": \"https:\/\/yourtenant.b2clogin.com\/\",\n    \"Domain\": \"yourtenant.onmicrosoft.com\",\n    \"TenantId\": \"...your TenantId...\",\n    \"ClientId\": \"...your AppId...\",\n    \"ClientSecret\": \"... your App secret ...\",\n    \"SignUpSignInPolicyId\": \"B2C_1a_signup_signin\",\n    \"CallbackPath\": \"\/signin-oidc\"\n  },\n<\/code><\/pre>\n\n\n\n<p>Then change method ConfigureServices in Startup.cs to be as belo<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp line-numbers\">public void ConfigureServices(IServiceCollection services)\n{\nservices.AddMicrosoftIdentityWebAppAuthentication(Configuration, \"AzureAdB2C\");\n\n    services.Configure&lt;OpenIdConnectOptions&gt;(\n                OpenIdConnectDefaults.AuthenticationScheme, options =&gt;\n                {\n                   options.TokenValidationParameters.RoleClaimType = \"roles\";\n                });\n\n    services.AddAuthorization(policies =&gt;\n    {\n        policies.AddPolicy(\"TeamManager\", p =&gt;\n        {\n            p.RequireClaim(\"http:\/\/schemas.microsoft.com\/ws\/2008\/06\/identity\/claims\/role\", \"TeamManager\");\n        });\n    });\n    services.AddControllersWithViews(options =&gt;\n    {\n        var policy = new AuthorizationPolicyBuilder()\n            .RequireAuthenticatedUser()\n            .Build();\n        options.Filters.Add(new AuthorizeFilter(policy));\n    });\n    services.AddRazorPages()\n        .AddMicrosoftIdentityUI();\n}\n<\/code><\/pre>\n\n\n\n<p>Then add a View named TeamManager.cshtml and make sure the file contents look like this.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp line-numbers\">@{\n    ViewData[\"Title\"] = \"Team Manager\";\n}\n&lt;h1&gt;@ViewData[\"Title\"]&lt;\/h1&gt;\n\n&lt;p&gt;This is a page only for users with the role of Team Managers&lt;\/p&gt;\n<\/code><\/pre>\n\n\n\n<p>In file HomeController.cs, add this so we navigate to the Team Manager page.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"csharp\" class=\"language-csharp line-numbers\">[Authorize(Policy = \"TeamManager\")]\npublic IActionResult TeamManager() {\n       return View();\n}\n<\/code><\/pre>\n\n\n\n<p>And finally, in _Layout.cshtml, add the menu item for Team Managers after the Privacy menu item. It will be conditional and only show for logged in users with the role TeamManager.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"markup\" class=\"language-markup line-numbers\">&lt;li class=\"nav-item\"&gt;\n    &lt;a class=\"nav-link text-dark\" asp-area=\"\" asp-controller=\"Home\" asp-action=\"Privacy\"&gt;Privacy&lt;\/a&gt;\n&lt;\/li&gt;\n@if (User.Identity.IsAuthenticated &amp;&amp; User.HasClaim(System.Security.Claims.ClaimTypes.Role, \"TeamManager\")) {\n&lt;li class=\"nav-item\"&gt;\n    &lt;a class=\"nav-link text-dark\" asp-area=\"\" asp-controller=\"Home\" asp-action=\"TeamManager\"&gt;Team Manager&lt;\/a&gt;\n&lt;\/li&gt;\n}\n<\/code><\/pre>\n\n\n\n<p>Then, build your Visual Studio project and run it. I do not run it via IISExpress. If you do, you need to change the redirect uri as the port number will not be the same.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Update: There is a follow up blog post that builds on this post to build Delegated App Admin using AppRoles. The goal of this article is to be able to create an Azure AD B2C JWT Token that contains the user&#8217;s roles so you can build apps like this Azure AD B2C&#8217;s Identity Experience Framework [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[444],"tags":[449,448],"_links":{"self":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts\/7751"}],"collection":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=7751"}],"version-history":[{"count":19,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts\/7751\/revisions"}],"predecessor-version":[{"id":7831,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts\/7751\/revisions\/7831"}],"wp:attachment":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=7751"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=7751"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=7751"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}