How to assign Service Principal to workspace programmatically in C# - c#

I have created a service principal in AAD and am able to assign to the workspace manually in https://app.powerbi.com/home
I want to assign the service principal to all the workspaces programmatically.
Is there any way to do it?
Please help
Thanks

Yes, you can use the Power BI REST API and call Update Group User to add the service principal to the workspace:
Request:
PUT https://api.powerbi.com/v1.0/myorg/groups/f089354e-8366-4e18-aea3-4cb4a3a50b48/users
Request body:
{
"identifier": "1f69e798-5852-4fdd-ab01-33bb14b6e934",
"groupUserAccessRight": "Admin",
"principalType": "App"
}
To use the API, you must authenticate yourself, for example with ADAL or MSAL. Here is an example how to get an access token with MSAL:
private static async Task<string> GetToken()
{
// TODO: Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory -Version 2.21.301221612
// and add using Microsoft.IdentityModel.Clients.ActiveDirectory
//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/";
//Get access token:
// To call a Power BI REST operation, create an instance of AuthenticationContext and call AcquireToken
// AuthenticationContext is part of the Active Directory Authentication Library NuGet package
// To install the Active Directory Authentication Library NuGet package in Visual Studio,
// run "Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory" from the nuget Package Manager Console.
// 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);
Console.ReadLine();
return token;
}
This token must be added to the request headers, when you call the API:
//Add token to the request header
request.Headers.Add("Authorization", String.Format("Bearer {0}", token));

Related

Call Azure AD protected Azure Function from console app/PowerShell w/delegated permission

What I want to do is have the user log in to the AAD prompt on their Windows Desktop machines, so I get a Bearer token that will work with my Azure Function.
I've followed the tutorial from this article on adatum but it only covers the application permission (not delegated permissions)
I already have an Azure Function that is set up for Azure AD
authentication.
I already have a client app that I registered (under
App Registrations).
I've configured it to use delegated permissions
for the Azure Function.
Here's my client code:
var clientId = "client id for my console app";//console app
var clientUrl = new Uri("https://login.microsoftonline.com/common/oauth2/nativeclient");
var tenant = "tenantid here";
string authority = "https://login.windows.net/" + tenant;
string resource = "https://myaadProtectedFunc.azurewebsites.net";
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
var pp = new PlatformParameters(PromptBehavior.Auto);
var token = authenticationContext.AcquireTokenAsync(resource, clientId, clientUrl,
pp, UserIdentifier.AnyUser).Result;
Console.WriteLine("Got the token: {0}", token.AccessToken);
I get an error saying "[my client app] needs permission to access resources in your organization that only an admin can grant. Please ask an admin to grant permission to this app before you can use it."
Is there some other way to get a Bearer token that will work with my Azure Function?
I did a test and your code does work if you set up the azure side correctly. most likely in the azure function you do not have it set up correctly.
in the azure function did you set up the service principal?
eg. function app -> platform features -> authentication / authorization -> App Service Authentication to ON -> select azure active directory -> express -> create. -> press ok. -> save.
then in your app registration, you will now see 2. the app reg for your client, and app reg for your function app. in the app reg for your client go to api permissions and add the app registration for your function app with the user impersonation selected.
finally make sure your enterprise application has the user/groups you want to be allowed to access for each of the enterprise apps. (client and function app registration)
Hope that helps.
Ok it turns out that I don't need to make a separate client application at all.
I can just use the Client ID of the Azure Function.
The thing is that you will have to go to 'Advance' instead of 'Express' because the library Microsoft.Identity.Client uses v2.0 tokens.
This is to configure your Azure Function - keep all fields the same, but add a /v2.0 to the Issuer URL:
This is the code to get the delegated bearer token for the Azure Function, which uses the v2.0 token - I don't know how to change it to use the v1 token:
string[] scopes = new string[] { "profile", "email", "openid" };
string ClientId = [clientId of Azure Function];
string Tenant = [tenantId];
string Instance = "https://login.microsoftonline.com/";
var _clientApp = PublicClientApplicationBuilder.Create(ClientId)
.WithAuthority($"{Instance}{Tenant}")
.WithDefaultRedirectUri()
.Build();
var accounts = _clientApp.GetAccountsAsync().Result;
var authResult = _clientApp.AcquireTokenInteractive(scopes)
.WithAccount(accounts.FirstOrDefault())
.WithPrompt(Prompt.SelectAccount)
.ExecuteAsync().Result;
var bearerTokenForAzureFunction = authResult.IdToken;
Now I don't understand the need to register a client application at all if I can get the delegated bearer token this way...

How to configure ADAL authentication prompt with the client_secret parameter

When I configure an Authentication prompt to connect to Azure AD:
AuthenticationContext authContext = new AuthenticationContext(authority);
var result = authContext.AcquireTokenAsync(resource (using the clientID),
clientId,
redirectUri,
new PlatformParameters(PromptBehavior.SelectAccount))
.Result;
It fails with the error:
"AADSTS7000218: The request body must contain the following parameter:
'client_assertion' or 'client_secret'"
Should a client_secret need to be configured when we are connecting by user, not client, credentials and if so how does it need to be configured?
No, client_secret needn't to be configured when we are connecting by user.
Just add a desktop+devices platform with a redirect url provided or use the default one. I am using the default one.

Web API on-behalf-of adal id_token error

I have created a Web API in Azure.
This Web API makes some calls in SharePoint Online. Some of the api calls are on-behalf-of.
This Web API works fine until 01.05.2018 - and it works fine on old app services, which were created before 01.05.2018.
A microsoft staff member said:
As part of our security hardening efforts we do not allow id_token
redemption for any application created after 2018-05-01 00:00:00.
During the log in process of adal, I got the id_token. The id_token has got the same value as the access_token:
When I call the web api, I will send this token as bearer token.
The Web API takes this token (string accessToken) and starts the method 'AcquireTokenAsync':
var clientID = ConfigurationManager.AppSettings["ClientID"];
var clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
var tenant = ConfigurationManager.AppSettings["Tenant"];
var appCred = new ClientCredential(clientID, clientSecret);
var authContext = new AuthenticationContext(
"https://login.microsoftonline.com/" + tenant);
var resource = new Uri(sharePointUrl).GetLeftPart(UriPartial.Authority);
var authResult = await authContext.AcquireTokenAsync(resource, appCred,
new UserAssertion(accessToken));
return authResult.AccessToken;
But in the line which calls 'AcquireTokenAsync' I have got the error message:
AADSTS240002: Input id_token cannot be used as 'urn:ietf:params:oauth:grant-type:jwt-bearer' grant
But where is the problem?
The problem is that you use the same application identity in the front-end and back-end, and MS does not allow you to use the Id token (which you use as an access token here because of the former) to get another access token.
A possible solution:
Register another application (the front-end JS app should be a Native app)
It should acquire an access token for your back-end API using either the API's client id or app Id URI as the resource
Then the API can exchange the access token for another access token
If this is a multi-tenant app, the migration is probably not going to be easy.
If it's single-tenant, then all should be possible.
Your front-end app should of course require permission to call your back-end API in Azure AD, and that permission should be granted.
Another solution would be to acquire the other access token in the front-end using ADAL.JS instead of using on-behalf-of in the back-end and attaching that to all API requests in addition to the Id token.

Azure Active Directory: web api call another web api

I have two different Web API applications (web-api-app1 and web-api-app2) integrated with Azure Active Directory authentication.
I am able to call both API applications individually using some console application like this:
AuthenticationContext ac = new AuthenticationContext("https://login.windows.net/[AD Tenent]");
var clientCredentials = new ClientCredential("app id", "app key");
AuthenticationResult ar = ac.AcquireTokenAsync("https://web-api-app1", clientCredentials).Result;
string result = string.Empty;
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ar.AccessToken);
HttpResponseMessage response = httpClient.GetAsync("https://web-api-app1/odata").Result;
if (response.IsSuccessStatusCode)
{
result = response.Content.ReadAsStringAsync().Result;
}
Console.WriteLine(result);
Now my requirement is that I need to call web-api-app2 from web-api-app1 (web API call to web API call). How do I do that? Thanks!
You need to implement on-behalf-of (docs for v1 endpoints) flow.
app1 should request an access token to app2 using own access token acquired on user authentication.
Manifest of app1 should include permission to access app2.
If apps are in different tenants, the app2 should be consented by app1 tenant admin first.
The official .net example app

Authenticate to Azure API App using ADAL

I have an Azure API App marked as "Public (authenticated)" and set up an Azure Active Directory identity in the associated gateway as detailed in Protect an API App.
I then created a native application in the same Azure Active Directory Tenant and added permission to access the Gateway in the delegated permissions.
Using ADAL and the following code, I'm able to successfully authenticate and get an access token, but I can't figure out how to use it to access my API app.
string Tenant = "[xxx].onmicrosoft.com";
string Authority = "https://login.microsoftonline.com/" + Tenant;
string GatewayLoginUrl = "https://[gateway].azurewebsites.net/login/aad";
string ClientId = "[native client id]";
Uri RedirectUri = new Uri("[native client redirect url]");
async Task<string> GetTokenAsync()
{
AuthenticationContext context = new AuthenticationContext(Authority);
PlatformParameters platformParams = new PlatformParameters(PromptBehavior.Auto, null);
AuthenticationResult result = await context.AcquireTokenAsync(GatewayLoginUrl, ClientId, RedirectUri, platformParams);
return result.AccessToken;
}
I've tested the API app manually entering an x-zumo-auth header I get in Chrome and it works then, but not with a token I get using ADAL. I've also tried the browser forms described in their sample code which works but doesn't give me a refresh token.
How do I need to set up my authentication code so I can use a TokenCache and ADAL with my API app?
Generally you pass the access token in the Authorization header when when calling a web api:
Authorization: Bearer ThisIsTheAccessTokenYouRecievedFromADAL
You may want to use AppServiceClient to authenticate the user and invoke a protected API App endpoint. Install Microsoft.Azure.AppService SDK (-pre) Nuget package to your client project.
You can find more details in the AzureCards samples on GitHub - https://github.com/Azure-Samples/API-Apps-DotNet-AzureCards-Sample

Categories