{"id":9545,"date":"2020-09-10T15:10:01","date_gmt":"2020-09-10T13:10:01","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=9545"},"modified":"2023-10-31T15:19:43","modified_gmt":"2023-10-31T14:19:43","slug":"oauth-2-0-on-behalf-flow-in-azure-active-directory-and-net-core","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/en\/oauth-2-0-on-behalf-flow-in-azure-active-directory-and-net-core\/","title":{"rendered":"OAuth 2.0 On Behalf flow in Azure Active Directory and .NET Core"},"content":{"rendered":"\n<p><span lang=\"EN-US\">There are couple of existing ways how to authenticate one app to another when you create a distributed system. You can write your own identity service and use it across your apps.<br>But here Azure Active Directory comes with its great features I personally love. In this post, I will show you how to configure one of those flows. <\/span><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">User wants to fetch its data<\/h2>\n\n\n\n<p>Let\u2019s assume we have a gateway service called Gateway.API and user service called User.API which preserves user data. We assume that nobody but user itself can access its data. But how User.API will know that the Gatway.API is requesting it in the name of the User? Here <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/v2-oauth2-on-behalf-of-flow\" rel=\"nofollow\" ><strong>OAuth2.0 On Behalf flow<\/strong><\/a> comes to the rescue.<\/p>\n\n\n\n<p>The main idea is to propagate user identity and its permissions to another element in a chain &#8211; to service which is requested by service requested by user. In this way we can easily configure access to specific resources thanks to AD.<\/p>\n\n\n\n<p>Full code sample I used in this blog post you can find on my <a href=\"https:\/\/github.com\/netsharpdev\/OnBehalf.Example\" rel=\"nofollow\" ><strong>Github<\/strong><\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">OAuth 2.0<\/h2>\n\n\n\n<p>What OAuth 2.0 is and how exactly it works you can read on its <a href=\"https:\/\/auth0.com\/docs\/protocols\/oauth2\" rel=\"nofollow\" >official website<\/a>. But let me give you a short introduction. OAuth2.0 is an authorization (not authentication) protocol. Basic idea is that client (application requesting access) asks Resource Owner to for authorization. Resource Owner grants authorization and in the next step client with authorization granted goes to the Authorization Server which returns access token to the client. Finally, our client can access requested resource.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">On Behalf<\/h2>\n\n\n\n<p>On Behalf flow means that application accessed by the user gains access to another resource on behalf of a user and all actions performed there are authorized with user token.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/On-Behalf-Flow-Diagram-Software-Development.png\"><img decoding=\"async\" width=\"482\" height=\"278\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/On-Behalf-Flow-Diagram-Software-Development.png\" alt=\"\" class=\"wp-image-9556\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/On-Behalf-Flow-Diagram-Software-Development.png 482w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/On-Behalf-Flow-Diagram-Software-Development-300x173.png 300w\" sizes=\"(max-width: 482px) 100vw, 482px\" \/><\/a><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><span lang=\"EN-US\">How to configure Azure Active Directory on Behalf flow?&nbsp;<\/span><\/h2>\n\n\n\n<p><span lang=\"EN-US\">Configuration of Azure Active Directory is quite simple. Basically, Azure AD already exists on our Azure account. To register a new app which will be using it we have to go to \u201cApp Registrations\u201d and click \u201cNew registration\u201d. Then you should see the following screen.<\/span><\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/Azure-Portal-Register-Application-Software-Development-e1596439566697.png\"><img decoding=\"async\" width=\"833\" height=\"386\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/Azure-Portal-Register-Application-Software-Development-e1596439566697.png\" alt=\"\" class=\"wp-image-9560\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/Azure-Portal-Register-Application-Software-Development-e1596439566697.png 833w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/Azure-Portal-Register-Application-Software-Development-e1596439566697-300x139.png 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/Azure-Portal-Register-Application-Software-Development-e1596439566697-768x356.png 768w\" sizes=\"(max-width: 833px) 100vw, 833px\" \/><\/a><\/figure>\n\n\n\n<p>In the <strong>Name<\/strong> field we might type whatever we want to correctly identify the app. More important are next two sections.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Supported account types &#8211; tells the AD if we want to allow to login only users from our tenant (company) or also grant access to other tenants \/ personal accounts. I want my app to be used only in my company so I will go for the first option. Meaning, only people with users created for this tenant will be able to login. They don\u2019t have to be in the same domain name like <strong>@netsharpdev.com<\/strong>. Users you can define in section called <strong>Users<\/strong> within Azure Active Directory section.<\/li>\n\n\n\n<li>Redirect URI &#8211; this is optional option, but if we want to authorize with impersonate flow, we need to setup a redirect url. It is an url of our app where we have an endpoint for authentication. If we use OpenIdConnect it is by default <strong>\/signin-oidc<\/strong>, but can be overwritten in <strong>Startup.cs<\/strong>. However in this case it is not needed because we authorize with bearer token claimed from Azure AD.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Configure Access<\/h2>\n\n\n\n<p>I have created one more app registration called <strong>test-client<\/strong>. It represents the client app which is meant to authenticate to <strong>test<\/strong> on behalf of authorized user.<\/p>\n\n\n\n<p>If you want to grant <strong>test-client<\/strong> access to <strong>test<\/strong> you have to do two steps. First of all, in <strong>test<\/strong> app registration you must create scope. You can have multiple scopes and decide on code level which scope gives access to which part of the app. I am going to create <strong>Weather.Read <\/strong>scope. I also selected that only Admin can grant consent, meaning that if anyone in the Azure AD will try to login to our test app and application will ask user for permission he will not be able to consent since he need an approval from Administrator and he will see this message.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/MS-Login-Need-Approval-Software-Development-e1596136877626.png\"><img decoding=\"async\" width=\"840\" height=\"404\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/MS-Login-Need-Approval-Software-Development-e1596136877626.png\" alt=\"\" class=\"wp-image-9566\"\/><\/a><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/Azure-Portal-Create-Scope-Software-Development-e1596136935300.png\"><img decoding=\"async\" width=\"840\" height=\"325\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/Azure-Portal-Create-Scope-Software-Development-e1596136935300.png\" alt=\"\" class=\"wp-image-9567\"\/><\/a><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p>Below you can see the view from Azure portal when I was creating scope for my app.<\/p>\n\n\n\n<p>In the second step, we must grant our client app access to specific scopes. That way user won\u2019t be asked to grant a consent for that app.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter\"><a href=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/Add-Client-App-Portal-Azure-Software-Development-e1596439932257.png\"><img decoding=\"async\" width=\"840\" height=\"380\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/Add-Client-App-Portal-Azure-Software-Development-e1596439932257.png\" alt=\"\" class=\"wp-image-9602\"\/><\/a><\/figure>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">How to implement on Behalf flow in .NET Core 3.1?<\/h2>\n\n\n\n<p>Currently, there is a library under development and still in prelease version written by Microsoft community to make it easier, but creating production apps we want to have stable implementation. However, I will use some of the extensions provided by this preview to avoid implementing complex solutions while the problem has already been solved. Stable version is expected to be released by the end of the year 2020.<\/p>\n\n\n\n<p>Let start with proper setup in appsettings.json. Here, we need to provide Azure AD <strong>instance<\/strong> which is by default <strong>https:\/\/login.microsoftonline.com\/<\/strong>. <strong>Domain<\/strong> and <strong>TenantId <\/strong>can be found in Active Directory overview. <strong>ClientId<\/strong> which is the id of our app registration. It binds our app with specific app registration.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">appsettings.json<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\n{\n\"AzureAd\": {\n\"Instance\": \"https:\/\/login.microsoftonline.com\/\",\n\"Domain\": \"netsharpdev.com\",\n\"TenantId\": \"80c55bf4-64c0-4b58-965e-f2398ba61766\",\n\"ClientId\": \"642b4ac0-ccc4-4e02-a626-47c24531820e\"\n},\n<\/pre><\/div>\n\n\n<p>Next, all you have to do in your Web API startup is the following line of code, which is adding Azure Active Directory bearer authorization into your app.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Startup.cs<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\tservices.AddMicrosoftWebApiAuthentication(Configuration);\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Securing App with Scope filtering<\/h2>\n\n\n\n<p>Let\u2019s assume we want our endpoint to prevent from accessing the users which do not have required scope within token. Same validation we should provide on issuer and audience to make sure we let in only authorized users. Here is the simple example of how to verify scope during processing request. We could also delegate this to attribute but for example purpose I left it in controller\u2019s action.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;HttpGet]\npublicIActionResultGet()\n{\nHttpContext.VerifyUserHasAnyAcceptedScope(scope);\nvar\nrng=newRandom();\nreturnOk(\nEnumerable.Range(1,5)\n.Select(\nindex=&gt;newWeatherForecast\n\nDate=DateTime.Now.AddDays(index),\nTemperatureC=rng.Next(-20,55),\nSummary=Summaries&#x5B;rng.Next(Summaries.Length)]\n})\n.ToArray());\n}\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Web Client Application<\/h2>\n\n\n\n<p>Last step is to configure our web client which is going to use OIDC (OpenIdConnect) and OAuth 2.0 flow. With latest library provided by Microsoft community it is super easy. All we have to do is to use following methods in startup.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Using statements<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing Microsoft.Identity.Web;\nusing Microsoft.Identity.Web.TokenCacheProviders.InMemory;\n<\/pre><\/div>\n\n\n<p>We should specify list of scopes application should be granted. You can use multiple scopes from multiple app registrations, always use <strong>clientId<\/strong> of resource you want to get scope.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Services configuration<\/h4>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nservices.AddMicrosoftWebAppAuthentication(Configuration)\n.AddMicrosoftWebAppCallsWebApi(Configuration,newList&lt;string&gt;(){&quot;api:\/\/642b4ac0-ccc4-4e02-a626-47c24531820e\/Weather.Read&quot;})\n;.AddInMemoryTokenCaches();\n<\/pre><\/div>\n\n\n<p>This registrations mean I am going to use OpenIdConnect with Azure AD Bearer scheme, tell my app to use MSAL and store Active Directory token in memory.<\/p>\n\n\n\n<p>Here is a small sample request made by my app. TokenAcquisition is an interface implemented within the Microsoft.Identity.Web library and it loads for us token from cache granted for defined scopes. That is how we can authorize to our Web API.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic asyncTaskRequestUserDataOnBehalfOfAuthenticatedUser(HttpContextcontext)\n{\nvar\nscopes=newList&lt;string&gt;{$&quot;api:\/\/642b4ac0-ccc4-4e02-a626-47c24531820e\/Weather.Read&quot;};\ntry\n{\nvar\ntoken=await_tokenAcquisition.GetAccessTokenForUserAsync(scopes);\nvar\nhttpClient=new HttpClient();\n \nvar\ndefaultRequestHeaders=httpClient.DefaultRequestHeaders;\nif(defaultRequestHeaders.Accept.All(m=&gt;m.MediaType!=&quot;application\/json&quot;))\nhttpClient.DefaultRequestHeaders.Accept.Add(\nnewMediaTypeWithQualityHeaderValue(&quot;application\/json&quot;));\n \ndefaultRequestHeaders.Authorization=newAuthenticationHeaderValue(&quot;Bearer&quot;,token);\n \nresponse=await httpClient.GetAsync(\n&quot;https:\/\/localhost:5051\/WeatherForecast&quot;);\n}\ncatch(Exception ex)\n{\n\/\/something went wrong\n}\n}\n<\/pre><\/div>\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;9545&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;7&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;4.4&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;4.4\\\/5 ( votes: 7)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;OAuth 2.0 On Behalf flow in Azure Active Directory and .NET Core&quot;,&quot;width&quot;:&quot;122.1&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 122.1px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            4.4\/5 ( votes: 7)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>There are couple of existing ways how to authenticate one app to another when you create a distributed system. You &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/en\/oauth-2-0-on-behalf-flow-in-azure-active-directory-and-net-core\/\">Continued<\/a><\/p>\n","protected":false},"author":254,"featured_media":9715,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1320],"tags":[1364,1396,1365,1398],"class_list":["post-9545","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-hard-development","tag-net-core-en","tag-active-directory-en","tag-asp-net-core-en","tag-azure-en"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2020\/07\/oauth.jpg","category_names":["Hard development"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/9545"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/users\/254"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/comments?post=9545"}],"version-history":[{"count":2,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/9545\/revisions"}],"predecessor-version":[{"id":25369,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/posts\/9545\/revisions\/25369"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media\/9715"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/media?parent=9545"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/categories?post=9545"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/en\/wp-json\/wp\/v2\/tags?post=9545"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}