I find federated/Oauth authentication highly confusing. I have a web application that is using an azure AD to authenticate users. I also have a azure function that authenticates against the same Azure AD. I can reach both independently and authenticate and access them.
But now I want the web application to make requests to the azure function as the user logged in to the web application. This is so that the API can return user specific information.
What Oauth ?flow is required and how do I implement it.
Though the question for now is about a single tenant app registrations. I may need to change that to multi-tenant in the future. Optionally consider this for the answer.
The Azure function API is running .net6 ISOLATED. So much of the easy config wizards don't work for that in the azure admin portal.
The situation you're describing involves two key phases of authentication:
The most suitable OAuth 2.0 mechanism for this scenario is the On-Behalf-Of (OBO). This permits your web application to obtain an access token for the Azure function (the 'downstream' API), representing the user.
Here's a brief summary of how this process operates:
some modifications are needed in your Azure AD app registrations for both your web application and Azure function. Additionally, adjustments are required in the token retrieval logic in your web application.
In the Azure portal, locate the app registration for your web application.
This adjustment enables your web application to request tokens for the Azure function.
In the logic for obtaining tokens within your web application, when the Azure function needs to be called:
Here's an example using the 'Microsoft.Identity.Web' library:
string[] scopes = new string[] { "<AppIdUri>/access_as_user" };
// Assuming 'userAccessToken' is the token of the authenticated user
AuthenticationResult result = await _tokenAcquisition
.AcquireTokenOnBehalfOf(scopes, new UserAssertion(userAccessToken))
.ExecuteAsync();
// Use 'result.AccessToken' to call the Azure function
Usually, the 'Microsoft.Identity.Web' library would be used to validate incoming tokens. However, Azure Functions' isolated model doesn't directly support it. Instead, manual validation of the tokens is required.
To do this, use the 'System.IdentityModel.Tokens.Jwt' library. This requires you to:
Regarding multi-tenancy, if you want to make your application accessible to users from various tenants, you should:
You also have to consider if your web application is an application which is written as a Single Page Web App or Is using Backend server side code for authenticating user or making a the call to azure function. Above Approach is suitable if your web application is using server side code to all the azure function. If you are having the web application which is a Single Page Application, you will have to adjust your approach as you may not want to expose the client id and secret etc via browser java script code.
For a SPA, you would typically use the OAuth 2.0 Implicit Grant or Authorization Code with PKCE (Proof Key for Code Exchange) flow to authenticate the user.
With this approach, the SPA can obtain an access token for the user, which can then be used to call the Azure function.
However, SPA cannot use the On-Behalf-Of (OBO) flow, as this requires the application to have access to the client secret or a certificate, which is not suitable for a public client like a SPA.
In this scenario you should:
Implement the Azure function to accept tokens from both the web application (in case of a server-side app) and the user (in case of a SPA). This means validating the scp
or roles
claim in the token to ensure the user has the necessary permissions.
Use the user's access token to call the Azure function directly from the SPA. The Azure function should validate the token and use the sub
or oid
claim to determine the user's identity and return user-specific information.
This way, the Azure function can provide user-specific information in both scenarios: when called directly from the SPA and when called from the web application on behalf of the user.