How to recover Call Record information from MS Teams via Graph API - azure

We have a stand alone process which needs to get call record details of completed calls via the Graph API.
We have obtained record IDs via the Azure Dashboard so that we can use them with the following endpoint
GET https://graph.microsoft.com/v1.0/communications/callRecords/{id}
as shown in
https://learn.microsoft.com/en-us/graph/api/callrecords-callrecord-get?view=graph-rest-1.0
The Azure App has been configured for access and has readAll permissions set.
The following code generates a token for access, but when actually trying to read back a call record specified by id, it always returns 404 Not found.
var scopes = new string[] { "https://graph.microsoft.com/.default" };
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create(clientID)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
string id = "YYYYYYYY-F571-45D9-ABC6-XXXXXXXXXXXX"; // Real ID, but obfuscated for this example
HttpResponseMessage response = await httpClient.GetAsync(new Uri("https://graph.microsoft.com/v1.0/communications/callRecords/" + id));
Can anyone advise what I am missing.
Thanks

Related

dotnet Core - Using azure AD authentication to retrive data from sharepoint REST API

My project is set up to use azure ad as login(from the dotnet core template). I have successfully managed to log in.
However, i want to use the same logged in user to retrive data from sharepoint rest api.
I have the following method:
public async Task<FileResults> Test()
{
var siteUrl = "https://xxxxx.sharepoint.com";
var username = "xx#xx.no";
var password = "xxxxxx";
var securePassword = new SecureString();
password.ToCharArray().ToList().ForEach(c => securePassword.AppendChar(c));
var credentials = new SharePointOnlineCredentials(username, securePassword);
var handler = new HttpClientHandler();
handler.Credentials = credentials;
var uri = new Uri(siteUrl);
handler.CookieContainer.SetCookies(uri, credentials.GetAuthenticationCookie(uri));
var json = string.Empty;
using (var client = new HttpClient(handler))
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.Add("Accept", "application/json;odata=verbose");
var response = await client.GetAsync(siteUrl + "/_api/Web/GetFolderByServerRelativeUrl('/Delte%20dokumenter/Test')/Files");
json = await response.Content.ReadAsStringAsync();
var result = Newtonsoft.Json.JsonConvert.DeserializeObject<Rootobject>(json);
var files = result.FileResults;
return files;
}
}
This is working fine and im getting documents from sharepoint.
But, this is when using hardcoded credentials. How do i use the credentials of the logged in user via azure AD? Do i retrive the accesstoken?
To use the Azure AD Authentication you need to have one of the Authentication flows.
Note: Username/Password flow is not recommended.
After that you will be getting the tokens according to the scopes that are specified and you need to hit the Microsoft Graph Api, internally you need to hit the SharePoint API endpoints according to your requirement.
You can start exploring with this sample

How to call Microsoft Graph from console application c#

I need to call Microsoft Graph API to create user in Azure AD.
First I need to test from console application and then need to implement in Azure function.
https://developer.microsoft.com/en-us/graph/graph-explorer
I am new to Microsoft Graph API , How can I connect and execute API from c# console application.
I have already registered the application in AAD.
I am trying to acquire token as :
string resourceId = "https://graph.microsoft.com";
string tenantId = "<tenantID>";
string authString = "https://login.microsoftonline.com/" + tenantId;
string upn = String.Empty;
string clientId = "<ClientID>";
string clientSecret = "<clientSecret>";
//string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
log.Verbose("ClientSecret=" + clientSecret);
log.Verbose("authString=" + authString);
var authenticationContext = new AuthenticationContext(authString, false);
// Config for OAuth client credentials
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resourceId,clientCred);
string token = authenticationResult.AccessToken;
log.Verbose("token=" + token);
I trying to use existing AADB2C.
b2c-extensions-app. Do not modify. Used by AADB2C for storing user data.
I have enabled permission as:
I neither get exception nor get access token and program silently exit
Also :
There is new library
<package id="Microsoft.Identity.Client" version="1.1.0-preview" targetFramework="net46" />
How can I direct login without login pop-up with the following and acquire token ?
PublicClientApplication
I assume that you already have Azure AD application with granted Administrative Consent.
In order to connect from a console app, you'll need to first obtain a valid token. Since you lack a UI, you'll want to Get access without a user. Note that this type of "app-only" token requires Administrative Consent before it can be used.
Then you have to add two NuGet dependencies to your dotnet project
<PackageReference Include="Microsoft.Graph" Version="1.15.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.0.0" />
Microsoft.Identity.Client for authentication using Azure AD and Microsoft.Graph for executing MS Graph queries.
var tenantId = "you-azure-tenand-id";
var clientId = "azure-ad-application-id";
var clientSecret = "unique-secret-generated-for-this-console-app";
// Configure app builder
var authority = $"https://login.microsoftonline.com/{tenantId}";
var app = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
// Acquire tokens for Graph API
var scopes = new[] {"https://graph.microsoft.com/.default"};
var authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
// Create GraphClient and attach auth header to all request (acquired on previous step)
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(requestMessage => {
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
return Task.FromResult(0);
}));
// Call Graph API
var user = await graphClient.Users["Me#domain.com"].Request().GetAsync()
Update 2020.01
There is a new package Microsoft.Graph.Auth that simplify auth and token management.
Let's say you want to use some Beta API this time.
<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.2" />
<PackageReference Include="Microsoft.Graph.Beta" Version="0.12.0-preview" />
var tenantId = "you-azure-tenand-id";
var clientId = "azure-ad-application-id";
var clientSecret = "unique-secret-generated-for-this-console-app";
// Configure application
var clientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientSecret)
.Build();
// Create ClientCredentialProvider that will manage auth token for you
var authenticationProvider = new ClientCredentialProvider(clientApplication);
var graphClient = new GraphServiceClient(authenticationProvider);
// Call Graph API
var user = await graphClient.Users["Me#domain.com"].Request().GetAsync()
In order to connect from a console app, you'll need to first obtain a valid token. Since you lack a UI, you'll want to Get access without a user. Note that this type of "app-only" token requires Administrative Consent before it can be used.
In order to support the Create User scenario, you will need to ensure your permission scopes include User.ReadWrite.All.
Once you have a valid token you can make calls into the Graph API. Graph is a REST API so all calls are made over HTTP with the token passed within the Authorization Header.
You can read a general overview at Get started with Microsoft Graph and REST. There are also several language/framework specific overviews available but all of them assume you have a UI (i.e. not simply console). Generally speaking, if you're looking for a console tool for creating users you may prefer using PowerShell.
This question is rather old, but it was one of the first questions that popped up when I initially needed to do the same thing. Below I will document the steps and resources I used to make it happen:
I used an O365 tenant (you can get one from office.com - note that you can get a one year developer trial). Once you have a tenant, you also have access to Azure portal if you log in as your tenant admin user. Under Azure Portal, go to Active Directory/properties to see the tenant ID.
I followed the instructions here https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-netcore-daemon to create a new registered application. I created a new secret and copied the value (that will be client secret in your console app). The registered application id will be the client ID in your console app.
I cloned the github repo in the above link and changed the values in the appsettings to the tenant ID, client ID, and client secret noted in the steps above.
The code in that repo has some methods called which no longer exist in ConfigurationBuilder as of .NETCore 2.1. I substituted these lines (there's probably a better / shorter way):
authenticationConfig.Tenant = Configuration.GetSection("Tenant").Value.ToString();
authenticationConfig.ClientId = Configuration.GetSection("ClientId").Value.ToString();
authenticationConfig.ClientSecret = Configuration.GetSection("ClientSecret").Value.ToString();
You should now be iterating through users in your tenant. You can go to the graph explorer ( https://developer.microsoft.com/en-us/graph/graph-explorer ) to find more URLs (find the line in Program.cs to substitute them). As far as I know so far, v2.0 of the API is "beta" (put "beta" where "v1.0" is - someone please correct me if I'm wrong).
await apiCaller.CallWebApiAndProcessResultASync("https://graph.microsoft.com/v1.0/users", result.AccessToken, Display);
This MSAL console app tutorial describes getting a token using MSAL (Microsoft Authentication Library) in a .NET console app.
To make a Microsoft Graph call, I replaced the RunAsync() function
with this, which attaches the acquired token to the requests with the
GraphServiceClient:
static async Task RunAsync()
{
const string clientId = "your client id";
string[] scopes = { "User.Read" };
AuthenticationResult result;
var clientApp = new PublicClientApplication(clientId);
try
{
result = await clientApp.AcquireTokenAsync(scopes.Split(new char[] { ' ' }));
Console.WriteLine(result.AccessToken);
GraphServiceClient graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
// Append the access token to the request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
// Some identifying header
requestMessage.Headers.Add("SampleID", "aspnet-connect-sample");
}));
// Get a page of mail from the inbox
var inboxMail = await graphClient.Me.MailFolders.Inbox.Messages.Request().GetAsync();
foreach(var mail in inboxMail.CurrentPage.ToList())
{
Console.Write("From: {0}\nSubject: {1}\nBody:\n{2}\n--------------------\n",
mail.From.EmailAddress.Address, mail.Subject, mail.BodyPreview);
}
}
// Unable to retrieve the access token silently.
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

Access token validation failure when creating Microsoft Graph webhook using the "Web API on-behalf-of flow"

What I am trying to do is to use the "Web API on-behalf-of flow" scenario Microsoft described in this article to create a web hook.
So I started with the Microsoft github example and made sure that I can successfully get the users profile via the Graph API.
Then I modified the code where it gets the users profile to create the web hook, so the code looks like this:
// Authentication and get the access token on behalf of a WPF desktop app.
// This part is unmodified from the sample project except for readability.
const string authority = "https://login.microsoftonline.com/mycompany.com";
const string resource = "https://graph.windows.net";
const string clientId = "my_client_id";
const string clientSecret = "my_client_secret";
const string assertionType = "urn:ietf:params:oauth:grant-type:jwt-bearer";
var user = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
var authenticationContext = new AuthenticationContext(authority,new DbTokenCache(user));
var assertion = ((BootstrapContext) ClaimsPrincipal.Current.Identities.First().BootstrapContext).Token;
var userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null
? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value
: ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
var result = await authenticationContext.AcquireTokenAsync(resource,new ClientCredential(clientId,clientSecret),new UserAssertion(assertion,assertionType,userName));
var accessToken = result.AccessToken;
// After getting the access token on behalf of the desktop WPF app,
// subscribes to get notifications when the user receives an email.
// This is the part that I put in.
var subscription = new Subscription
{
Resource = "me/mailFolders('Inbox')/messages",
ChangeType = "created",
NotificationUrl = "https://mycompany.com/subscription/listen",
ClientState = Guid.NewGuid().ToString(),
ExpirationDateTime = DateTime.UtcNow + new TimeSpan(0, 0, 4230, 0)
};
const string subscriptionsEndpoint = "https://graph.microsoft.com/v1.0/subscriptions/";
var request = new HttpRequestMessage(HttpMethod.Post, subscriptionsEndpoint);
var contentString = JsonConvert.SerializeObject(subscription, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
request.Content = new StringContent(contentString, System.Text.Encoding.UTF8, "application/json");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await new HttpClient().SendAsync(request);
if (response.IsSuccessStatusCode)
{
// Parse the JSON response.
var stringResult = await response.Content.ReadAsStringAsync();
subscription = JsonConvert.DeserializeObject<Subscription>(stringResult);
}
The error I get from the response is:
{
"error":
{
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure.",
"innerError":
{
"request-id": "f64537e7-6663-49e1-8256-6e054b5a3fc2",
"date": "2017-03-27T02:36:04"
}
}
}
The webhook creation code was taken straight from the ASP.NET webhook github sample project, which, I have also made sure that I can run successfully.
The same access token code works with the original user profile reading code:
// Call the Graph API and retrieve the user's profile.
const string requestUrl = "https://graph.windows.net/mycompany.com/me?api-version=2013-11-08";
request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await new HttpClient().SendAsync(request);
So I want to find out:
Is creating a webhook via the graph API using the on-behalf-of flow even supported? Not sure if this SO question is what I'm looking for here.
If it is supported, what am I missing here?
If it is not supported, is there an alternative to achieve it? E.g. is there anything from the existing Office 365 API that I can use?
"message": "Access token validation failure.",
The error means you got incorrect access token for the resource . According to your code ,you get the access token for resource :https://graph.windows.net( Azure AD Graph API) , But then you used that access token to access Microsoft Graph API(https://graph.microsoft.com) ,so access token validation failed .

Create custom extension through Graph API with Client Credentials auth

I have a .NET Web API that I am using to do some interaction with Microsoft Graph and Azure AD. However, when I attempt to create an extension on the user, it comes back with Access Denied.
I know it is possible from the documentation here however, it doesnt seem to work for me.
For the API, I am using client credentials. So my web app authenticates to the API using user credentials, and then from the API to the graph it uses the client.
My app on Azure AD has the Application Permission Read and Write Directory Data set to true as it states it needs to be in the documentation for a user extension.
I know my token is valid as I can retrieve data with it.
Here is my code for retrieving it:
private const string _createApprovalUrl = "https://graph.microsoft.com/beta/users/{0}/extensions";
public static async Task<bool> CreateApprovalSystemSchema(string userId)
{
using(var client = new HttpClient())
{
using(var req = new HttpRequestMessage(HttpMethod.Post, _createApprovalUrl))
{
var token = await GetToken();
req.Headers.Add("Authorization", string.Format("Bearer {0}", token));
req.Headers.TryAddWithoutValidation("Content-Type", "application/json");
var requestContent = JsonConvert.SerializeObject(new { extensionName = "<name>", id = "<id>", approvalLimit = "0" });
req.Content = new StringContent(requestContent, Encoding.UTF8, "application/json");
using(var response = await client.SendAsync(req))
{
var content = await response.Content.ReadAsStringAsync();
ApprovalSystemSchema schema = JsonConvert.DeserializeObject<ApprovalSystemSchema>(content);
if(schema.Id == null)
{
return false;
}
return true;
}
}
}
}
Is there anyone who may have a workaround on this, or information as to when this will be doable?
Thanks,
We took a look and it looks like you have a bug/line of code missing. You appear to be making this exact request:
POST https://graph.microsoft.com/beta/users/{0}/extensions
Looks like you are missing the code to replace the {0} with an actual user id. Please make the fix and let us know if you are now able to create an extension on the user.

Graph API is not working via project code but same URL is working via AD Graph Explorer

I have download a MVC application from Git for AAD graph API. I ran this application but each time i am not getting expected result.
To find the error i run same api using postman and generated token below was the response.
{
"odata.error": {
"code": "Request_ResourceNotFound",
"message": {
"lang": "en",
"value": "Resource not found for the segment 'me'."
}
}
}
I am using below Get URL-
https://graph.windows.net/XXXXX/me?api-version=1.6
Also, to verify is it working with AAD Grapg api explorer. After log in everything is working fine.
Below is my code to call Grapg API-
// Get the access token from the cache
string userObjectID =
ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")
.Value;
string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
//AuthenticationContext authContext = new AuthenticationContext(authority,
// new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(clientId, appKey);
AuthenticationContext authContext = new AuthenticationContext(authority, true);
result = await authContext.AcquireTokenAsync(graphResourceId.ToString(), credential);
var Token = result.AccessToken;
//// AcquireTokenSilentAsync
//result = await authContext.AcquireTokenSilentAsync(graphResourceId, credential,
// new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// Call the Graph API manually and retrieve the user's profile.
string requestUrl = String.Format(
CultureInfo.InvariantCulture,
graphUserUrl,
HttpUtility.UrlEncode(tenantId));
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
// Return the user's profile in the view.
if (response.IsSuccessStatusCode)
{
string responseString = await response.Content.ReadAsStringAsync();
profile = JsonConvert.DeserializeObject<UserProfile>(responseString);
}
Could you guys please tell me what is the problem with my code. Why it is working on AAD explorer not with Localhost.
To request the me endpoint of Azure AD Graph REST, we need to use the delegate token which represents the sign-in user.
The code above you acquire token using the Client Credential flow is request the access token which represents for the application which doesn't contain the info of sign-in user.
To achieve this in the MVC application, we need to acquire the token after you get the authorization code when users login. Next time, we can acquire the token from the token cache based on the login user.
Here is the code for your reference( code sample from here):
result = await authContext.AcquireTokenSilentAsync(graphResourceId, credential,
new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

Resources