Signing API requests on Azure App Services using MSI Access Token - azure

I have a web application running on Azure App Services. The front-end (javascript + html + css) communicates with the backend (Flask). Both are runing on the same app service instance.
My app is protected by Active Directory Authentication (configured using Azure Portal).
User authentication to the app works perfectly. When a user navigates to the app, they are redirected to the login for our azure AD tenant. When they try to sign in, their permission to the app is controlled by their membership to an azure AD group. This bit works as expected
The challenge is that the front-end needs to send authenticated requests in order for them to actually reach the backend. They must be authenticated using a service principal token, not the user's token. And to this end, we are using the new, recommended approach; Managed Service Identity (MSI), not the service principal account workflow directly.
There are 2 stages to this:
1) Adding the authorization header
2) Ensuring that the MSI principal has access (i.e. belongs to the AD group)
1) The server generates an access token using the below code:
credentials = azure_active_directory.MSIAuthentication()
session = credentials.signed_session()
return session.headers.get("Authorization")
We then add the {"Authorization": "Bearer "} header where is the result of the above code.
This appears to work as expected - we are seeing long alphanumeric access tokens
2) The tricky bit was ensuring the MSI was added to the AD group. The GUIs at myapps.microsoft.com and mygroups.microsoft.com only allow the adding of users. Instead, I used the Azure CLI and ran the following:
a) Retrieve MSI principal ID
msiobjectid=$(az webapp identity show --resource-group <resource-group-name> --name <azure app services name> --query principalId)
b) Add principal to group
az ad group member add --group <group name> --member-id $msiobjectid
We are still getting 401 Unauthorized and we have exhausted all documentation :(
I should note that I only completed step 2 (adding the principal to the azure AD group via Azure CLI) about an hour ago. Perhaps there is a delay?
Edit: my scenario is closest to https://github.com/uglide/azure-content/blob/master/articles/app-service-api/app-service-api-dotnet-service-principal-auth.md except a) I'm using MSI, not a direct service principal and b) I have an extra layer of authorization, which is the ad groups, restricting access to the app to a few users rather than the whole tenant.

I believe you got this all backwards.
Your scenario is SPA + backend.
What you need to implement is OAuth 2.0 implicit grant flow as described here.
Source for diagram: dzimchuk.net
With the resulting access_token placed in the user-agent's session storage you can then call your backend. Use adal.js to make your life easier.
Since your Python backend IS a confidential client, you can now request an access token from the MSI endpoint for the desired audience (Azure resource), call it then filter the results so that it matches your access rights logic.
Note that at the time of writing only a subset of Azure resources are able to consume MSI-issued access tokens.

Related

Allow Azure B2C App Registration access to regular organization AD App Registration

I have the following scenario:
An organization has an internal application, X, which is registered under the 'main' tenant, allowing employees to utilize it.
App X has an API exposed for other applications (which are also registered under the main organization tenant) to used, and thus, this is all setup in AD.
A new B2C tenant has been created, where another public facing application, Y, will be registered.
How do I allow my App Registration for Y in my B2C tenant to use the exposed API of X?
Any feedback would be appreciated.
Edit 1:
I'm assuming I'd need to setup a Daemon auth flow, as the backend of Y will be authenticating with X as the app itself, and not as or on behalf of the user logged into Y.
Edit 2:
After some looking into this today, I'm considering creating an AD App Registration for Y in the main organization of X, allowing me to set up any connections that need to be made there, and I'd update the backend of Y to make a call as a Daemon to X, passing all the relevant information and client secret.
Seems a bit unusual, so will look for alternatives, but would also appreciate some feedback from someone who has more experience :)
Edit 3:
To clarify, I am looking to facilitate the communication between backend applications between two tenants, where one is a B2C tenant, and the other is an internal organization tenant.
This can be achieved using multi-tenancy. Both the applications need to register as multi-tenant application.
1.In Tenant A - Create an app registration as multi-tenant application in tenant A (eg: TenantA) and expose it as an API (api://app-id) and add the app roles in the application.
2.In Tenant B - Create an app registration as multi-tenant application in tenant B and note the client-id of the application.
3.The client id of application in Tenant B need to be added in known client application in the manifest of application registered in tenant A.
4.Provide consent to the application and permission in Tenant B to create the service principal using https://login.microsoftonline.com/common/adminconsent?client_id=clientIdOfTenantA&redirect_uri=redirectURIOfTenantA
5.In Tenant B, service principal of Tenant A has been created under Enterprise applications
6.Now tenant A is available in Tenant B. You can go ahead and make the API exposed in tenant A to the tenant B.
• Yes, you can surely allow the App registration considered Y in Azure AD B2C tenant to use the exposed API of another ‘App registration’ named X in an Azure AD tenant. For that purpose, you will have to configure the ‘Application Y’ registered in Azure AD B2C tenant as a ‘multitenant’ application and use it to start an authentication request to the authorization endpoint via a user flow. Thus, in here, the user flow defines and controls the user experience. After users complete the user flow, Azure AD B2C generates a token and then redirects users back to your application.
For this purpose, you will have to configure a user flow in your Azure AD B2C application.
Please refer to the below snapshots and steps defined for more details on this: -
a) You might be having a front end and a back end to your application registered for authentication purposes with your web app. The backend application might have the authentication with the application registration X in an Azure AD tenant while the frontend application might have the authentication with the application registration Y registered in the Azure AD B2C tenant.
Then, you will have to modify the front-end code for the web API and the back-end code for the web API as given in the below relevant link: -
https://learn.microsoft.com/en-us/azure/app-service/tutorial-auth-aad?pivots=platform-windows#call-back-end-api-from-front-end
For further configuring the authentication and authorization for the two apps, you can configure the front-end app to generate an access token that you can use to make authenticated calls to the back-end app. For this purpose, you will have to configure Azure AD as the identity provider with the app service configured for the front end as well as the back end as given in the link below: -
https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad
b) Once the above has been done, ensure that you are granting front end app access to the back end as below through the ‘Authentication’ section in the Azure AD app: -
Then configure the app service to return a usable access token for the front-end app to access the back-end app with the required permissions for configuring the App service authentication and authorization on behalf of the ‘App registration Y’ in the Azure AD B2C tenant for it to access the ‘App registration X’ in Azure AD tenant as below by adding the scope parameter to the authentication setting ‘identityProviders.azureActiveDirectory.login.loginParameters’. Replace and in the below commands: -
authSettings=$(az webapp auth show -g myAuthResourceGroup -n <front-end-app-name>)
authSettings=$(echo "$authSettings" | jq '.properties' | jq '.identityProviders.azureActiveDirectory.login += {"loginParameters":["scope=openid profile email offline_access api://<back-end-client-id>/user_impersonation"]}')
az webapp auth set --resource-group myAuthResourceGroup --name <front-end-app-name> --body "$authSettings"
The commands effectively add a ‘loginParameters’ property with additional custom scopes. Here's an explanation of the requested scopes: -
openid, profile, and email are requested by App Service by default already.
For information, see OpenID Connect Scopes: -
api://<back-end-client-id>/user_impersonation is an exposed API in your back-end app registration. It's the scope that gives you a JWT token that includes the back-end app as a token audience.
offline_access is included here for convenience (in case you want to refresh tokens)
Thus, thereby you can call the back-end API (Azure AD app registration) from the front-end API (Azure AD B2C app registration) by injecting a X-MS-TOKEN-AAD-ACCESS-TOKEN header to each authenticated request as shown below: -
https://learn.microsoft.com/en-us/azure/app-service/tutorial-auth-aad?pivots=platform-windows#call-api-securely-from-server-code
Thus, in this way, you can surely expose an API for an application registered in Azure AD B2C for it to access the application in Azure AD.

Azure API Management + Azure Function and managed identity

I'm trying to call an Azure function from an API Management instance by using Managed Identity.
I have set a System Managed Identity to my APIM instance.
I have granted the Contributor role to this identity on the Azure Function App.
I have also change the App Service Authentication to AD.
Now I'm trying to call the function from an API.
I have two issues:
First One: when I use the authentication-managed-identity policy to get a token, I got an error when I use the audience https://myfunctionapp.azurewebsites.net. AD tells me that this app is not registered in the tenant
Second: If I retrieve a token for https://management.azure.com, I got a token but I received a 401 Unauthorized error from the Azure Function.
Maybe I'm just trying to get a token on the wrong audience, but unfortunately the audience of functions is not listed in the document (for service bus for example, there is a common URI to use, also for KeyVault, ...).
I think that I probably missed something in the picture...
Thanks.
You need to use the authentication-managed-identity policy to authenticate with a backend service using the managed identity of the API Management service.
https://learn.microsoft.com/en-us/azure/api-management/api-management-authentication-policies#ManagedIdentity
EDIT
1-In your APIM application on Azure AD,
grab the Application ID assigned for enterprise application.
2-Then go to Platform features in your Azure Function App, and click on Authentication / Authorization.
3-Select Azure Active Directory as the authentication provider, and the management mode "express".
4-Back to authentication-managed-identity policy, set the Application ID from step 1 as the resource.
you need to add the url in apim required resource. If you're planning to use delegated flow. To check if the issue is with the url registration you can use the AppId instead. This will at least tell you if the token can be retrieved.

Credentials prompt for access to Azure management APIs

I've been using the Azure fluent management APIs (https://github.com/Azure/azure-libraries-for-net) with some success in .NET Core.
However, I want to prompt the user to enter some credentials for a Microsoft account. Those credentials would have access to one or more Azure tenants / subscriptions, so I'd like to be able to use the result to browse and manage resources there.
This is something very close to what I would believe Azure Data Studio does: you can enter some Azure creds, and your resources will appear in the app.
I'm trying to understand the best approach for this. There seem to be a billion sites out there when you talk about Azure AD app registrations, but I haven't found a fruitful specific search query yet. I know I can register an app, get a client ID and client secret. I know I can set it to be usable by organisational accounts in the current tenant, or all tenants.
I can add the "Azure Service Management (delegated permissions : user_impersonation)" permission to my API permissions section for the app, but what's next?
If I use Microsoft.Identity.Client (as in https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-netcore-daemon), I run into some questions:
AcquireTokenForClientAsync doesn't prompt the user - I guess because it's getting a token for the app to act with its own permissions?
AcquireTokenOnBehalfOfAsync wants a JWT.. great! I'll pass the one I got from AcquireTokenForClientAsync! Nope, AADSTS70002: Error validating credentials. AADSTS500137: The token issuer doesn't match the api version: A version 1 token cannot be used with the v2 endpoint.
I don't know what scope I want. https://management.azure.com/user_impersonation is apparently invalid.. https://management.azure.com/.default works, but is that right? It's a guess, combo of the former and a .default suffix I found for Graph API scopes online. Any docs on this?
I ultimately get a JWT and tenant ID back. I can't find a way to use a JWT with the Fluent management APIs.. and my account (for instance) is associated with 3 tenants or 5 different tenants / directories - so how do I choose?
That's just what I've tried, the appropriate route might be a different one. In summary: I want a .NET Core Console app to request user credentials, and then get access to the Azure resources they have access to, in order to perform some resource management.
AcquireTokenForClientAsync doesn't prompt the user - I guess because it's getting a token for the app to act with its own permissions?
You are using the OAuth 2.0 client credentials grant to access web-hosted resources by using the identity of an application. This type of grant commonly is used for server-to-server interactions that must run in the background, without immediate interaction with a user .
AADSTS70002: Error validating credentials. AADSTS500137: The token issuer doesn't match the api version: A version 1 token cannot be used with the v2 endpoint.
Azure AD provide two service : Azure AD V1.0 and Azure AD V2.0 . Please refer to Comparing the Azure AD v2.0 endpoint with the v1.0 endpoint . You can't use v1 token to acquire v2's token in a on-behalf-of flow .
AcquireTokenOnBehalfOfAsync wants a JWT.. great! I'll pass the one I got from AcquireTokenForClientAsync
AS pointed above , That function is used to acquire an access token for this application (usually a Web API) from the authority configured in the application, in order to access another downstream protected Web API on behalf of a user using the OAuth 2.0 On-Behalf-Of flow. So you can't use app token which acquire using Client Credential flow .
https://management.azure.com/.default works, but is that right? It's a guess, combo of the former and a .default suffix I found for Graph API scopes online. Any docs on this?
You are using the Azure Active Directory v2.0 and the OAuth 2.0 client credentials flow , when sending a POST request to the /token v2.0 endpoint ,the scope should be :
The value passed for the scope parameter in this request should be the resource identifier (Application ID URI) of the resource you want, affixed with the .default suffix. For the Microsoft Graph example, the value is https://graph.microsoft.com/.default. This value informs the v2.0 endpoint that of all the direct application permissions you have configured for your app, it should issue a token for the ones associated with the resource you want to use.
Please check the Get a tokensection in above document .
I ultimately get a JWT and tenant ID back. I can't find a way to use a JWT with the Fluent management APIs..
AFAIK , currently Azure AD V2.0 apps can use:
Its own API
Microsoft Outlook APIs
Microsoft Graph API
Azure AD V2.0 currently doesn't support Azure management APIs .
So you problem is you need to allows work and school accounts from Azure AD and personal Microsoft accounts (MSA) which works with Azure AD V2.0 , but you can't use Azure management APIs . You can use Azure management APIs in Azure AD V1.0 but it allows only work and school accounts to sign in to your application , unless you invite Microsoft accounts as guest user in Azure AD V1.0 ,but you need to configure to point to the tenant-specific endpoint :https://login.microsoftonline.com/{TenantId_or_Name}). during authentication if you want to login with MSA in v1.0 apps.
Update:
You can use Code flow and azure ad v1.0 endpoint , user will be redirect to AAD's login page and enter their credential. Here is code sample for .net Core .
With Azure AD V1.0 endpoint , requests are sent to an endpoint that multiplexes across all Azure AD tenants: https://login.microsoftonline.com/common . When Azure AD receives a request on the /common endpoint, it signs the user in and, as a consequence, discovers which tenant the user is from. See document here . But in this scenerio ,you can only use work and school accounts(AAD) account to login .
The code sample in your link is using Azure Service Principal for Authentication , no interactive user login . You can use OpenID Connect Owin Middleware for authentication in .net Core applications as shown here .

Azure AD - Multi-Tenant with Daemon Service and Authorization Code Grant flow, can a target tenant generate a client_secret?

I am working with Azure AD through OAuth 2.0 protocol and also creating a Service/ Dameon application to handle the authentication process for Microsoft Graph SDK. For the service/daemon, I make a HttpWebRequest and pass along the client_id and client_secret to generate an access_token where I then can supply to the Microsoft Graph SDK.
I also have successfully created a corresponding service principal to the target tenant, in which an admin has granted permissions to the application using the authorization code grant flow. The application then shows in Overview -> Quick tasks -> Find an enterprise app, within the (portal.azure.com).
My question is there an approach where I can leverage the service/daemon approach while also allowing an admin from the target tenant to authorize the application, that would allow the target tenant to create a client_secret to pass which would be unique to that tenant?
Short answer is no. When an admin consents your multi-tenant app:
A Service Principal is created for it in their tenant
Permissions requested by the app are granted in that tenant
This means your app can now authenticate with its client credentials (id + secret) against their tenant as well. So the same keys work in all approved tenants.
What that means is your app is free to get an access token for any of them at any given time, no matter who is signed in. So it puts some responsibility to your app to keep data separated.
If you get an access token from https://login.microsoftonline.com/company.com/oauth2/token, the resulting token will contain that tenant's identifier. And APIs like the Microsoft Graph API will only give you data for that tenant with that token. So your app must make sure to only use a token that has a tenant id equal to the user's tenant id claim.
I would say that juunas' answer is 99% correct. The short answer is basically no, and the considerations he mentions are also solid.
But I believe that this would be technically possible under certain considerations. When the admin consents to your daemon service, a service principal is created in your customer's tenant. Service principals do allow addition of credentials that can be used as client secrets on a per-tenant basis. The thing is, there's not really a way to add a credential to a service principal programmatically from your app. You would have to get an administrator to run some script to add the new credential to their tenant's service principal.
Even if you went through all this, you would need to make sure that your service is also isolated on a customer/tenant basis. Security-wise, it's sort of pointless to create per-tenant client secrets if your singular daemon has access to all of the secrets.

Azure Management API access from a web app

Is it possible to gain access to the Azure Management APIs through the client ID and secret for a web app?
I have a web app through which i want to be able to manage Azure. I want to do this using the credentials of the application itself so that the current user does not have to be an azure administrator.
I have given the web app the necessary role on my subscriptions and obtained the access token through the client credentials grant flow in AD but i still get an unauthorized.
This is probably because the azure management API has no permission set other than delegated - the access works fine if i use the authorization code grant flow for the logged in user, but thats not what i want.
So to reiterate, if, given a web app that has RBAC to a subscription and is able to obtain an access token from AD, is there any way, without an interactive user, that the web app is able to use the management API??
Yes, you can obtain a token from AAD for a service principal and use that to manage resources as long as that service principal has all the access you need.
Make sure the token you get has a resource/audience of "https://management.azure.com" and is for the tenantId that the subscription is associated with.
You can also see this article from Brady Gaster that explains how to use Azure AD applications to manage Azure Services from an external app : http://www.bradygaster.com/post/using-windows-azure-active-directory-to-authenticate-the-management-libraries
EDIT : Azure AD supports Service to Service calls using OAuth 2.0 client credentials: https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx
Hope this helps,
Julien

Resources