I try to read all users contacts by using graph sdk and c# but in the user at the response always the array of contacts is null even though the user has contacts
I was requesting all user's contacts from exchange online with graph sdk and c#, but
var graphResult = graphClient.Users.Request().GetAsync().Result;
Console.WriteLine(graphResult[0].Contacts[0]);
returns NullReferenceException.
I granted following privileges:
the following token is set in azure
here you can see my tenant id and so on
Main Class
using Microsoft.Graph;
using Azure.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Identity.Client;
using System.Data.SqlClient;
namespace ExchangeTestAppKonsole
{
internal class Program
{
static void Main(string[] args)
{
getContacts();
Console.ReadLine();
}
public static void getContacts()
{
var clientId = "de196208-b4d7-468f-8fa4-7328551566b9";
var clientSecret = "~uG8Q~~vrTGuaIPfzeIR9GUUpSK5aaG.KZTYGcnD";
var redirectUri = "https://global.consent.azure-apim.net/redirect";
var authority = "https://login.microsoftonline.com/0be300e6-91fd-4638-bcd1-40d742ef6ece/v2.0";
var cca = ConfidentialClientApplicationBuilder.Create(clientId)
.WithAuthority(authority)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret)
.Build();
// use the default permissions assigned from within the Azure AD app registration portal
List<string> scopes = new List<string>();
scopes.Add("https://graph.microsoft.com/.default");
var authenticationProvider = new MsalAuthenticationProvider(cca, scopes.ToArray());
GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider);
var graphResult = graphClient.Users.Request().GetAsync().Result;
Console.WriteLine(graphResult[0].Contacts[0]);
}
}
}
Authentication Provider
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace ExchangeTestAppKonsole
{
internal class MsalAuthenticationProvider : IAuthenticationProvider
{
private IConfidentialClientApplication _clientApplication;
private string[] _scopes;
public MsalAuthenticationProvider(IConfidentialClientApplication clientApplication, string[] scopes)
{
_clientApplication = clientApplication;
_scopes = scopes;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var token = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
public async Task<string> GetTokenAsync()
{
AuthenticationResult authResult = null;
authResult = await _clientApplication.AcquireTokenForClient(_scopes).ExecuteAsync();
return authResult.AccessToken;
}
}
}
I also requested the contacts of the first user by logging in with this user into graphExplorer
and requested the /me/contacts endpoint it shows 3 contacts
it seems to be a premissions thing but i've no idea what exactly the problem is.
Contacts is a relationship on User resource type.
If you want to include some relationship in the response, you need to specify the relationship in Expand.
Not all relationships and resources support the expand. According to the documentation there is no mention that contacts supports expand, so probably this code won't work.
var graphResult = graphClient.Users
.Request()
.Expand("contacts")
.GetAsync().Result();
Console.WriteLine(graphResult[0].Contacts[0]);
In that case you need to make a separate call for each user to get contacts.
var contacts = await graphClient.Users["{user_id}"].Contacts
.Request()
.GetAsync();
Users relationships
Related
We used to be able to log into SharePoint Online using the SharePointOnlineCredentials class eg here.
This class is not available in .NET5 and I don't have the necessary Azure permissions to configure the application in Azure AD as described here
I'd like a C# program bring up a prompt, so the user can log into Sharepoint manually. Once the user has done this the program will do what it needs to do. Is there some C# code that will bring up the Sharepoint online login prompt?
I've downloaded Pnp and CSOM. This code contains the following errors.
The name 'GetSecureString' does not exist in the current context
No overload for method 'GetContext' takes 3 arguments
'Program.GetContext(Uri, string, SecureString)': not all code paths return a value
The name 'context' does not exist in the current context
using System;
using System.Security;
using Microsoft.SharePoint.Client;
using System.Threading.Tasks;
using PnP.Framework;
namespace ConsoleApplication1
{
class Program
{
public static async Task Main(string[] args)
{
Uri site = new Uri("https://contoso.sharepoint.com/sites/siteA");
string user = "joe.doe#contoso.onmicrosoft.com";
SecureString password = GetSecureString($"Password for {user}");
// Note: The PnP Sites Core AuthenticationManager class also supports this
using (var authenticationManager = new AuthenticationManager())
using (var context = authenticationManager.GetContext(site, user, password))
{
context.Load(context.Web, p => p.Title);
await context.ExecuteQueryAsync();
Console.WriteLine($"Title: {context.Web.Title}");
}
}
public ClientContext GetContext(Uri web, string userPrincipalName,
SecureString userPassword)
{
context.ExecutingWebRequest += (sender, e) =>
{
// Get an access token using your preferred approach
string accessToken = MyCodeToGetAnAccessToken(new Uri($"
{web.Scheme}://{web.DnsSafeHost}"), userPrincipalName, new
System.Net.NetworkCredential(string.Empty, userPassword).Password);
// Insert the access token in the request
e.WebRequestExecutor.RequestHeaders["Authorization"] =
"Bearer " + accessToken;
};
}
}
}
I am building a console application in C#. I want to make some calls to Microsoft Graph API to access and edit some Excel files in my SharePoint so I can automate some processes in my Organization.
The logic of the app is simple.
I call Azure Active Directory to authenticate this console application using the clients credential flow which means we will provide a clientsID and AppKey. I took the clientsID and AppKey from Azure Active Directory > App Registrations.
Then I want to receive the access token and use this to make a GET Request to the Microsoft Graph API.
E.g https://graph.microsoft.com/v1.0/me/
But then response I get is this:
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure. Invalid audience.",
"innerError": {
"request-id": "0a3ec**************",
"date": "2019-10-15T13:54:33"
}
}
}
Below you will find the full code of my application with the two methods of getting the access token and calling the Graph API:
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IdentityModel.Tokens;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using AuthenticationContext = Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext;
namespace Project_Budget
{
class Program
{
private const string clientId = "14f1****************";
private const string aadInstance = "https://login.microsoftonline.com/{0}";
private const string tenant = "******.onmicrosoft.com";
private const string resource = "https://graph.windows.net";
private const string appKey = "IKV***********";
static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
private static HttpClient httpClient = new HttpClient();
private static AuthenticationContext context = null;
private static ClientCredential credential = null;
static void Main(string[] args)
{
context = new AuthenticationContext(authority);
credential = new ClientCredential(clientId,appKey);
Task<string> token = GetToken();
token.Wait();
//Console.WriteLine(token.Result + "\n");
Task<string> graphCall = GetExcelFile(token.Result);
graphCall.Wait();
Console.WriteLine(graphCall.Result + "\n");
Console.ReadLine();
}
private static async Task<string> GetExcelFile(string result)
{
string apiJsonResult = null;
var apiCallString = "https://graph.microsoft.com/v1.0/me/";
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result);
var getResult = await httpClient.GetAsync(apiCallString);
if (getResult.Content != null)
{
apiJsonResult = await getResult.Content.ReadAsStringAsync();
}
return apiJsonResult;
}
private static async Task<string> GetToken()
{
AuthenticationResult result = null;
string token = null;
result = await context.AcquireTokenAsync(resource, credential); //authentication context object
token = result.AccessToken;
return token;
}
}
}
I have given all the access required for the app to run. Also I run the query on Graph Explorer and runs properly.
Why do I get this error on the console application?
Ideally, the resource should actually be
private const string resource = "https://graph.microsoft.com";
But you still need to select the scopes that you want to target in your application.
The way you are doing it at the moment does seem to acquire/set the relevant scopes which is done for you by Graph Explorer.
I would suggest following this quick start tutorial on how to build a dot net core console app and you should be up and running in no time.
It uses the MSAL library which works better than the ADAL library you are using in your scenario.
https://learn.microsoft.com/en-us/graph/tutorials/dotnet-core
I think the problem is with resource value you're specifying in your code.
Current Code: (This resource value https://graph.windows.net corresponds to Azure AD Graph API which is older API)
private const string resource = "https://graph.windows.net";
Try changing this to: (This resource value https://graph.microsoft.com corresponds to newer Microsoft Graph API which is the one you're calling in code that comes later var apiCallString = "https://graph.microsoft.com/v1.0/me/";)
private const string resource = "https://graph.microsoft.com";
Hi I'm trying to write a simple console app which I intend to make into a batch file and get a list of external users who were invited by email have and now they have guest accounts in our Azure tenant and they have redeemed the url that was sent to them in email. When they redeem, their extenalUserState sets to "Accepted". I want to find which ones have that status.
I was told that I have to point to beta version of the API and not v.1.0 of the graph endpoint.
I have the following rudimentary code I have written looking at various examples I could find on GitHub/MS documentation for API etc.
using Microsoft.Graph;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
namespace CreateAzureADUser
{
class Program
{
static string TenantDomain;
static string TenantId;
static string ClientId;
static string ClientSecret;
static void Main(string[] args)
{
GetUsers();
//Console.WriteLine("------------------------------------------\n\n");
//GetGroupsAndMembers();
//CreateAzureADUserNow();
}
private static void GetUsers()
{
var graphServiceClient = CreateGraphServiceClient();
var users = graphServiceClient.Users.Request().Filter("userType eq 'Guest' and startswith(mail,'phs')")
.Select("id,mail,OnPremisesExtensionAttributes,userType,displayName,externalUserState")
.GetAsync()
.Result;
Console.WriteLine("Users found: {0}", users.Count);
Console.WriteLine();
foreach (var item in users)
{
Console.WriteLine("displayName: {3} \nuser id: {0} \nuser email: {1} \nExtensionAttribute8: {2}\n", item.Id, item.Mail, item.OnPremisesExtensionAttributes.ExtensionAttribute8, item.DisplayName);
}
}
public static GraphServiceClient CreateGraphServiceClient()
{
TenantDomain = "mycompanytenant.onmicrosoft.com";
TenantId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
ClientId = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";
ClientSecret = "zzzzzzzzzzzz";
var clientCredential = new ClientCredential(ClientId, ClientSecret);
var authenticationContext = new AuthenticationContext($"https://login.microsoftonline.com/mycompanytenant.onmicrosoft.com");
var authenticationResult = authenticationContext.AcquireTokenAsync("https://graph.microsoft.com", clientCredential).Result;
var delegateAuthProvider = new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
return Task.FromResult(0);
});
// Use this for v.1.0 endpoint
//return new GraphServiceClient(delegateAuthProvider);
// Use this for connecting to beta endpoint
return new GraphServiceClient("https://graph.microsoft.com/beta", delegateAuthProvider);
}
}
}
When I run through the debugger, I do not see "ExternalUserState" as an attribute on the users that are returned.
How to access ExternalUserState attribute on the guest user object?
You're using the SDK so you're using Graph v1.0, not the Beta. The SDKs are all generated from v1.0 metadata so beta properties and methods simply do not exist in the models.
From time to time there is a beta build pushed out to GitHub but it is generally a few versions behind. Currently, the latest beta SDK available seems to be v1.12.0 (for reference, the current SDK is v1.15).
I am trying to connect to my SQL server in azure and list the dbs from a .net application, but I keep getting
ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
even though i am trying to use the Sql Management client with TokenCloudCredentials.
var authContext = new AuthenticationContext(authority);
var clientCredential = new ClientCredential(clientId, appKey);
var result = authContext.AcquireTokenAsync(resource, clientCredential).Result;
var credentials = new Microsoft.Azure.TokenCloudCredentials(subscriptionId, result.AccessToken);
var client = new SqlManagementClient(credentials);
try
{
var servers = await client.Servers.ListAsync();
}
catch (CloudException c)
{
Console.WriteLine(c.Message);
throw;
}
The AD application have permissions to access the resource group and the Azure Management API.
Any ideas why it keeps complaining about a certificate, while using token?
EDIT: I managed to do it using the "new" fluent management API. You need to create an AD application associated with the subscription and have access to the resource group. Then just create credentials and initialize the fluent API.
using Microsoft.Azure.Management.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent;
using Microsoft.Azure.Management.ResourceManager.Fluent.Authentication;
using Microsoft.Azure.Management.ResourceManager.Fluent.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AzureManagement
{
public class Program
{
public static void Main(string[] args)
{
var azureCredentials = new AzureCredentials(new
ServicePrincipalLoginInformation
{
ClientId = "clientId",
ClientSecret = "clientSecret="
}, "tenantId", AzureEnvironment.AzureGlobalCloud);
var _azure = Azure
.Configure()
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(azureCredentials)
.WithSubscription("subscriptionId");
var sql = _azure.SqlServers.List().ToList();
foreach (var s in sql)
{
var dbs = s.Databases.List().ToList();
}
Console.ReadLine();
}
}
}
I am not sure if this is supported. Please create a support case for this problem.
The code works like this.
You have to assign a role - in my use case I assigned the contributor role to the App registration, on to the Ressource Group
Trying to test search functions of the YoutubeAPI, but getting this
Could it be because of my channel(which is binded to my gmail account, which I currently using in console.developers.google) was banned?
UPD:
Created new account, situation still the same
Well, what I've have done here:
created porject in console.developers.google
activated youtube data api(choosed app or somth like this, not the
js one), downloaded json, which looks like that
First I call the Authorize method (new page shows, asking permission, where I just clicking allow button, and everything seems like ok), but then I trying to use Search
and I get 401 error
heres the code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApplication3.Services.ServiceAuthoriaztion.Impl
{
using System.Data.Entity.Core.Metadata.Edm;
using System.IO;
using System.Threading;
using Google.Apis;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
public class Youtube : ICustomService
{
private static YouTubeService _currentYouTubeService;
public void Authorize()
{
UserCredential userCreds;
;
var filePath = string.Format("{0}{1}",AppDomain.CurrentDomain.BaseDirectory, #"App_Data\client_id.json");
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
userCreds = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] {YouTubeService.Scope.YoutubeReadonly},
"user",
CancellationToken.None,
new FileDataStore("YouTubeData")
).Result;
}
_currentYouTubeService = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = userCreds,
ApplicationName = "yttest"
});
SerachTest();
}
private void SerachTest()
{
var searchListRequest = _currentYouTubeService.Search.List("snippet");
searchListRequest.Q = "Google"; // Replace with your search term.
searchListRequest.MaxResults = 50;
// Call the search.list method to retrieve results matching the specified query term.
var searchListResponse = searchListRequest.Execute();
var asa = new List<string>();
}
}
}
UPD2:
Tried other type of app - not helped either.
JSON file looks like that
Okay, I decided start everything from scratch, so, I don't know what exactly helped me but here's a simple instructions about how I made it work. And also, here's the proof :)
So, first I created new project, set name of it DanilTestApp.
Second, turned on YouTube Data API there.
When I was creating new credentials, choosed OAuth Client ID and then, as a type, choosed Other.
After it was ready I just downloaded JSON.
Then in my code I decide to add refreshing token functionality, so now it looks like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace WebApplication3.Services.ServiceAuthoriaztion.Impl
{
using System.Data.Entity.Core.Metadata.Edm;
using System.IO;
using System.Threading;
using Google.Apis;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using Google.Apis.YouTube.v3;
public class Youtube : ICustomService
{
private static YouTubeService _currentYouTubeService;
public void Authorize()
{
UserCredential userCreds;
var filePath = string.Format("{0}{1}", AppDomain.CurrentDomain.BaseDirectory, # "App_Data\client_id.json");
using (var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
userCreds = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
new[] {
YouTubeService.Scope.YoutubeReadonly
},
"user",
CancellationToken.None,
new FileDataStore("YouTubeData")
).Result;
}
RefreshToken(userCreds);
_currentYouTubeService = new YouTubeService(new BaseClientService.Initializer
{
HttpClientInitializer = userCreds,
ApplicationName = "DanilTestApp"
});
SerachTest();
}
private void SerachTest()
{
var searchListRequest = _currentYouTubeService.Search.List("snippet");
searchListRequest.Q = "Google"; // Replace with your search term.
searchListRequest.MaxResults = 50;
// Call the search.list method to retrieve results matching the specified query term.
var searchListResponse = searchListRequest.Execute();
var asa = new List<string>();
}
//might not work, but something like this, got working code at home, office right now
private void UpdateTokenIfExpired(UserCredential credential)
{
if (credential.Token.IsExpired(credential.Flow.Clock))
{
Console.WriteLine("The access token has expired, refreshing it");
if (credential.RefreshTokenAsync(CancellationToken.None).Result)
{
Console.WriteLine("The access token is now refreshed");
}
else
{
throw new Exception("refresh token failed");
}
}
}
}
}