Azure REST Api Authentication using C# - c#

I would like to be able to get information about one of my Azure SQL databases using this call: https://learn.microsoft.com/en-gb/rest/api/sql/manageddatabases/manageddatabases_get
When I use the Try It button and login to my account it works perfectly, however I can't get my C# function app to get an authentication token so it can work in C#. I've spent 3 days on this. I have tried the Keyvault way but haven't managed to set up the permissions correctly. Forgetting Keyvault, the nearest I've got I think is by using this code but I don't know what my app password is:
// I am using:
// tenant id is the Azure AD client id
// client id is the application id of my function app in Azure AD
public static string GetAccessToken(string tenantId, string clientId, string clientSecret)
{
var authContextUrl = "https://login.windows.net/" + tenantId;
var authenticationContext = new AuthenticationContext(authContextUrl);
var credential = new ClientCredential(clientId, clientSecret );
var result = authenticationContext.AcquireTokenAsync(resource: "https://management.azure.com/", clientCredential: credential).Result;
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
var token = result.AccessToken;
return token;
}

When I use the Try It button and login to my account it works perfectly
When you click the Try it, you use the user credential with username and user_password to authenticate. And the code you provided is using App registered in Azure AD to authenticate, and it would work well with the following steps you have followed.
1.As silent said, you need to create a Service Principle in Azure Active Directory. You could refer to this article.
2.The Sign in value about TenantId, clientId and clientSecret you could refer to this link.
3.Finally, you would access to Azure SQL Database, you need to add permission to you Azure AD App. Click the App you registered in Azure AD before and click Settings, and add Require Permission. After adding API access, Grant Permission.

I found an answer that worked for me (after 3 days of trying different things and trying to read articles about it on the web - its not very well documented I don't think).
This link contains some powershell steps:
https://msftstack.wordpress.com/2016/01/03/how-to-call-the-azure-resource-manager-rest-api-from-c/
These are the steps I tried in PowerShell
Login-AzureRmAccount
Get-AzureRmSubscription
Select-AzureRmSubscription –SubscriptionID “id”
$SecurePassword=ConvertTo-SecureString <my password> –asplaintext –force
$azureAdApplication = New-AzureRmADApplication -DisplayName “my ARM App” -HomePage
“https://<a home page>” -IdentifierUris “https://<a home page>” -Password $SecurePassword
New-AzureRmADServicePrincipal -ApplicationId $azureAdApplication.ApplicationId
New-AzureRmRoleAssignment -RoleDefinitionName Contributor -ServicePrincipalName $azureAdApplication.ApplicationId
Get-AzureRmSubscription
$subscription = Get-AzureRmSubscription –SubscriptionId "id"
$creds=get-credential
(enter application id and password at this point)
Login-AzureRmAccount -Credential $creds -ServicePrincipal -Tenant $subscription.TenantId

Related

SharePointOnline CSOM 401 Unauthorized Using Provided Access Token

I am building a feature that automates the retrieval of documents and other SharePoint files from a Web API, but I'm having a difficult time getting authorized to perform even basic read operations. I am testing this in a .NET 6 console application using the Microsoft.SharePointOnline.CSOM NuGet package.
I have registered an application in Azure Active Directory and given it the Sites.Read.All permission. I've taken the ClientID, ClientSecret and TenantID as reported by that registered application and I'm using those in my console application. I can retrieve an access token without issue, and decoding that JWT shows that it comes with Sites.Read.All permission. But regardless of what I try, ClientContext.ExecuteQueryAsync() consistently throws an exception complaining that the remote server responded with a 401.
Here is the code that I'm testing this with:
var clientId = "myClientId";
var clientSecret = "myClientSecret";
var tenantId = "myTenantId";
var authority = "https://login.microsoftonline.com/" + tenantId;
var siteUrl = "https://myorg.sharepoint.com";
var app = new ConfidentialClientApplicationBuilder
.Create()
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.WithTenantId(tenantId)
.Build();
var paramBuilder = app.AcquireTokenForClient(new[] { siteUrl + "/.default" });
var authResult = await paramBuilder.ExecuteAsync();
// authResult has successfully retrieved an access token at this point
var context = new ClientContext(siteUrl);
context.ExecutingWebRequest += (_, e) =>
{
e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + authResult.AccessToken;
}
context.Load(context.Web);
await context.ExecuteQueryAsync(); // 401 is thrown here
var title = context.Web.Title;
I have tried several different ways of getting around this to no avail:
I have gone to the Admin center of my SharePoint site and given the app FullControl permissions, as well as giving the app those permissions in Azure AD. This doesn't seem to have changed anything, I still get the same 401.
I have registered an entirely new app directly from my SharePoint sub-site admin center and given it FullControl permissions. I used the new client ID and client secret that were generated, and I was able to get back an access token. No luck, still get the 401 calling ClientContext.ExecuteQueryAsync()
I have tried changing my siteUrl to a SharePoint site-specific URL (e.g. https://myorg.sharepoint.com/sites/mySite), but once I do that I am no longer able to retrieve an access token. I instead get an Msal exception thrown, AADSTS500011, which reads:
"The resource principal named https://myorg.sharepoint.com/sites/mysite was not found in the tenant named (my tenant). 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.
I have also tried using the base siteUrl to retrieve the token, then giving the site-specific URL to ClientContext. I get the same 401 result.
I have tried several different authorities in case the token I'm being provided is invalid. I've tried using the V1 token URL, the V2 token URL, no token-specific URL (only the default authority address + tenant ID). All of these return an access token, but none of them avoid the 401.
A MS documentation article suggests appending an additional "/" to the requested .default scope in instances where a 401 is being returned (e.g. https://myorg.sharepoint.com/sites/mysite//.default). This doesn't seem to have changed anything.
My application seems to have the permissions it needs to do this basic read operation, but I am continually rebuffed. I am using the ClientID, ClientSecret and Tenant ID as copied directly from the AAD application page. The code I'm using above is recommended by Microsoft to use the new SharePointOnline.CSOM package. What am I missing here?
Constructor of ClientContext requires site url including site name.
var clientId = "myClientId";
var clientSecret = "myClientSecret";
var tenantId = "myTenantId";
var authority = "https://login.microsoftonline.com/" + tenantId;
var siteUrl = "https://myorg.sharepoint.com";
var siteName = "MySiteName";
var app = new ConfidentialClientApplicationBuilder
.Create()
.WithClientSecret(clientSecret)
.WithAuthority(authority)
.WithTenantId(tenantId)
.Build();
var paramBuilder = app.AcquireTokenForClient(new[] { siteUrl + "/.default" });
var authResult = await paramBuilder.ExecuteAsync();
// authResult has successfully retrieved an access token at this point
var webFullUrl = $"{siteUrl}/sites/{siteName}";
var context = new ClientContext(webFullUrl);
If the site has some prefix
var webFullUrl = $"{siteUrl}/sites/{sitePrefix}/{siteName}";
I wound up "solving" this problem by using the PnP.Framework NuGet package instead of Microsoft.SharePointOnline.CSOM. I changed nothing else about my app registration or its designated permissions, and PnP.Framework was able to handle it without issue (and with fewer arguments). It seems to know something that SharePointOnline.CSOM doesn't considering that the following simple console app works:
using System;
using PnP.Framework
const string clientId = "myClientId";
const string clientSecret = "myClientSecret";
const string siteUrl = "https://myorg.sharepoint.com/sites/mysite";
using var clientContext = new AuthenticationManager()
.GetACSAppOnlyContext(siteUrl, clientId, clientSecret);
cc.Load(cc.Web);
await cc.ExecuteQueryAsync(); // no longer throws a 401
Console.WriteLine(cc.Web.Title); // prints my site's title
I tried to use the newer PnP.Core SDK, but I couldn't find any documentation or examples on how to get that package working with an app-only client secret authenticated context. PnP.Framework's API is the cleanest and most reliable that I've found as of yet.

how reproduce result of powershell > az login in c#?

The result of "az login" is list of subscriptions: like here:
https://learn.microsoft.com/en-us/rest/api/resources/subscriptions/list?source=docs
How to make token for this request without providing tenantId or clientId exactly how it was made on website's login?
I can make token is quite close to required but do not have what I see inside token from website:
var functionCred = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(clientId, secret);
var context = new AuthenticationContext("https://login.microsoftonline.com/" + tenantId, false);
var result = context.AcquireTokenAsync("https://management.core.windows.net/", functionCred).Result;
var token44 = result.AccessToken;
What should I do to improve token?
By default, az login command logs in with a user account. CLI will try to launch a web browser to log in interactively, if browser is not available then CLI will fall back to device code login.
For the question -
How to make token for this request without providing tenantId or clientId exactly how it was made on website's login?
It is not feasible to get an authentication token without using tenantId and clientId. Mostly token is generated by client id and secret. Token can be acquired via app registration + tenentId or user credentials + tenentId.
Below code snippet shows how you get the access token using Azure Active Directory Authentication Library (ADAL).
static AuthenticationResult AccessToken()
{
string resourceUri = "https://datacatalog.azure.com";
// register a client app and get a Client ID
string clientId = clientIDFromAzureAppRegistration;
//A redirect uri gives AAD more details about the specific application that it will authenticate.
string redirectUri = "https://login.live.com/oauth20_desktop.srf";
// Create an instance of AuthenticationContext to acquire an Azure access token
string authorityUri = "https://login.windows.net/common/oauth2/authorize";
AuthenticationContext authContext = new AuthenticationContext(authorityUri);
// AcquireToken takes a Client Id that Azure AD creates when you register your client app.
return authContext.AcquireToken(resourceUri, clientId, new Uri(redirectUri), PromptBehavior.RefreshSession);
}
For more information check this Steps to get an access token section of the Microsoft Document.

Unable to get access token from Google for Service Account

I'm trying to configure an application able to work with Gmail API. As you know to work with it we must have an access token. There are several way of requesting this token, but for my needs it should be a service account, because in future this program code will be inside the Windows Service... (so, there is no opportunity to receive the token manually by redirecting from Google URL, only a web-request and response is a way out)
So, what I have done already:
Created new project (in Google Cloud Platform);
Created new service account in this project (according to the steps mentioned here: https://developers.google.com/identity/protocols/oauth2/service-account#creatinganaccount );
Generated and downloaded *.P12 key;
Enabled domain-wide delegation [before step 4 as were suggested in many similar questions];
Authorized the scope "https://mail.google.com/" in G Suite admin account for correct Client Id (according to the steps mentioned here: https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority );
Used such simple code for authorization and requesting token:
const string serviceAccountEmail = "***test#oauthtester-271011.iam.gserviceaccount.com";
const string serviceAccountCertPath = #"C:\Users\user\Documents\Visual Studio 2017\Projects\OAuthTester\OAuthTester\bin\Debug\oauthtester-271011-bd2cced31ea5.p12";
const string serviceAccountCertPassword = "notasecret";
const string userEmail = "***oauthtest#***.com";
X509Certificate2 certificate = new X509Certificate2(
serviceAccountCertPath,
serviceAccountCertPassword,
X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { GoogleScope.ImapAndSmtp.Name }, //"https://mail.google.com/"
User = userEmail
}.FromCertificate(certificate));
credential.RequestAccessTokenAsync(CancellationToken.None).Wait();
Unfortunately, I'm facing with an error:
Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.
I have also tried:
To change serviceAccountEmail to ClientId;
To create, remove and add again the Authorized access in G Suite for the same Client Id;
To delete and create another service account and then Authorize new Client Id in G Suite.
Unfortunately, each time I'm facing with the same error. Maybe somebody guesses what I do wrong?

Authentication to Azure Active Directory and receive AADSTS90019 error

I am trying to authenticate against AAD using the following code:
string userName = "something.com"; //(just an example)
string password = "IafksdfkasdaFadad=asdad=a="; //(just an example)
string clientId = "6cd6590f-4db9-4c6b-98d1-476f9e90912f"; //(just an example)
var credentials = new UserPasswordCredential(userName, password);
var authenticationContext = new AuthenticationContext("https://login.windows.net/common");
var result = await authenticationContext.AcquireTokenAsync("https://api.partnercenter.microsoft.com", clientId, credentials);
return result;
and I got AADSTS90019 error: No tenant-identifying information found in either the request or implied by any provided credentials.
As a remark, it is just a console application made in Visual Studio using C#.
Based on the information from https://learn.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes the explication for AADSTS90019 error is: MissingTenantRealm - Azure AD was unable to determine the tenant identifier from the request.
So, my question is: What is the tentant identifier and how should I use it in my request?
Should it be the one from the following screenshot? The screenshot is made from the Azure account.
Azure Application Overview
Any information can help.
Thank you.
You should initialize your authentication context with a tenant-specific authority instead of common:
var authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/your-directory-id");
Replace your-directory-id with your Directory (tenant) id.
var authenticationContext = new AuthenticationContext("https://login.windows.net/common");
Here Replace the string "common" with the tenant name.

How to Get a User List from Azure Actice Directory (Native App)?

I want to get a list of users who have the right to use the application.
It is not possible to enter Secret keys in the Native App. I try to use the same accessToken as I log in. Authentication works fine. ObjectId is the same as my native app ObjectId.
result = await authContext.AcquireTokenAsync(graphResourceId, _ADClientId, redirectUri, new PlatformParameters(PromptBehavior.Never));
string accessToken = result.AccessToken;
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot, async () => await Task.FromResult(accessToken));
var groupFetcher = (IGroupFetcher)activeDirectoryClient.Groups.ExecuteAsync().Result.CurrentPage.First(g => g.ObjectId == "5deedc8c-2ba6-45d8-a4f2-xxxxxxx");
Error: Authorization_RequestDenied
What went wrong?
Within your Azure Active Directory, go to your Application registration and add a delegation permission for the Windows Active Directory API. Im not sure which is the least permission you can select (maybe Sign-in and read user profile may work) but Read directory data will give you the necessary permissions:

Categories