Authenticate Azure Function by Token with Resource - azure

I have an Azure Function with Authorization/authnetication enabled via AD log in.
I am trying to authenticate by generating a token using client_credentials:
Refer to the following code below:
var tokenendpoint = "https://login.microsoftonline.com/172f05a2-f956-4856-b4c8-9580a54dbd56/oauth2/token";
string clientID = "eaeff78a-26ef-4bcb-b977-638316ff15b7";
string clientSecret = "HvVlipQkpuezmD4YiUcWVpZ5Cn1cP3vxiW61pSpDo8k=";
string resource = "eaeff78a-26ef-4bcb-b977-638316ff15b7"; //ClientID
string grantType = "client_credentials";
using (var reqToken = new WebClient())
{
NameValueCollection parameters = new NameValueCollection();
parameters.Add("client_id", clientID);
parameters.Add("client_secret", clientSecret);
parameters.Add("resource", resource);
parameters.Add("grant_type", grantType);
var responseTokenBytes = reqToken.UploadValues(tokenendpoint, "POST", parameters);
string responseTokenContent = Encoding.UTF8.GetString(responseTokenBytes).Replace(#"\", "");
azureFunctionTokenResponse = responseTokenContent.Deserialize<AzureFunctionTokenResponseBase>();
AzureFunctionToken = azureFunctionTokenResponse.access_token;
}
All works fine if I set the resource as the ClientID of my function.
However, in many examples online the Resource is set to the Azure Function Uri.
If I set my Resource to https://www.xxxxxx.azurewebsites.com then I get a 401 error.
Why is this?
I spent a whole day in getting this to finally work but nowhere in the docs does it say to enter the ClientID as the Resource??

If you use the same AAD app to enable Authorization/Authentication for your Azure Function and your client code to acquire the access_token for accessing your Azure Function, you could specify the resource to the Application ID (ClientID) or the App ID URI of your AAD app.
In general, we would use the ClientID as the resource, and App Service Authorization/Authentication would compare the Client ID you configured under Authentication / Authorization > Azure Active Directory Settings with the aud property of the incoming JWT bearer token, you could leverage https://jwt.io/ to decode your token.
However, in many examples online the Resource is set to the Azure Function Uri.
If I set my Resource to https://www.xxxxxx.azurewebsites.com then I get a 401 error.
I assume that those samples may use the App ID URI, you could set the App ID URI to https://www.xxxxxx.azurewebsites.com for your AAD app (Settings > Properties > App ID URI), then you could use App ID URI for the resource parameter.
Note: For this approach, you may need adjust the Azure Active Directory Settings for your Azure Function, you may keep the Client ID to the Application ID of your AAD app and add App ID URI to ALLOWED TOKEN AUDIENCES list or you could just replace it with your App ID URI.
Additionally, you could ADAL library for acquiring the token. Also, if you create each AAD app for your Azure Function and your client app, you could follow this issue.

Related

Azure AD, Multi-tenant, App Roles Assignment for users from another tenant

I'm working on web application that contains client side (SPA, angular 9) and backend (WebAPI, ASP.NET Core 3.0). Decided to use Application Roles feature to authorize users in our application. And i have requirement to be able to manage Application role assignments for users from our application UI via MSFT Graph API.
I registered MyAuthApp application in Azure AD TenantA. And created several App Roles there.
Authentication works fine. Client side gets token and attaches it to http requests to backend. Authorization also works fine i can extract app roles from the token and validate them.
Problem with adding Application role assignments for users from other AzureAD tenant -- TenantB. Seems that problem in GraphServiceClient configuration due to GraphApiAuth registered in TenantA.
Question: is this possible to add application role assignment for user from TenantB using GraphServiceClient authorized by Client Credentials in TenantA?
Right now when i do add role assignment i'm getting exception like resource with some Guid not found. This resource is a user (from TenantB).
This is a piece of code that adds user app role assignment. I see possible problem in GetGraphServiceClient function. It uses as authority URL with TenantA Id.
public async Task<AppRoleAssignment> AssignAppRoleToUser(Guid userId, Guid appRoleId)
{
var graphClient = await this.graphClientProvider.GetGraphServiceClient();
return await graphClient.Users[userId.ToString()].AppRoleAssignments.Request().AddAsync(
new AppRoleAssignment()
{
PrincipalId = userId,
AppRoleId = appRoleId,
ResourceId = this.graphAppSettingsProvider.GetAppRoleResourceIdAsGuid()
});
}
df0b3e71-fd2d-41a4-bfa9-0310b31395ae is Id of user from tenantB.
UPDATE:After further investigation i was able to assign App role for user from TenantB. But i had to change settings in the code that returns GraphServiceClient and provide TenantB Id and Application Service Principal Id from TenantB (instead of values from TenantA). But that's a problem. We would like to be able to assign application roles for users from any tenant and it will be not doable if we will have to provide TenantId and Service Principal Id for each tenant separately.
Is it possible to do this some how with some common settings?
This is how i get GraphServiceClient:
public async Task<GraphServiceClient> GetGraphServiceClient()
{
var clientId = this.graphAppSettingsProvider.GetClientId();
var clientSecret = this.graphAppSettingsProvider.GetClientSecret();
var tenantId = this.graphAppSettingsProvider.GetTenant();
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithTenantId(tenantId)
.Build();
string[] scopes = {"https://graph.microsoft.com/.default"};
return new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider((requestMessage) =>
{
var ar = app.AcquireTokenForClient(scopes).ExecuteAsync();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", ar.Result.AccessToken);
return Task.FromResult(0);
}));
}
UPDATE 2
Changed a little requirements and now we just need to manage App Roles list for users from current user tenant. So, we changed permissions type from Application to Delegated to be behalf of authenticated user.
As i said earlier we have Angular app in pair with ASP.NET Core WebAPI backend. Angular app gets access token and sends it to backend in Authorizaiton header. When i attach with access token to GraphServiceClient request (header) i'm getting error "Access token validation failure. Invalid audience."
Question: is this correct flow to use access token from client for Graph API requests or should i get new access token for Graph API at backend using access token from client?
Any help/ideas appreciated. Thanks in advance!
First, you need to set up the MyAuthApp application as a multi-tenant application.
Next, run admin consent url in the browser, and then you need to log in with another tenant's administrator account and consent. The multi-tenant application will then be added to the target tenant as an enterprise application. https://login.microsoftonline.com/common/adminconsent?client_id={client-id}.
At the same time, the app role you created in tenant A will also be synchronized to the target tenant (for example, tenant B). Next, you only need to grant the app role of MyAuthApp to the users of tenant B through the Azure portal of tenant B or use ms graph api.

Get bearer token with MSAL.NET to access App Service with EasyAuth

I have an Azure App Service which is authenticated using Azure AD EasyAuth.
I am trying to send a request from another App Service using C# and MSAL.NET (Microsoft.Identity.Client).
The authentication code looks like this
var app = ConfidentialClientApplicationBuilder
.Create(config.ClientId) // The Client ID in the App Registration connected to the App Service
.WithClientSecret(config.ClientSecret)
.WithAuthority(new Uri(config.Authority)) // https://login.microsoftonline.com/tenant.onmicrosoft.com/v2.0
.WithTenantId(config.TenantId) // Tenant Id Guid
.Build();
// Used Scopes: ["https://graph.microsoft.com/.default"]
var credentials = await app.AcquireTokenForClient(config.Scopes)
.ExecuteAsync(cancellationToken);
I get a bearer token successfully, but when I try to call the App Service with token injected to the headers I get a 401 and You do not have permission to view this directory or page. :(
Update 1:
I tried #Jim Xu answer and it's still giving me 401. It returns a www-authenticate header with the following value
The resource id is the same ClientId in the App Reg
Update 2 - Solution
So to summarize the fix:
The requested scopes when calling AcquireTokenForClient should include {Application ID Uri}/.default
In EasyAuth configuration, the Allowed Token Audiences needs to be set to the Application ID Uri as well
If you want to call the Azure API app which enables easy auth, please refer to the following steps
Get the Application ID URI of the AD application you use to enable easy auth
a. In the Azure portal menu, select Azure Active Directory or search for and select Azure Active Directory from any page.
b. Select App registrations > Owned applications > View all applications in this directory. Select your web app name, and then select Overview.
code
var app = ConfidentialClientApplicationBuilder
.Create(config.ClientId) // The Client ID in the App Registration connected to the App Service
.WithClientSecret(config.ClientSecret)
.WithAuthority(new Uri(config.Authority)) // https://login.microsoftonline.com/tenant.onmicrosoft.com/v2.0
.WithTenantId(config.TenantId) // Tenant Id Guid
.Build();
// Used Scopes: ["{Application ID URI}/.default"]
var credentials = await app.AcquireTokenForClient("{Application ID URI}/.default")
.ExecuteAsync(cancellationToken);
For more details, please refer to here.

Get a Power BI access token after logged into Azure AD

I have code that logs me into Azure AD, but I cant' figure out how to get the access token to call the REST API's or PowerBI
There is the sample code to get access token for Power BI REST API.
//The client id that Azure AD created when you registered your client app.
string clientID = "{Client_ID}";
//RedirectUri you used when you register your app.
//For a client app, a redirect uri gives Azure AD more details on the application that it will authenticate.
// You can use this redirect uri for your client app
string redirectUri = "https://login.live.com/oauth20_desktop.srf";
//Resource Uri for Power BI API
string resourceUri = "https://analysis.windows.net/powerbi/api";
//OAuth2 authority Uri
string authorityUri = "https://login.microsoftonline.com/common/";
// AcquireToken will acquire an Azure access token
// Call AcquireToken to get an Azure token from Azure Active Directory token issuance endpoint
AuthenticationContext authContext = new AuthenticationContext(authorityUri);
var token = authContext.AcquireTokenAsync(resourceUri, clientID, new Uri(redirectUri)).Result.AccessToken;
Console.WriteLine(token);
For more details, see here.

Retrieve Azure KeyVault secret using client secret

I'm experimenting with various Azure features and currently want to retrieve a secret from KeyVault.
Straight to the case:
I'm using this nuget package to interact with my azure resources.
I've developed a simple .NET Core console app and run it locally.
I have a KeyVault resource with one secret defined which is active and not expired.
I've registered an App in AAD so my locally shipped .NET Core console app has an identity within AAD.
Than I've created a "client secret" within this registered app in AAD to use it to authenticate myself as an app.
After that I've added access policy in my KeyVault resource to allow GET operation for secrets for this registered app:
Then I've developed a small piece of code which should retrieve the desired secret:
public class AzureAuthentication
{
public async Task<string> GetAdminPasswordFromKeyVault()
{
const string clientId = "--my-client-id--";
const string tenantId = "--my-tenant-id--";
const string clientSecret = "--my-client-secret--";
var credentials = new ClientSecretCredential(tenantId, clientId, clientSecret);
var client = new SecretClient(new Uri("https://mykeyvaultresource.vault.azure.net"), credentials);
var secret = await client.GetSecretAsync("admincreds");
return secret.Value.Value;
}
}
However when I'm trying to do this I'm getting an AccessDenied error:
Am I missing something painfully obvious here? Or there is some latency (>30 min for this moment) for which changes from Access policies screen in KeyVault resource are applied?
I test your code and Get permission, it works fine.
From your screenshot, it looks you didn't add the correct service principal related to the AD App to the Access policies.
If you add the service principal related to the AD App, it will appear as APPLICATION, not COMPOUND IDENTITY.
So when you add it, you could search for the client Id(i.e. application Id) or the name of your App Registration directly, make sure you add the correct one.
Make sure your AD App(service principal) has the correct permission in your keyvault -> Access policies

What is ResourceId Here?

Currently, am trying to get the bearer token from the AAD(which is a Native app). I have the current block of code
private AuthenticationContext authContext = null;
authContext.AcquireTokenAsync(todoListResourceId, clientId, redirectUri, new PlatformParameters(PromptBehavior.Always))
So in the current Code block what is todoListResourceId ?
That's the resource identifier for the API you want an access token for.
In this case your client app says to Azure AD "Give me a token for the Todo List Service".
In case of MS APIs, you typically use the URI e.g.:
MS Graph API: https://graph.microsoft.com
Azure AD Graph API: https://graph.windows.net
In case of your own APIs, you can use either the API client id or App ID URI (found in the Properties for the app registration).
Though it does require the API to be configured to accept both as a valid audience.
If it only accepts one, then you have to use that one.

Resources