I am trying to build a simple console application in C# that would allow me to call in this case the beta api. I am also trying to call a method delegate method.
This is my main call
static void Main(string[] args)
{
const string clientId = <clientID>;
const string tenantId = <tenantID>;
const string clientSecret = <secretKey>;
const string tokenUrl = "https://login.microsoftonline.com/common/oauth2/v2.0/token";
List<string> scopeURL = new List<string>();
scopeURL.Add("https://graph.microsoft.com/.default");
var graphClient = DelegateApp(clientId,tenantId,clientSecret,scopeURL);
try
{
getUsersAsync(graphClient).GetAwaiter().GetResult();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
This is how i defined my graph call
private static GraphServiceClient DelegateApp(string clientId, string tenantId, string clientSecret, List<string> scope)
{
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.Build();
OnBehalfOfProvider authProvider = new OnBehalfOfProvider(confidentialClientApplication, scope);
var graphClient = new GraphServiceClient(authProvider);
graphClient.BaseUrl = "https://graph.microsoft.com/beta";
return graphClient;
}
And this is what I am trying to call
public async static Task getUsersAsync(GraphServiceClient client)
{
try
{
var bookingBusiness = await client.BookingBusinesses.Request().GetAsync();
foreach (var business in bookingBusiness)
{
Console.WriteLine(business.DisplayName);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
This is the exception I get.
ex.Message = "Code: generalException\r\nMessage: An error occurred sending the request.\r\n"
This happens when i call client.bookingbusinesses.request.getasync();
Related
I have code to authenticate with EWS using oAuth working fine if I call it from winform button click, but not working if I place my code inside custom class and call it inside constructor I don't know what is the problem ?
Authenticate Code function :
public async Task<AuthenticationResult> oAuthLoginRequest()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var cca = ConfidentialClientApplicationBuilder
.Create(Settings.Default.appId)
.WithClientSecret(Settings.Default.clientSecret)
.WithTenantId(Settings.Default.tenantId)
.Build();
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
try
{
_authenticationResult = await cca.AcquireTokenForClient(ewsScopes)
.ExecuteAsync();
return _authenticationResult;
}
catch (Exception ex)
{
string.Format("oAuthLoginRequest: Exception= {0}", ex.Message).LogIt(TLogType.ltError);
return _authenticationResult;
}
}
Working well and I got access token :
private async void button1_Click(object sender, EventArgs e)
{
oAuthLoginRequest();
//Access Token Available here
var accessToken = _authenticationResult.AccessToken ; //Working fine
}
NOT WORKING :
public class TServiceController
{
private bool _started = false;
public bool Started { get { return _started; } }
TEWSService mailService = null;
public ExchangeService _service = null;
public AuthenticationResult _authenticationResult = null;
public DateTimeOffset TokenExpiresOn { get; set; }
public async Task<AuthenticationResult> oAuthLoginRequest()
{
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var cca = ConfidentialClientApplicationBuilder
.Create(Settings.Default.appId)
.WithClientSecret(Settings.Default.clientSecret)
.WithTenantId(Settings.Default.tenantId)
.Build();
// "https://outlook.office365.com/.default" ,"https://outlook.office365.com/EWS.AccessAsUser.All" , "https://graph.microsoft.com/Mail.Send"
// "https://ps.outlook.com/full_access_as_app"
var ewsScopes = new string[] { "https://outlook.office365.com/.default" };
try
{
_authenticationResult = await cca.AcquireTokenForClient(ewsScopes).ExecuteAsync();
TokenExpiresOn = _authenticationResult.ExpiresOn;
("AccessToken:" + _authenticationResult.AccessToken).LogIt(TLogType.ltDebug);
}
catch (Exception ex)
{
string.Format("oAuthLoginRequest: Exception= {0}", ex.Message).LogIt(TLogType.ltError);
}
return _authenticationResult;
}
public TServiceController()
{
var auth = oAuthLoginRequest().Result; //STUCK HERE
"Service controller started.".LogIt();
} //end constructore
} //END CLASS
Any explanation ?
I tried two methods one of them work just fine in winform click button and other solution not working within my class constructor .
A while ago I created an application to create Outlook events in the calendars of our employees with Microsoft Graph in a background process. Now I want to expand the current application and also manage contacts.
First of all I want to created a new contact person.
Code that calls the GraphHelper.cs:
AuthenticationConfig config = AuthenticationConfig.ReadFromJsonFile("appsettings.json");
string[] scopes = new string[] { $"{config.ApiUrl}.default" };
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create($"{config.ClientId}")
.WithTenantId($"{config.Tenant}")
.WithAuthority(new Uri(config.Authority))
.WithClientSecret($"{config.ClientSecret}")
.Build();
ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication, scopes[0]);
GraphHelper.Initialize(authenticationProvider);
string status = CreateContact().GetAwaiter().GetResult();
private static async Task<string> CreateContact()
{
return await GraphHelper.CreateContact("admin#premed.be");
}
GraphHelper.cs:
public static void Initialize(IAuthenticationProvider authProvider)
{
graphClient = new GraphServiceClient(authProvider);
}
public static async Task<string> CreateContact(string userName)
{
var contact = new Contact
{
GivenName = "GivenNameTest",
Surname = "SurnameTest",
EmailAddresses = new List<EmailAddress>()
{
new EmailAddress
{
Address = "GivenNameTest.SurnameTest#hotmail.com",
Name = "GivenNameTest SurnameTest Test"
}
},
BusinessPhones = new List<String>()
{
"+32489789654"
}
};
try
{
await graphClient.Users[userName]
.Contacts
.Request()
.AddAsync(contact);
return "OK";
}
catch (ServiceException ex)
{
return ex.Message;
}
}
When I run the code, I get an ErrorAccessDenied error "Access is denied. Check credentials and try again."
But I don't understand why I get the error. For the autorisation I use the same functionality I used for the calendar. Also the same tenant, client-id and client secret is used. In the Azure portal all permissions are given.
For example: creating an event is no problem.
public static async Task<string> CreateEvent(Event newEvent, string userName)
{
try
{
// POST /users/{id | userPrincipalName}/events
var returnEvent = await graphClient.Users[userName]
.Events
.Request()
.Header("Prefer", "outlook.timezone=\"Europe/Paris\"")
.AddAsync(newEvent);
return returnEvent.Id;
}
catch (ServiceException ex)
{
return ex.Message;
}
}
Can someone help me please?
I am trying to login to Azure with my c# code and was able to find something like this on the net.
Not sure what to pass in the scope array here and once I get the access token, how can I make the rest call ?
public static string getAccessToken(string[] scopes)
{
var interactiveCredential = new InteractiveBrowserCredential();
return interactiveCredential.GetToken(new Azure.Core.TokenRequestContext(scopes, null)).Token;
}
First create a Azure AD Application:
Follow this link
Then get the Tenant ID , Client ID , Client Secret from the AD Application and use this class to query your azure subscription recourses.
class CustomLoginCredentials : ServiceClientCredentials
{
//Variables
private static string tenantId = "<Tenant ID goes here>";
private static string clientId = "<Client ID goes here>";
private static string clientSecret = "<Client Secret goes here>";
private static string windowsURL = "https://login.windows.net/";
private static string azureManagementURL = "https://management.azure.com/";
private string AuthenticationToken { get; set; }
public override void InitializeServiceClient<T>(ServiceClient<T> client)
{
var authenticationContext =
new AuthenticationContext(windowsURL + tenantId);
var credential = new ClientCredential(clientId, clientSecret);
var result = authenticationContext.AcquireTokenAsync(azureManagementURL,
clientCredential: credential).Result;
AuthenticationToken = result.AccessToken;
}
public override async Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AuthenticationToken);
await base.ProcessHttpRequestAsync(request, cancellationToken);
}
}
Example:
private async void GetAzureResourcesConsumption()
{
var credentials = new CustomLoginCredentials();
ConsumptionManagementClient client = new ConsumptionManagementClient(credentials);
client.SubscriptionId = subscriptionId;
var resources = await client.UsageDetails.ListAsync(null, null, null, top: NumberOfItems);
var results = resources.ToList<UsageDetail>();
}
Do you mean to get access token?
private static string GetAuthorizationToken()
{
ClientCredential cc = new ClientCredential(ClientId, ServicePrincipalPassword);
var context = new AuthenticationContext("https://login.windows.net/" + AzureTenantId);
var result = context.AcquireTokenAsync("https://management.azure.com/", cc);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
return result.Result.AccessToken;
}
I have a class that acts as a wrapper for the MS Graph SDK. Fairly simple purpose, inside the class there are methods for getting various data sets out of Graph for a particular user.
EDIT: this runs under the context of an application, so no user creds are ever used.
All of that part works fine, what isn't working is the DelegateAuthenticationProvider never finds the access token in the cache. Each call to a graph endpoint gets a new token, even in the same instance of the class. Within the class I'm using a singleton pattern for the GraphServiceClient.
Here is the code I'm using to handle the client:
private static GraphServiceClient _graphServiceClient;
private static AuthenticationContext _authContext;
private static readonly object _locker = new();
private GraphServiceClient GetClient(M365ServiceOptions options)
{
if (_graphServiceClient == null)
{
lock (_locker)
{
if (_graphServiceClient == null)
{
_authContext = new AuthenticationContext($"https://login.microsoftonline.com/{options.TenantId}/");
var provider = new DelegateAuthenticationProvider(async (requestMessage) =>
{
AuthenticationResult accessToken;
try
{
//Use Token from cache or refresh token
accessToken = await _authContext.AcquireTokenSilentAsync(options.GraphURL, options.ClientId);
_logger.LogDebug("Cache Hit");
}
catch (AdalSilentTokenAcquisitionException)
{
//If no cached token, get a new one
_logger.LogDebug($"Cache Miss: {_authContext.TokenCache?.Count}");
var credentials = new ClientCredential(options.ClientId, options.ClientSecret);
accessToken = _authContext.AcquireTokenAsync(options.GraphURL, credentials).Result;
}
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.AccessToken);
});
_graphServiceClient = new GraphServiceClient(provider);
}
}
}
return _graphServiceClient;
}
While debugging it is clear the token cache has an item in it, and the details all seem to match, but no matter what, the AcquireTokenSilentAsync always throws the AdalSilentTokenAcquisitionException exception and forces it to get a new token for each call. This is impacting performance as no matter what, each call to the graph gets a new token.
Thank you for any assistance.
Please try this class. First call AuthenticationHelper.GetAuthenticatedClient() to get the a GraphServiceClient, then you this to access the user information.
public class AuthenticationHelper
{
static readonly string clientId = "";
public static string[] Scopes = { "User.Read" };
public static PublicClientApplication IdentityClientApp = new PublicClientApplication(clientId);
public static string TokenForUser = null;
public static DateTimeOffset Expiration;
private static GraphServiceClient graphClient = null;
// Get an access token for the given context and resourced. An attempt is first made to
// acquire the token silently. If that fails, then we try to acquire the token by prompting the user.
public static GraphServiceClient GetAuthenticatedClient()
{
if (graphClient == null)
{
// Create Microsoft Graph client.
try
{
graphClient = new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
var token = await GetTokenForUserAsync();
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
requestMessage.Headers.Add("SampleID", "MSGraphConsoleApp");
}));
return graphClient;
}
catch (Exception ex)
{
Debug.WriteLine("Could not create a graph client: " + ex.Message);
}
}
return graphClient;
}
public static async Task<string> GetTokenForUserAsync()
{
AuthenticationResult authResult;
try
{
authResult = await IdentityClientApp.AcquireTokenSilentAsync(Scopes, IdentityClientApp.GetAccountsAsync().Result.First());
TokenForUser = authResult.AccessToken;
}
catch (Exception)
{
if (TokenForUser == null || Expiration <= DateTimeOffset.UtcNow.AddMinutes(5))
{
authResult = await IdentityClientApp.AcquireTokenAsync(Scopes);
TokenForUser = authResult.AccessToken;
Expiration = authResult.ExpiresOn;
}
}
return TokenForUser;
}
public static void SignOut()
{
foreach (var user in IdentityClientApp.GetAccountsAsync().Result)
{
IdentityClientApp.RemoveAsync(user);
}
graphClient = null;
TokenForUser = null;
}
}
I have the following class:
public static class RequestGenerator
{
static string clientID;
static string clientSecret;
static string url = "https://servicestierdp1qa.workmgmt-bcbsm/v1.0/createWorkItem";
static HttpClient client = null;
static WorkResponse workResponse;
public static string generateRequest(WorkRequest workRequest)
{
if (client == null)
{
client = new HttpClient();
clientID = "myclientid";
clientSecret = "mysecret";
}
sendRequest(workRequest); //warning is moot because ensureSuccessStatusCode implicitly awaits
return workResponse.payloadArea.id;
}
static async Task sendRequest(WorkRequest workRequest)
{
try
{
string jsonRequest = JsonConvert.SerializeObject(workRequest);
HttpResponseMessage responseMessage = client.PostAsync(url,
new StringContent(jsonRequest,
Encoding.UTF8,
"application/json")).Result;
responseMessage.EnsureSuccessStatusCode();
string jsonResponse = await responseMessage.Content.ReadAsStringAsync();
workResponse = JsonConvert.DeserializeObject<WorkResponse>(jsonResponse);
}
catch (HttpRequestException ex)
{
throw ex;
}
catch (Exception ex)
{
throw ex;
}
}
}
This class is nearly identical to a class from another process that works. I don't know why I am getting this compile error.
The only real difference between this class and the one that works is the url, clientid and secret (in addition to names, etc)