Introduction
During a job I came across the following requirement:
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).
I will elaborate on the implementation of this below.
What do you need
The starting situation is a created SharePoint provider-hosted app project.
As standard, you receive the Microsoft SharePoint CSOM assemblies and the TokenHelper class, of which the following assemblies are the most important:
▪ Microsoft.SharePoint.Client
▪ Microsoft.SharePoint.Client.Runtime
Also add the following NuGet packages:
▪ SharePointPnP.IdentityModel.Extensions
▪ SharePointPnPCoreOnline
You can also easily install this via the NuGet Package Manager console with the following commands:
Install-Package Microsoft.SharePointOnline.CSOM -Version 16.1.3912.1204
Install-Package SharePointPnP.IdentityModel.Extensions -Version 1.2.2
Install-Package SharePointPnPCoreOnline -Version 2.19.1710.2
Setup of the Web API Service
Add an empty Web API 2 Controller to the project.
Then add the “register” method below to this controller. This is intended as a starting point in the authentication process.
This “register” method must be called when the user starts the SharePoint app.
From a WebForms project this can be done as follows:
Or, if you are working in an MVC project:
This is what happens behind the scenes when the “register” method is called:
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.
2. A cookie with the name “service token” and the value of the cache key is added to the response page.
3. The context token, client ID, client secret, host web url and app web url are retrieved and used as input to call the “register” method.
4. The “register” 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.
The web service can be called using clientside JavaScript as follows:
The above call results in calling the Web API service. The following code can then be used in the service to “re-instantiate” the ClientContext of the calling user and thus perform actions on SharePoint:
This is what happens behind the scenes when the GetClientContext method is called:
1. The cache key is retrieved from the “service token” cookie.
2. The access token is retrieved from the cache with the cache key.
3. If the access token has expired, the refresh token is used to obtain a new access token.
4. A SharePoint ClientContext object is created using the access token.
Authorized web service calls
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:
This makes it possible to simply “decorate” the methods and/or classes you want to protect in the Web API with the WebAPIContextFilter attribute, such as this class:
Or like this method for example:
The result of unauthorized calls is the following message in the response:
“Service requestor is not registered: access denied”.
Cross domain calls
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: https://github.com/SharePoint/PnP/tree/master/Samples/Core.Services.Authenticate
HostedAppHostNameOverride in web.config
The WebApiHelper.AddToCache method authenticates the user to SharePoint.
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:
If you do not do this, you will receive the following error message when starting your app:
“rd00155d516979” is not the intended audience “58471cbd-4d01-4567-92a1-c1adca8656b1/spapp.mydomain.com@946d293d-00c0-43c1-9c20-e0e0314fd8d3”
The TokenHelper.ReadAndValidateContextToken function needs this setting to validate the audience.
Finally
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).
References
https://sharepoint.stackexchange.com/questions/201378/pass-authentication-from-provider-hosted-add-in-to-external-web-service
https://github.com/SharePoint/PnP/tree/master/Samples/Core.Services.Authenticate
https://ypcode.wordpress.com/2017/12/29/sharepoint-add-in-custom-web-api-and-spfx
Geef een reactie