I am trying to access an Azure Application Insights resource via Redash, using the (preview) ADXProxy feature.
I've created an App Registration in Azure, and I've got some proof-of-concept python code which can successfully access my Application Insights resource and execute a Kusto query (traces | take 1) using an application token:
import azure.kusto
import azure.kusto.data.request
import msal
cluster = 'https://ade.applicationinsights.io/subscriptions/<MY_SUBSCRIPTION>/resourcegroups/<MY_RESOURCE_GROUP>/providers/microsoft.insights/components/<MY_APP_INSIGHTS_RESOURCE>'
app_id = '<MY_APP_ID>'
app_key = '<MY_SECRET>'
authority_id = '<MY_AAD_SUBSCRIPTION_ID>'
def run():
app = msal.ConfidentialClientApplication(
client_id=app_id,
client_credential=app_key,
authority='https://login.microsoftonline.com/<MY_AAD_SUBSCRIPTION_ID>')
token = app.acquire_token_for_client(['https://help.kusto.windows.net/.default'])
kcsb = azure.kusto.data.request.KustoConnectionStringBuilder.with_aad_application_token_authentication(
connection_string=cluster,
application_token=token['access_token']
)
client = azure.kusto.data.request.KustoClient(kcsb)
result = client.execute('<MY_APP_INSIGHTS_RESOURCE>', 'traces | take 1')
for res in result.primary_results:
print(res)
return 1
if __name__ == "__main__":
run()
However, Redash doesn't support application token authentication: it uses application key authentication, making a call like:
kcsb = azure.kusto.data.request.KustoConnectionStringBuilder.with_aad_application_key_authentication(
connection_string = cluster,
aad_app_id = app_id,
app_key = app_key,
authority_id = '<MY_AAD_SUBSCRIPTION_ID>'
)
I can't successfully connect to my App Insights resource using this type of flow. If I substitute this KustoConnectionStringBuilder into my program above, I get an exception telling me:
The resource principal named https://ade.applicationinsights.io was not found in the tenant named <MY_AAD_SUBSCRIPTION_ID>. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.
Is there something I can do in code or Azure Portal configuration to connect my 'tenant' to the ade.applicationinsights.io resource principal and get this connection working?
Adxproxy supports only tokens minted by Azure Active Directory (AAD). The token must be created for an Azure Data Explorer cluster (ADX), that you own. If you don't have your own ADX cluster, and for whatever reason you want to access your Application Insights resources via Adxproxy, you can always authenticate to 'https://help.kusto.windows.net' and use that token.
Related
I have done the following:
Created a project in Google API console
Enabled the Google Drive API in the project
Created a service account
Shared a Google Drive folder with the service account
Connected successfully to Google Drive and retrieved the list of folders and files shared with the service account.
When you create an OAuth client ID, you can limit that to predefined scopes. As far as I can tell, the service account has access to any Google Drive scope. I wanted to tighten that down to the following scope: https://www.googleapis.com/auth/drive.readonly just as a reassurance that there's no way the Google Drive app I'm making unintentionally adds/edits/deletes any files.
I know I can add the account to different roles. However, I looked through the list multiple times and none of them are related to Google Drive. I attempted to make my own role, but the available permissions on that screen do not reference Google Drive either. It's possible I missed something or there's another place I could look. Any suggestions?
To limit the scope a Service Account, you have to specify the scope on the server-side.
Service accounts are special Google accounts that can be used by
applications to access Google APIs programmatically via OAuth 2.0. A
service account uses an OAuth 2.0 flow that does not require human
authorization. Instead, it uses a key file that only your application
can access.
For example:
In python, you can specify the scope of a service account by creating a list of scopes and use it as parameter when getting the credentials.
Folder and Files:
python:
Search all image with jpeg extension:
import httplib2
import os
from apiclient import discovery
from google.oauth2 import service_account
scopes = ["https://www.googleapis.com/auth/drive.readonly"]
secret_file = os.path.join(os.getcwd(), 'client_secret.json')
credentials = service_account.Credentials.from_service_account_file(secret_file, scopes=scopes)
service = discovery.build('drive', 'v3', credentials=credentials)
page_token = None
while True:
response = service.files().list(q="mimeType='image/jpeg'",
spaces='drive',
fields='nextPageToken, files(id, name)',
pageToken=page_token).execute()
for file in response.get('files', []):
# Process change
print('Found file: %s' % (file.get('name')))
page_token = response.get('nextPageToken', None)
if page_token is None:
break
Output:
Found file: cute-puppy.jpg
Creating folder with readonly scope:
import httplib2
import os
from apiclient import discovery
from google.oauth2 import service_account
scopes = ["https://www.googleapis.com/auth/drive.readonly"]
secret_file = os.path.join(os.getcwd(), 'client_secret.json')
credentials = service_account.Credentials.from_service_account_file(secret_file, scopes=scopes)
service = discovery.build('drive', 'v3', credentials=credentials)
file_metadata = {
'name': 'Invoices',
'mimeType': 'application/vnd.google-apps.folder'
}
file = service.files().create(body=file_metadata,
fields='id').execute()
Error message:
<HttpError 403 when requesting https://www.googleapis.com/drive/v3/files?fields=id&alt=json returned "Insufficient Permission: Request had insufficient authentication scopes.". Details: "Insufficient Permission: Request had insufficient authentication scopes.">
References:
Google Auth Python
OAuth Scopes
I am about to write my first python program to read/write remote azure storage blob (block blob).
I did some research. It is as if the storage "connection string" is absolutely mandatory. In another word, the Microsoft client-side python library requires a "connection-string" created by the storage account in order to gain access to the remote blob.
In order to keep everything as simple as possible, I am hoping that I can write a small python code to invoke an HTTP GET/PUT method (for accessing the remote azure blob storage resource) without touching the "connection string" generated by the storage account. Yet, it doesn't seem to be possible after reading Microsoft storage documentation.
Can anyone make any comment to shed any light? Thanks in advance.
SAS token is generated by account_name and account_key. Both them are in the connection string too.
With SAS token:
from datetime import datetime, timedelta
from azure.storage.blob import BlobServiceClient, generate_account_sas, ResourceTypes, AccountSasPermissions
sas_token = generate_account_sas(
account_name="<storage-account-name>",
account_key="<account-access-key>",
resource_types=ResourceTypes(service=True),
permission=AccountSasPermissions(read=True),
expiry=datetime.utcnow() + timedelta(hours=1)
)
blob_service_client = BlobServiceClient(account_url="https://<my_account_name>.blob.core.windows.net", credential=sas_token)
So you could access with Azure AD Access token based on service principal.
With Azure AD Access token:
from azure.common.credentials import ServicePrincipalCredentials
from azure.storage.blob import BlockBlobService
from azure.storage.common import TokenCredential
TENANT_ID = "xxxxxx"
CLIENT_ID = "xxxxxx"
CLIENT_SECRET = "xxxxxx"
RESOURCE = "https://storage.azure.com/"
credentials = ServicePrincipalCredentials(
client_id = CLIENT_ID,
secret = CLIENT_SECRET,
tenant = TENANT_ID,
resource = RESOURCE
)
token_credential = TokenCredential(credentials.token["access_token"])
ACCOUNT_NAME = "pamelastorage123"
CONTAINER_NAME = "pamelac"
blobService = BlockBlobService(account_name=ACCOUNT_NAME, token_credential=token_credential)
blob = blobService.get_blob_to_text(CONTAINER_NAME, "test.txt")
print(blob.content)
Note: plz follow these steps to assign Storage Blob Data Contributor role and register an application first.
For more information about authentication, see here.
I am implementing authentication for a command line client application that makes a web request to a web API. If I reason correctly, I can apply Azure Active Directory native app authentication scenario.
My concern here is that setting up Azure AD will require significant effort from the client app users on setting up AAD, plus they will have to work with an interactive dialog. This gets even worse in case no human is present, as the service to service scenario is even more complicated.
Is it possible to instead rely on the credentials of the signed-in user of the client computer? Assume Windows-based client machine that is joined to a domain, say FooDomain. The server uses an OWIN-based self-host implementation, Katana.
Related questions:
OWIN Web API Windows Service - Windows Identity Impersonation
#Konrad Jamrozik. IF you are working on .NET and want to use the logged-in user in Windows domain joined (your case), and even AAD joined, my advice would be to use MSAL.NET with the Integrated Windows Authentication (IWA) override. See https://aka.ms/msal-net-iwa. The simplified code looks like this:
string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
PublicClientApplication app = new PublicClientApplication(clientId, authority);
var accounts = await app.GetAccountsAsync();
AuthenticationResult result=null;
if (accounts.Any())
{
result = await app.AcquireTokenSilentAsync(scopes, accounts.FirstOrDefault());
}
else
{
result = await app.AcquireTokenByIntegratedWindowsAuthAsync(scopes);
}
This sample explains how to register the app and provides all the details about the code: https://github.com/azure-samples/active-directory-dotnet-iwa-v2
I am trying to search for users in my own and a third party Azure Active Directoriy via the application access flow.
I use the following to get a valid token.
string authority = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}", "<AD>.onmicrosoft.com");
AuthenticationContext authContext = new AuthenticationContext(authority);
ClientCredential clientCredential = new ClientCredential(<clientId>, <appKey>);
AuthenticationResult result = await authContext.AcquireTokenAsync("https://graph.windows.net" , clientCredential);
string TokenForApplication = result.AccessToken;
I use this method to search for users with a given name.
public async Task<List<IUser>> UsersSearch(IActiveDirectoryClient client, string searchString)
{
List<IUser> usersList = null;
IPagedCollection<IUser> searchResults = null;
IUserCollection userCollection = client.Users;
searchResults = await userCollection.Where(user =>
user.UserPrincipalName.StartsWith(searchString) ||
user.GivenName.StartsWith(searchString)).Take(10).ExecuteAsync();
usersList = searchResults.CurrentPage.ToList();
return usersList;
}
This all works fine on the Azure AD where I first setup the app.
But when when I try to use the app in another Azure Active directory I get the error: Authorization_RequestDenied: Insufficient privileges to complete the operation."
In my original Azure AD I have set all the permissions I need for the app to access the graph API and search for users:
In the third party Azure AD I have gone through the Admin flow and granted the app all the needed permissions:
As far as I can see I get a valid token to each Azure AD, but I keep getting the same error whenever I try to access the third party Azure AD.
The way I change what AD I am trying to access is by changing <AD> in
string authority = string.Format(CultureInfo.InvariantCulture, "https://login.microsoftonline.com/{0}", "<AD>.onmicrosoft.com");
I keep everything else the same.
From your screenshot , the selected permissions is for Microsoft Graph API(https://graph.microsoft.com) , but according to your code , you are acquiring token for Azure AD Graph api(https://graph.windows.net) .
If you want to use Azure AD Graph api , you should add permissions for Windows Azure Active Directory in Required permissions blade of your multi-tenant app , and do admin consent in other AAD .
if you want to use Microsoft Graph API , you should modify your code , use https://graph.microsoft.com instead of https://graph.windows.net .
I have a web application running on Azure. The web application authenticates the users via OpenID Connect from a Azure Active Directory tenant.
Azure Sample on GitHub.
On the Azure Active Directory tenant I have integrated Google Apps and configured single sing-on to Google Apps and automated user provisioning. Tutorial: How to integrate Google Apps with Azure Active Directory.
In my web application I would like to access user content from Google Apps (e.g. files on Google Drive) of the signed in user via Google API.
Is it possible to do this with the help of the setup single sign-on federation, so that the user only needs to sign in to the web application/Azure AD and for the Web API call there is no need for a further sign in, e.g. by using a token optained by Azure AD for accessing the Google Web API?
Tokens obtained from Azure AD cannot be used directly against Google API. However if you integrated Azure AD and Google Apps you should be able to go through the google token acquisition process without gathering user credentials again. You might want to go through an authorization code flow for getting tokens from google, and inject in the request information that would help to leverage your existing session. Typical examples are passing your user's UPN (via login_hint query parameter) and tenant (domain_hint). However I don't know if the google authorization endpoint will pass those along, you'll need to consult the google api documentation.
I ended up with two solutions:
a) Service Account:
Accessing the users data with a service account on behalf of a user.
For this you have to setup a service account: Using OAuth 2.0 for Server to Server Applications
private static ServiceAccountCredential GetServiceAccountCredential(string user)
{
const string privateKey = "<PRIVATEKEY>";
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer("<SERVICEACOUNTEMAIL>")
{
Scopes = new[] {DriveService.Scope.Drive},
User = user
}.FromPrivateKey(privateKey));
return credential;
}
b) User:
Accessing the users data with the user. For this you have to register your app to get the client ID and secret: Using OAuth 2.0 for Web Server Applications
private static UserCredential GetUserCredential(string user)
{
ClientSecrets secrets = new ClientSecrets
{
ClientId = "<CLIENTID>",
ClientSecret = "<CLIENTSECRET>"
};
IDataStore credentialPersistanceStore = new FileDataStore("Drive.Sample.Credentials");
Task<UserCredential> result = GoogleWebAuthorizationBroker.AuthorizeAsync(
secrets,
new[] {DriveService.Scope.Drive},
user,
CancellationToken.None,
credentialPersistanceStore);
result.Wait();
UserCredential credential = result.Result;
return credential;
}
With the credentials I can request the files from Drive:
Claim emailClaim = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email);
IConfigurableHttpClientInitializer credential = GetServiceAccountCredential(emailClaim.Value);
//IConfigurableHttpClientInitializer credential = GetUserCredential(emailClaim.Value);
var service = new DriveService(new BaseClientService.Initializer
{
HttpClientInitializer = credential,
ApplicationName = "My App"
});
FileList list = service.Files.List().Execute();
I am not yet sure which option I will use. Maybe you have some advices or suggestions.