Monday 19 February 2018

SPFx: Calling AAD secured Azure Function on behalf of a user with AadHttpClient

This post is part of a series where we explore consuming Azure AD secured Azure Functions from SharePoint Framework components. Articles in the series:

1) SharePoint Framework: Calling AAD secured Azure Function on behalf of a user (this post)
2) Calling Microsoft Graph API from an AAD secured Azure Function on behalf of a user
3) SharePoint Framework: Calling back to SharePoint from an AAD secured Azure Function on behalf of a user

Recently, after long last, the support for easily calling Azure AD secured custom APIs was released in the latest version of the SharePoint Framework (v1.6). Here are the details if you want to learn more: https://docs.microsoft.com/en-us/sharepoint/dev/spfx/use-aadhttpclient

In this post, let's have a look at how to secure our custom API (Azure Function) with Azure AD and then call the custom API from within a SharePoint Framework web part on behalf of the currently logged in user. We will be able to get the current user identity/claims in the custom API to know which user made the call to the Azure Function.



So let's quickly jump into how we are going to achieve this:

1) Create an Azure AD app registration


Go to Azure AD > App registrations > New application registration and create a new app registration. I am calling my app "User Details Custom API for SPFx"



By default the app registration will have the Sign in and read user profile scope on the Windows Azure Active Directory API. For the purpose of this post, those permissions are enough for us. We don't need to modify anything here right now.

Click on Properties and copy the App ID URI of the app registration. We will need this later in the SharePoint Framework web part.

As a subscription admin, Grant permissions to the App registration for all users so that each user does not have to do this individually:


2) Create an Azure Function App and configure it


Go to App Services > Add > Function App and create a new Azure Function App which we will use to host our Azure Function


Once the Function App is created, we need to secure it with our Azure AD app registration.

Go to Function App > Platform Features > Authentication / Authorization


In the Authentication / Authorization pane, for "Action to take when request is not authenticated" select "Log in with Azure Active Directory".

For Authentication Provider, click on Azure AD > Advanced and for Client ID, paste the Client ID (Application ID) of our Azure AD app registration we created earlier.

In Allowed Token Audiences, add the App ID URI of the app registration we copied in Step 1


Click Ok and Save the Configuration.

Go to Function App > Platform Features > CORS and add the SharePoint Online domain from which your SPFx webpart will make a call to the Azure Function.


Click on Save.

3) Create the Azure Function


Now let's actually create the Azure Function which we will deploy to the Function App as our custom API.

Using Visual Studio 2017, I have created a precompiled .NET Framework Azure Function project. As we are going to use this function as an API, I have selected an Http Triggered function:

Notice that the Authorization level for the function itself is set to Anonymous. This is because we are using Azure AD at the Function App level to secure it.

All this function does is returns the claims of the current authenticated user in JSON format.

Publish the function to the Azure Function App we created earlier.

4) Create the SharePoint Framework web part


Before going ahead with this step, make sure you have the latest version of the SharePoint Framework yeoman generator (1.6 at the time of this writing) This page should contain all the current versions of the generator, npm and node supported by the SharePoint Framework: https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-development-environment

Once you have all the latest packages, create the SPFx web part with yo @microsoft/sharepoint


To keep it simple, I am making it a tenant scoped solution with no JavaScript framework.

5) Request permissions for the SPFx web part


Once the SPFx web part solution is created, navigate to the config/package-solution.json file and add the webApiPermissionRequests property:

The resource will be the name of the Azure AD app registration we used to secure our Azure Function and the scope will be user_impersonation as we want to make a call on behalf of the current user.

As of SPFx 1.6 release, we also have to add the User.Read permission for "Windows Azure Active Directory" as reported in this issue: https://github.com/SharePoint/sp-dev-docs/issues/2473

6) Using AadHttpClient to call the custom Azure Function


Now in your SPFx webpart, include the following imports:

and the code to use the AadHttpClient to make a request to your custom Azure Function:

To display the table properly, add the following to the .scss file of your web part:


7) Installing and running the SPFx package 


Since we are going to run the SPFx solution in debug mode, we will run the commands without the --ship or --production switch. This will enable us to debug the solution locally.

Run the following commands to build and package your solution:

gulp build

gulp bundle

gulp package-solution

Start local debugging with

gulp serve --nobrowser

Now, upload the .sppkg file from the sharepoint/solution folder to the App Catalog:


Select the checkbox and Click on Deploy.

8) Granting permissions using the SPO Admin API management page


After deploying the package to the app catalog, as a SharePoint Administrator, navigate to the new SharePoint Online Admin centre and go to the API management section. You will notice the permissions we requested from the solution package can be approved from here. 

Approve the permissions before moving to the next step



9) Add web part to page

 

Now go to any modern page and add your web part to it. Since we have deployed a tenant scoped solution, no need to install it individually on each site.


The current user identity and claims coming through will be what we have sent from the Azure Function!

Hope this helps :) As always, the code from this post is available on GitHub: https://github.com/vman/spfx-azure-function-custom-api

13 comments:

Rajendra Shekhawat said...

Vardhaman, super. Thanks for sharing.

russell said...

+1!

Unknown said...

Did you ever try this with an ASP.NET Core 2.0 Web Api solution and not with an azure function? Because that's what I'm trying....and I can't figure out how I have to configure the authentication etc. for it to work with the AADHttpClienet.

Vardhaman Deshpande said...

Hi, I haven't tried it myself but looks like this is something similar? https://twitter.com/PaoloPia/status/965942866655690753

semekhlouf said...

Hi, I tried to call a secure azure function from my developer Tenant , but always has this error:
A service principal with the name "User Details Custom API for SPFx" could not be found. Parameter name: resourceName

Unknown said...

Is the AADHttpclient solution working for IE 11?

Unknown said...

Great article. Might be worth extending this series to discuss the additional security you can apply to the service principal via the Azure AD Enterprise Applications blade.

I've used this to tighten up who can call the function by setting what Azure AD groups a user has to be a member of.

Unknown said...

Hi Verdhaman,

Great post and detailed very easy to follow steps.

I have followed the steps and for some reason get 401 unauthorized error. Am I missing anything or any direction would be helpful.

Thanks,
Vishu

Nanddeep Nachan said...

Nice article.
In my situation, I have separate Azure and Office 365 subscription.
When I try to approve the App permissions from SharePoint API Management, I get below error:
A service principal with the name ---- could not be found.
Parameter name: resourceName"

Please suggest. Thank you.

MyWorld said...

It is a wonderful post.

I get an issue in the Promise of the first .then chain.
Uncaught (in promise) TypeError: Failed to execute 'json' on 'Response': body stream is locked
at e.json (sp-pages-assembly_en-us_9e9449a10a42b2bfe9427253a0d86c6c.js:1134)

Its seems like the JSON object in the second .then chain is undefined. However in the console log in the first .then chain I get a Promise and the PromiseValue array has an array of the data.

Thank you in advance.

Unknown said...

Hi, I am getting this error - "Property 'aadHttpClientFactory' does not exist on type 'WebPartContext'."

ashish said...

I am using exact same approach but facing issue in Edge browser saying..'TypeError: Invalid argument at Anonymous function' when I try to get method using AadHttpClient. Did you also encountered similar issue. If yes, how did you resolve this? I am using v1.8.2.

Thanks in advance.

Big King said...

Is it possible to display the users AAD security group with this?