{"id":3643,"date":"2019-04-03T17:24:17","date_gmt":"2019-04-03T17:24:17","guid":{"rendered":"https:\/\/wspelt.com\/?p=3643"},"modified":"2024-07-16T15:08:40","modified_gmt":"2024-07-16T15:08:40","slug":"secure-web-api-in-an-sharepoint-provider-hosted-app","status":"publish","type":"post","link":"https:\/\/wspelt.com\/?p=3643","title":{"rendered":"Secure Web API in an SharePoint provider hosted app"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Introduction<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">During a job I came across the following requirement:<br><em>Being able to call a web service in a secure manner from a provider hosted app (MVC application) on pages with client-side code (JavaScript).<\/em><br>I will elaborate on the implementation of this below.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What do you need<\/h2>\n\n\n\n<p class=\"has-normal-font-size wp-block-paragraph\">The starting situation is a created SharePoint provider-hosted app project.<br>As standard, you receive the Microsoft SharePoint CSOM assemblies and the TokenHelper class, of which the following assemblies are the most important:<br>\u25aa Microsoft.SharePoint.Client<br>\u25aa Microsoft.SharePoint.Client.Runtime<br><br>Also add the following NuGet packages:<br>\u25aa SharePointPnP.IdentityModel.Extensions<br>\u25aa SharePointPnPCoreOnline<br>You can also easily install this via the NuGet Package Manager console with the following commands:<br><em>Install-Package Microsoft.SharePointOnline.CSOM -Version 16.1.3912.1204<br>Install-Package SharePointPnP.IdentityModel.Extensions -Version 1.2.2<br>Install-Package SharePointPnPCoreOnline -Version 2.19.1710.2<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setup of the Web API Service<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Add an empty Web API 2 Controller to the project.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"333\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/1-1024x333.png\" alt=\"Ading an empty Web API 2 Controller\" class=\"wp-image-3645\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/1-1024x333.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/1-300x98.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/1-768x250.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/1-800x261.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/1.png 1213w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Ading an empty Web API 2 Controller<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Then add the \u201cregister\u201d method below to this controller. This is intended as a starting point in the authentication process.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"273\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/2-1024x273.png\" alt=\"\" class=\"wp-image-3646\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/2-1024x273.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/2-300x80.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/2-768x205.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/2-800x214.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/2.png 1274w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Adding the \u201cregister\u201d method to the controller<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This &#8220;register&#8221; method must be called when the user starts the SharePoint app.<br>From a WebForms project this can be done as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"158\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/3-1024x158.png\" alt=\"\" class=\"wp-image-3648\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/3-1024x158.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/3-300x46.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/3-768x119.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/3-800x124.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/3.png 1273w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Register web service from a WebForms project<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Or, if you are working in an MVC project:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"121\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/4-1024x121.png\" alt=\"\" class=\"wp-image-3649\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/4-1024x121.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/4-300x36.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/4-768x91.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/4-800x95.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/4.png 1273w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Register web service from an MVC project<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This is what happens behind the scenes when the &#8220;register&#8221; method is called:<br>1. The cache key is retrieved. This is a unique string for the combination of user, publisher username, SharePoint app, and SharePoint farm or SharePoint Online tenant.<br>2. A cookie with the name &#8220;service token&#8221; and the value of the cache key is added to the response page.<br>3. The context token, client ID, client secret, host web url and app web url are retrieved and used as input to call the &#8220;register&#8221; method.<br>4. The &#8220;register&#8221; Web API method calls the AddToCache method which requests an access token for the provided input. The supplied input, the requested access token and the associated refresh token are cached.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The web service can be called using clientside JavaScript as follows:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"466\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/5-1024x466.png\" alt=\"\" class=\"wp-image-3651\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/5-1024x466.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/5-300x137.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/5-768x349.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/5-800x364.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/5.png 1178w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Calling web service with clientside javascript<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The above call results in calling the Web API service. The following code can then be used in the service to &#8220;re-instantiate&#8221; the ClientContext of the calling user and thus perform actions on SharePoint:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1273\" height=\"642\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/6-1024x516.png\" alt=\"\" class=\"wp-image-3652\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/6-1024x516.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/6-300x151.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/6-768x387.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/6-800x403.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/6.png 1273w\" sizes=\"auto, (max-width: 1273px) 100vw, 1273px\" \/><figcaption class=\"wp-element-caption\"><em>Reinstantiate SharePoint ClientContext in the Web API<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This is what happens behind the scenes when the GetClientContext method is called:<br>1. The cache key is retrieved from the \u201cservice token\u201d cookie.<br>2. The access token is retrieved from the cache with the cache key.<br>3. If the access token has expired, the refresh token is used to obtain a new access token.<br>4. A SharePoint ClientContext object is created using the access token.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Authorized web service calls<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The cookie issued during registration of the Web API service is used to authorize the calling user: without the appropriate cookie, the user is not authorized to call the service. This is done by a custom ActionFilterAttribute implementation:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"321\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/7-1024x321.png\" alt=\"\" class=\"wp-image-3654\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/7-1024x321.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/7-300x94.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/7-768x241.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/7-800x251.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/7.png 1273w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Behind the scenes: the ActionFilterAttribute<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This makes it possible to simply \u201cdecorate\u201d the methods and\/or classes you want to protect in the Web API with the WebAPIContextFilter attribute, such as this class:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"195\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/8-1024x195.png\" alt=\"\" class=\"wp-image-3655\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/8-1024x195.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/8-300x57.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/8-768x146.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/8-800x153.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/8.png 1033w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Apply WebAPIContextFilter attribute to class<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Or like this method for example:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"232\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/9-1024x232.png\" alt=\"\" class=\"wp-image-3657\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/9-1024x232.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/9-300x68.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/9-768x174.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/9-800x181.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/9.png 1274w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Apply WebAPIContextFilter attribute to method<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The result of unauthorized calls is the following message in the response:<\/p>\n\n\n\n<p class=\"has-larger-font-size wp-block-paragraph\"><strong><em>&#8220;Service requestor is not registered: access denied&#8221;.<\/em><\/strong><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cross domain calls<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In my situation it was not necessary to make cross domain calls, but if you want to do that, this is also possible. See this site for more information: <a href=\"https:\/\/github.com\/SharePoint\/PnP\/tree\/master\/Samples\/Core.Services.Authenticate\">https:\/\/github.com\/SharePoint\/PnP\/tree\/master\/Samples\/Core.Services.Authenticate<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">HostedAppHostNameOverride in web.config<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The WebApiHelper.AddToCache method authenticates the user to SharePoint.<br>For this to work, it is not sufficient to only have the ClientId and ClientSecret as app settings in the web.config. The HostedAppHostNameOverride setting containing the hostname of the app is also important:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"118\" src=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/10-1024x118.png\" alt=\"\" class=\"wp-image-3659\" srcset=\"https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/10-1024x118.png 1024w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/10-300x34.png 300w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/10-768x88.png 768w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/10-800x92.png 800w, https:\/\/wspelt.com\/wp-content\/uploads\/2024\/07\/10.png 1271w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\"><em>Required settings in MVC application with HostedAppHostNameOverride<\/em><\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">If you do not do this, you will receive the following error message when starting your app:<br><em>&#8220;rd00155d516979&#8221; is not the intended audience &#8220;58471cbd-4d01-4567-92a1-c1adca8656b1\/spapp.mydomain.com@946d293d-00c0-43c1-9c20-e0e0314fd8d3&#8221;<\/em><br>The TokenHelper.ReadAndValidateContextToken function needs this setting to validate the audience.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Finally<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In the manner described above, it is possible to securely call a Web API web service from a provider hosted app (MVC application) on pages with client-side code (javascript).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">References<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/sharepoint.stackexchange.com\/questions\/201378\/pass-authentication-from-provider-hosted-add-in-to-external-web-service\">https:\/\/sharepoint.stackexchange.com\/questions\/201378\/pass-authentication-from-provider-hosted-add-in-to-external-web-service<\/a><br><a href=\"https:\/\/github.com\/SharePoint\/PnP\/tree\/master\/Samples\/Core.Services.Authenticate\">https:\/\/github.com\/SharePoint\/PnP\/tree\/master\/Samples\/Core.Services.Authenticate<\/a><br><a href=\"https:\/\/ypcode.wordpress.com\/2017\/12\/29\/sharepoint-add-in-custom-web-api-and-spfx\">https:\/\/ypcode.wordpress.com\/2017\/12\/29\/sharepoint-add-in-custom-web-api-and-spfx<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>During a job I came across the following requirement:<br \/>\nBeing able to call a web service in a secure manner from a provider hosted app (MVC application) on pages with client-side code (JavaScript).<br \/>\nI will elaborate on the implementation in this blog post.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_genesis_hide_title":false,"_genesis_hide_breadcrumbs":false,"_genesis_hide_singular_image":false,"_genesis_hide_footer_widgets":false,"_genesis_custom_body_class":"","_genesis_custom_post_class":"","_genesis_layout":"","footnotes":""},"categories":[25],"tags":[],"class_list":["post-3643","post","type-post","status-publish","format-standard","category-sharepoint","entry","has-post-thumbnail"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/wspelt.com\/index.php?rest_route=\/wp\/v2\/posts\/3643","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wspelt.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wspelt.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wspelt.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/wspelt.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3643"}],"version-history":[{"count":8,"href":"https:\/\/wspelt.com\/index.php?rest_route=\/wp\/v2\/posts\/3643\/revisions"}],"predecessor-version":[{"id":3661,"href":"https:\/\/wspelt.com\/index.php?rest_route=\/wp\/v2\/posts\/3643\/revisions\/3661"}],"wp:attachment":[{"href":"https:\/\/wspelt.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3643"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wspelt.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3643"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wspelt.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3643"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}