how reproduce result of powershell > az login in c#? - 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.

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.

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?

Azure REST Api Authentication using 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

What are the permission needed for a ServicePrincipal to call getObjectsByObjectIds?

What are the permission needed for an Service Principal to call AD graph API: Get Objects By Object Ids:
POST https://graph.windows.net/{tenantID}/getObjectsByObjectIds?api-version=1.6
I set up an App with
Application Permission: Read directory data.
Delegated Permission:
Read Directory data
Read and write directory data
Read all users full profile
Sign in and read user profile.
But I am receiving Forbidden ( status code 403) while making the API call:
Also i got access token through this code:
string authority = "https://login.microsoftonline.com/" + "microsoft.com";
AuthenticationContext ac = new AuthenticationContext(authority);
ClientCredential cr = new ClientCredential("Appid", "Cred");
string OfficeUrl = "https://graph.windows.net";
AuthenticationResult authenticationResult = ac.AcquireTokenAsync(OfficeUrl, cr).Result;
string token = authenticationResult.AccessToken;
// string token = this.SetAuthorizationToken();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
Yes, this is correct permissions of Read directory data (Directory.Read.All) is required for this API. Please make sure to press the "Grant Permissions" button after adding the required permissions for the Graph API because this permission requires admin consent.

Azure Native Client Authentication

I have created a native app on Azure but I'm not sure how to get the access token with just the client ID. I was using a web app earlier in which I had a client ID and secret and used those to authenticate.
I've tried using AcquireTokenSilentlyAsync but I cant do that.
You can use the AcquireToken method like below. This will pop up a browser dialog to ask for your credentials.
private static string GetToken(string authority, string clientId, string redirectUri)
{
var authContext = new AuthenticationContext(authority, validateAuthority: false);
var result = authContext.AcquireToken("https://graph.windows.net",
clientId, new Uri(redirectUri), PromptBehavior.Auto);
return result.AccessToken;
}
If you want to authenticate without prompting the user, have a look at the resource owner credential grant. This is explained in this article by Vittorio Bertocci.

Categories