Calling Microsoft Graph API to create an event, What am I doing wrong? - c#

I've been trying to call Microsoft Graph API for creating events, however I've not been able to do it.
Context: I have a Web MVC application (C#) already in production, with the "common" authentication method, reading a database of users. Recently the customer asked me the possibility to create Microsoft Teams Meetings from the application and also those created meetings have to be scheduled in the Microsoft Teams Calendar with the "Join" button to enter the meeting.
I already configured the API permissions, client secret and used the other properties like tenant, user id, etc from the Azure Portal, I'm sharing a screenshot of my configuration. I'm doing the "Get access on behalf of a user" process.
API Permissions:
Permissions image
Taking the example of the authorize endpoint from the docs, of course I'm replacing the values with my own info
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?
client_id=11111111-1111-1111-1111-111111111111
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&response_mode=query
&scope=offline_access%20user.read%20mail.read
&state=12345
Here is my code to Receive the code once the user authorizes the permissions, I'm just storing the value in a static class for testing
public ActionResult ReceiveCode(string code)
{
AuthenticationConfig.Code = code;
//this.Code = code;
return RedirectToAction("Index");
}
Once I got the Auth code, I'm using it to create the event with the generated token, also I already verified that the token contains the permissions given in the Azure Portal.
This is the input for the /events endpoint
var json = JsonConvert.SerializeObject(new
{
subject = "Let's go for lunch",
body = new
{
contentType = "HTML",
content = "Does noon work for you?"
},
start = new
{
dateTime = "2017-04-15T12:00:00",
timeZone = "Pacific Standard Time",
},
end = new
{
dateTime = "2017-04-15T14:00:00",
timeZone = "Pacific Standard Time"
},
location = new
{
displayName = "Harry's Bar",
},
attendees = new List<Attendee>()
{
new Attendee
{
EmailAddress = new EmailAddress
{
Address = "mymail#whatever.com",
Name = "Foo Bar"
},
Type = AttendeeType.Required
}
},
allowNewTimeProposals = true,
isOnlineMeeting = true,
onlineMeetingProvider = "teamsForBusiness",
});
This is the complete method, for the json value, please see the json above. I also tried with the "me" url but it does not work either.
public async Task<ActionResult> OnlineMeeting()
{
try
{
var httpClient = new HttpClient();
var paramsDictionary = new Dictionary<string, string>();
paramsDictionary.Add("client_id",AuthenticationConfig.ClientId);
paramsDictionary.Add("scope", "Calendars.ReadWrite");
paramsDictionary.Add("code", AuthenticationConfig.Code);
paramsDictionary.Add("redirect_uri", "https://localhost:44379/Meeting/Reunion/ReceiveCode");
paramsDictionary.Add("grant_type", "authorization_code");
paramsDictionary.Add("client_secret", AuthenticationConfig.ClientSecret);
var url = string.Format("https://login.microsoftonline.com/{0}/oauth2/v2.0/token", "tenant");
var response = await httpClient.PostAsync(url, new FormUrlEncodedContent(paramsDictionary));
if (response.IsSuccessStatusCode)
{
var jsonResponse = await response.Content.ReadAsStringAsync();
var jsonResult = JsonConvert.DeserializeObject(jsonResponse) as JObject;
var accessToken = jsonResult.GetValue("access_token").ToString();
httpClient = new HttpClient();
var json = JsonConvert.SerializeObject(new { });
var defaultRequestHeaders = httpClient.DefaultRequestHeaders;
if (defaultRequestHeaders.Accept == null || !defaultRequestHeaders.Accept.Any(m => m.MediaType == "application/json"))
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
defaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var data = new StringContent(json);
response = await httpClient.PostAsync("https://graph.microsoft.com/v1.0/users/{user id}/events", data);
if (response.IsSuccessStatusCode)
{
// Nice
}
else
{
Console.WriteLine($"Failed to call the web API: {response.StatusCode}");
string content = await response.Content.ReadAsStringAsync();
}
}
else
{
Console.WriteLine($"Failed to call the web API: {response.StatusCode}");
string content = await response.Content.ReadAsStringAsync();
}
}
catch (Exception ex)
{
}
return View();
}
I'm able to the get the token, but when trying to create the event returns the next response.
{
"error": {
"code": "ResourceNotFound",
"message": "Resource could not be discovered.",
"innerError": {
"date": "2021-08-31T22:58:18",
"request-id": "c5c07afa-fa89-4948-a9f8-f80ca4cbafc3",
"client-request-id": "c5c07afa-fa89-4948-a9f8-f80ca4cbafc3"
}
}
}
Am I missing something? Maybe the wrong endpoint?
Please, help.
Thanks in advance.

Related

Problem accessing passed parameter in url redirection

Hello I'm trying to do an Oauth 2.0 to integrate two systems together,
and it uses oauth so I have successfully been able to get the auth code from the url after authenticating, but I couldn't get the parameter from the redirect url to use it and generate the token within the code.
public void getData()
{
var query = "Store-url?scope=offline_access&state=12345678&response_type=code&approval_prompt=auto&redirect_uri=localhost/api/Omar/test&client_id=";
Response.Redirect(query);
}
this is the redirect url which take me to the store website to give the access and then it return the auth code in the url like this:
https://localhost/api/Omar/test?code=OPCKC8KwzcTPUWnvJsA-apz09-PsDNrDYTrUfffMIDU.pnjIZahbJBxC9G_BY4KEom2LNkPHKbnojJCeotylcKA&scope=settings.read+products.read_write+offline_access&state=12345678
and I tried the code to generate the token and it worked
the problem is how I get store the code passed in the parameter so I can use in my method that generates the token within the code
and the method code is
[HttpGet("test")]
public async Task<IActionResult> test(string code)
{
var values = new Dictionary<string, string>
{
{ "client_id", "" },
{ "client_secret", "" },
{ "grant_type", "authorization_code" },
{ "code", code },
{ "redirect_uri", "localhost/api/Omar/test" }
};
var content = new FormUrlEncodedContent(values);
var response = await client.PostAsync("store-generate-token-url", content);
var responseString = await response.Content.ReadAsStringAsync();
return Ok("TEST SUCCESS"+ " "+ responseString);
}

OAuth 2.0 Mailkit "Authentication failed" in MVC, but c# console-app works fine

Because Microsoft ends the support for Basic Authentication access for IMAP in Office 365 I try to update our application to use OAuth 2.0. We use MailKit in a MVC .Net web-application to access an IMAP mailbox, but I get an error saying Authentication failed. However, as a test, I can get it to work in a c# console-application.
The strange thing is:
If I copy the access-token I acquired using the console-application and use it in my web-application I can successfully authenticate and read emails. So that part works.
The authentication itself seems to be successful in the web-application. Our webapp redirects to the Microsoft login-page, MFA works, I see successful audits in Azure A/D and I do get a token in the callback. However, this token gives the Authentication failed error by Mailkit.
In Azure A/D I see some of these errors between the successful audits, but I'm not sure whether they are related or not: Error AADSTS16000 SelectUserAccount - This is an interrupt thrown by Azure AD, which results in UI that allows the user to select from among multiple valid SSO sessions. This error is fairly common and may be returned to the application if prompt=none is specified.
I already verified that the scope for which I acquire a token is the same for both console and web.
The main difference is that I use pca.AcquireTokenInteractive(scopes) in the console application to acquire the token, but I use a webclient call with a call-back in the MVC-controller.
Here is my code (MVC):
public ActionResult Index()
{
string clientID = "[client-id here]";
string clientSecret = "[client-secret here]";
string redirectUri = "[redirectUri here]";
AuthorizationServerDescription server = new AuthorizationServerDescription
{
AuthorizationEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize"),
TokenEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/token"),
ProtocolVersion = ProtocolVersion.V20,
};
List<string> scopes = new List<string>
{
"email",
"offline_access",
"https://outlook.office365.com/IMAP.AccessAsUser.All"
};
WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);
OutgoingWebResponse response = consumer.PrepareRequestUserAuthorization(
scopes, new Uri(redirectUri));
return response.AsActionResultMvc5();
}
public async Task<ActionResult> Authorized(string code, string state, string session_state)
{
List<string> scopes = new List<string>
{
"IMAP.AccessAsUser.All",
"User.Read",
"offline_access"
};
HttpClient httpClient = new HttpClient();
var values = new Dictionary<string, string>
{
{ "Host", "https://login.microsoftonline.com" },
{ "Content-Type", "application/x-www-form-urlencoded" },
{ "client_id", "[client-id here]" },
{ "scope", string.Join(" ",scopes) },
{ "code", code },
{ "redirect_uri", [redirectUri here] },
{ "grant_type", "authorization_code" },
{ "client_secret", "[client-secret here]" },
{ "state", state },
};
var content = new FormUrlEncodedContent(values);
var response = await httpClient.PostAsync("https://login.microsoftonline.com/organizations/oauth2/v2.0/token", content);
var jsonString = await response.Content.ReadAsStringAsync();
var oathToken = JsonConvert.DeserializeObject<OathToken>(jsonString);
var oauth2 = new SaslMechanismOAuth2("[Email here]", oathToken.access_token);
var stringBuilder = new StringBuilder();
using (var client = new ImapClient())
{
try
{
await client.ConnectAsync("outlook.office365.com", 993, SecureSocketOptions.Auto);
await client.AuthenticateAsync(oauth2);
var inbox = client.Inbox;
inbox.Open(FolderAccess.ReadOnly);
for (int i = 0; i < inbox.Count; i++)
{
var message = inbox.GetMessage(i);
stringBuilder.AppendLine($"Subject: {message.Subject}");
}
await client.DisconnectAsync(true);
return Content(stringBuilder.ToString());
}
catch (Exception e)
{
return Content(e.Message);
}
}
}
The error Authentication failed occurs at the line
await client.AuthenticateAsync(oauth2);
The problem was the scope "email".
We had to remove that. Exactly why, I don't know. It was no problem when used in the console app. Maybe it had to do with the fact we used pca.AcquireTokenInteractive(scopes) in that.

Microsoft.Graph SDK SendMail As User - 400 - Unexpected Exception or Open navigation properties are not supported

I'm working on an application that needs to send email notifications and reminders to users when they have actions to complete. A user submits data, then the app notifies other users to perform actions in a specific order (i.e. User 1: Task 1, after Task 1 is complete, User 2: Task 2, etc.) - if a user is taking too long to perform their action, the system will remind them then defer to their manager (via a Windows service or similar). Due to this, I can't send messages on behalf of the current signed in user - it needs to be able to send messages on its own. It is preferred to send on behalf of the user that submitted the data, so subsequent users can reply directly to them.
I'm using the Microsoft Graph Client Library v1.10.0. Running my code yields an aggregate exception ultimately boiling down to a code 400, code "generalException", message "Unexpected exception returned from the service." I've used LinqPad to look into the Graph objects, and attempted to reproduce the call in Postman, which yields a 400 with a message of "Open navigation properties are not supported on OpenTypes. Property name: 'microsoft.graph.sendmail'."
More thorough details:
Application has Microsoft Graph -> Send mail as any user, Read all groups, Read all users' full profiles permissions.
Calling GraphServiceClient.Client.Users["MyUPN"].SendMail(email, true).Request().PostAsync() yields a 400 general exception with Unexpected exception returned from the service. (Full code below)
Looking at the request, I found it's calling https://graph.windows.net:443/{{tenantId}}/users/{{MyUPN}}/microsoft.graph.sendMail?api-version=1.6 and attempted to make the same call via Postman (with a valid token), which yielded a 400 bad request with message Open navigation properties are not supported on OpenTypes. Property name: 'microsoft.graph.sendMail'.
Full Code:
String MyEmailAddress = "";
String MyUpn = "";
String TenantId = "";
String AppGuid = "";
String AppKey = "";
var sender = new Microsoft.Graph.Recipient()
{
EmailAddress = new Microsoft.Graph.EmailAddress() { Address = MyEmailAddress }
};
var email = new Microsoft.Graph.Message
{
Sender = sender,
From = sender,
Subject = "Test",
Body = new Microsoft.Graph.ItemBody()
{
Content = "Test Body",
ContentType = Microsoft.Graph.BodyType.Text
}
};
email.ToRecipients = new List<Microsoft.Graph.Recipient>(){ sender };
email.BodyPreview = "Test Summary";
GraphSdk _Sdk = new GraphSdk(TenantId, AppGuid, AppKey);
// Where the error throws
await _Sdk.Client.Users[MyUpn].SendMail(email, true).Request().PostAsync();
As a test, I also tried await _Sdk.Client.Users[MyUpn].Messages.Request().Top(20).GetAsync(); which yielded the same error. Other Graph calls, like getting a user's groups or manager, work fine - this error only appears on email-related calls.
Update 9/19/2018 AM
It looks like I can get emails working if I use a certificate to generate the token instead of the Key -> Password; and call the Outlook API instead. Unfortunately, that doesn't work through the GraphServiceClient and Graph API - it can use the certificate, and use the Outlook API base URL, but the microsoft.graph.sendMail action is just sendMail in the Outlook API.
For maintainability, I'd still like to get it all working under the Graph API so I'm still looking for an answer to the original question.
At some point I had set the BaseUrl for the client to https://graph.windows.net:443/{{tenantId}}, possibly due to the varying branding over the past few years (Microsoft Graph vs Azure Graph). Under current recommendations for the Microsoft.Graph it should be https://graph.microsoft.com/v1.0/ - which also appears to be the default value.
Additionally, I had to switch to using a certificate instead of the Azure-generated Key -> Password for the app.
Total working code is:
String AADTenantId = "";
String AppGuid = "";
String SenderAddress = "";
String SenderId = "";
String ToAddress = "";
String SubjectText = "";
String BodyText = "";
Byte[] Certificate = ...GetCertBytes...
String CertPassword = "";
var client = new GraphServiceClient(new DelegateAuthenticationProvider(
async requestMessage =>
{
var authContext = new AuthenticationContext($"https://login.microsoftonline.com/{AADTenantId}");
var cert = new X509Certificate2(Certificate, CertPassword);
var clientAssertion = new ClientAssertionCertificate(AppGuid, cert);
AuthenticationResult authresult = await authContext.AcquireTokenAsync("https://graph.microsoft.com", clientAssertion);
// Append the access token to the request
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authresult.AccessToken);
}));
var sender = new Recipient()
{
EmailAddress = new EmailAddress() { Address = SenderAddress }
};
var email = new Message
{
Sender = sender,
From = sender,
Subject = SubjectText,
Body = new ItemBody()
{
Content = BodyText,
ContentType = BodyType.Text
},
ToRecipients = new List<Recipient>() {
new Recipient() { EmailAddress = new EmailAddress { Address = ToAddress }}
}
};
await client.Users[SenderId].SendMail(email, true).Request().PostAsync();
According to your description, you want send an email but get an 400 error.
Based on my test, we can use the following steps to send an email.
step1, we should get a graphClient which is a authenticated HttpClient.
The code like this:
GraphServiceClient graphServiceClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
string accessToken = await MsalAuthProvider.Instance.GetUserAccesstokenAsync();
requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", accessToken);
}));
return graphServiceClient;
We can refer to the simple code in the official document.
step2, we can use the following code to send a mail:
public async Task<bool> SendAsync(EmailAddress toaddress)
{
var email = new Message
{
Body = new ItemBody
{
Content = "Test for sending eamil ",
ContentType = BodyType.Text,
},
Subject = "Test for sending eamil",
ToRecipients = new List<Recipient>
{
new Recipient
{
EmailAddress = toaddress
}
},
};
try
{
await _serviceClient.Me.SendMail(email).Request().PostAsync(); // the _serviceClient is the result in the step1.
return true;
}
catch (Exception ex)
{
return false;
}

AADSTS90102: 'redirect_uri' value must be a valid absolute Uri

I'm following the instructions from this page. I've created myself a windows service and I'm stuck at requesting access token from Azure AD.
I managed to get an authorization code but I get the redirect_uri error when I POST. This is what my code looks like:
var dictionary = new Dictionary<string, string>
{
{ "resource", "https%3A%2F%2Foutlook.office365.com"},
{"client_id","Application ID from azure AD portal" }, //-is this ok?
{"client_secret","Object ID from azure AD portal" }, //-is this ok?
{"grant_type","authorization_code" },
{"redirect_uri",HttpUtility.UrlEncode("https://haw.trustteam.be/") },
{ "code","AQABAAIAAAAB..1AiAA"}
};
var content = new FormUrlEncodedContent(dictionary);
string requestUrl = "https://login.windows.net/common/oauth2/token"; // also tried with login.microsoftonline.com
using (HttpClient client = new HttpClient())
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, requestUrl);
request.Content = content;
using (HttpResponseMessage response = await client.SendAsync(request))
{
string responseString = await response.Content.ReadAsStringAsync();
return response.Content.ToString();
}
}
What am I doing wrong?
FormUrlEncodedContent function also help posting data in the HttpMessage body as url-encoded key/value pairs. So just remove the HttpUtility.UrlEncode function:
var dictionary = new Dictionary<string, string>
{
{ "resource", "https://outlook.office365.com"},
{"client_id","Application ID from azure AD portal" },
{"client_secret","Application key from azure portal" },
{"grant_type","authorization_code" },
{"redirect_uri","https://haw.trustteam.be/" },
{ "code","AQABAAIAAAAB..1AiAA"}
};
var content = new FormUrlEncodedContent(dictionary);
In addition , you can add client secret in Keys blade of your azure ad application . Please refer to this document .

Why new fb api 2.4 returns null email on MVC 5 with Identity and oauth 2?

Everything used to work perfect until fb upgraded it's api to 2.4 (I had 2.3 in my previous project).
Today when I add a new application on fb developers I get it with api 2.4.
The problem: Now I get null email from fb (loginInfo.email = null).
Of course I checked that the user email is in public status on fb profile,
and I went over the loginInfo object but didn't find any other email address.
and I google that but didn't find any answer.
please any help.. I 'm kind of lost..
Thanks,
My original code (which worked on 2.3 api):
In the AccountController.cs:
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
//A way to get fb details about the log-in user:
//var firstNameClaim = loginInfo.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:first_name"); <--worked only on 2.3
//var firstNameClaim = loginInfo.ExternalIdentity.Claims.First(c => c.Type == "urn:facebook:name"); <--works on 2.4 api
// Sign in the user with this external login provider if the user already has a login
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// If the user does not have an account, then prompt the user to create an account
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email }); //<---DOESN'T WORK. loginInfo.Email IS NULL
}
}
In the Startup.Auth.cs:
Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions fbOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
AppId = System.Configuration.ConfigurationManager.AppSettings.Get("FacebookAppId"),
AppSecret = System.Configuration.ConfigurationManager.AppSettings.Get("FacebookAppSecret"),
};
fbOptions.Scope.Add("email");
fbOptions.Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
{
OnAuthenticated = (context) =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
foreach (var claim in context.User)
{
var claimType = string.Format("urn:facebook:{0}", claim.Key);
string claimValue = claim.Value.ToString();
if (!context.Identity.HasClaim(claimType, claimValue))
context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
}
return System.Threading.Tasks.Task.FromResult(0);
}
};
fbOptions.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
app.UseFacebookAuthentication(fbOptions);
Taken from a Katana Thread I devised the following:
Change the FacebookAuthenticationOptions to include BackchannelHttpHandler and UserInformationEndpoint as seen below. Make sure to include the names of the fields you want and need for your implementation.
var facebookOptions = new FacebookAuthenticationOptions()
{
AppId = "*",
AppSecret = "*",
BackchannelHttpHandler = new FacebookBackChannelHandler(),
UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email,first_name,last_name"
}
Then create a custom FacebookBackChannelHandler that will intercept the requests to Facebook and fix the malformed url as needed.
UPDATE: The FacebookBackChannelHandler is updated based on a 27 Mar 2017 update to the FB api.
public class FacebookBackChannelHandler : HttpClientHandler
{
protected override async System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
{
request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("?access_token", "&access_token"));
}
var result = await base.SendAsync(request, cancellationToken);
if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
{
return result;
}
var content = await result.Content.ReadAsStringAsync();
var facebookOauthResponse = JsonConvert.DeserializeObject<FacebookOauthResponse>(content);
var outgoingQueryString = HttpUtility.ParseQueryString(string.Empty);
outgoingQueryString.Add("access_token", facebookOauthResponse.access_token);
outgoingQueryString.Add("expires_in", facebookOauthResponse.expires_in + string.Empty);
outgoingQueryString.Add("token_type", facebookOauthResponse.token_type);
var postdata = outgoingQueryString.ToString();
var modifiedResult = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(postdata)
};
return modifiedResult;
}
}
public class FacebookOauthResponse
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
}
One useful addition would be to check for the version 3.0.1 of the library and throw an exception if and when it changes. That way you'll know if someone upgrades or updates the NuGet package after a fix for this problem has been released.
(Updated to build, work in C# 5 without new nameof feature)
For me this Issue was solved by upgrading to Microsoft.Owin.Security.Facebook 3.1.0 and adding 'email' to the Fields collection:
var options = new FacebookAuthenticationOptions
{
AppId = "-------",
AppSecret = "------",
};
options.Scope.Add("public_profile");
options.Scope.Add("email");
//add this for facebook to actually return the email and name
options.Fields.Add("email");
options.Fields.Add("name");
app.UseFacebookAuthentication(options);
To resolve this you need to install Facebook SDK from NuGet packages.
In StartUp File
app.UseFacebookAuthentication(new FacebookAuthenticationOptions
{
AppId = "XXXXXXXXXX",
AppSecret = "XXXXXXXXXX",
Scope = { "email" },
Provider = new FacebookAuthenticationProvider
{
OnAuthenticated = context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));
return Task.FromResult(true);
}
}
});
In Controller or Helper
var identity = AuthenticationManager.GetExternalIdentity(DefaultAuthenticationTypes.ExternalCookie);
var accessToken = identity.FindFirstValue("FacebookAccessToken");
var fb = new FacebookClient(accessToken);
dynamic myInfo = fb.Get("/me?fields=email,first_name,last_name,gender"); // specify the email field
With this you can get the EmailId,First-last Name, Gender.
You can also add your additional required properties in that query string.
Hope this will help someone.
Just want to add on Mike's answer that this line
facebookOptions.Scope.Add("email");
still needs to be added after
var facebookOptions = new FacebookAuthenticationOptions()
{
AppId = "*",
AppSecret = "*",
BackchannelHttpHandler = new FacebookBackChannelHandler(),
UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email,first_name,last_name,location"
}
And if you already registered your facebook account to your dev website with no "email permission". After changing the code and trying again, you will still not get the email because the email permission isn't granted to your dev website. The way I do is go to https://www.facebook.com/settings?tab=applications, remove my facebook app, and redo the process again.
Upgrade Microsoft.Owin to 3.0.1 ( Install-Package Microsoft.Owin.Security.OAuth )
in the Startup.Auth.cs add facebookOptions.UserInformationEndpoint = "https://graph.facebook.com/v2.4/me?fields=id,name,email";
Read the changelog, this is by design. You need to explicitly request the fields and edges you want retuned in the response:
Declarative Fields
To try to improve performance on mobile networks,
Nodes and Edges in v2.4 requires that you explicitly request the
field(s) you need for your GET requests. For example, GET
/v2.4/me/feed no longer includes likes and comments by default, but
GET /v2.4/me/feed?fields=comments,likes will return the data. For more
details see the docs on how to request specific fields.

Categories

Resources