C# / Azure AD / Graph API - set additional user attributes with Open Extensions - c#

I'm trying to add some custom attributes to my newly created user objects in my Azure Active Directory with Microsoft Graph.
My code looks like this:
public async Task addUser(string firstName, string lastName)
{
var mail = firstName.ToLower() + "." + lastName.ToLower() + "#mail.com";
var user = new User
{
AccountEnabled = true,
UserPrincipalName = mail,
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = randomString(10) // I wrote a dedicated function here
},
};
var res = await graphServiceClient
.Users
.Request()
.AddAsync(user);
await addExtension(res.Id);
}
public async Task addExtension(string id)
{
var extension = new OpenTypeExtension
{
ExtensionName = "com.test.test", // necessary I guess
AdditionalData = new Dictionary<string, object>
{
{"NewAttribute1", "Batman"},
{"NewAttribute2" , "Spiderman"}
}
};
await graphServiceClient
.Users[id]
.Extensions
.Request()
.AddAsync(extension);
}
The error message, that I'm currently receiving is:
Message: One or more properties contains invalid values.
Inner error:
AdditionalData:
...
I've orientated myself here:
https://learn.microsoft.com/en-us/graph/api/opentypeextension-post-opentypeextension?view=graph-rest-1.0&tabs=http
I hope someone can help,
thanks!

I managed to make my program work. The code above is actually correct and is now doing fine for me, after some adjustments.
I had to read the extension seperately, as seen below. Accessing the user.Extensions property didn't work for me. Hope this helps someone in the future :)
public async Task getUserExtensions(string userID, string extensionID)
{
var extension = await configuration
.configure()
.Users[userID]
.Extensions[extensionID]
.Request()
.GetAsync();
foreach (var item in extension.AdditionalData)
{
Console.WriteLine($"{item.Key} | {item.Value}");
}
}

Related

Exception while processing email invitation : The invited user already exists in the directory

I have the below code written in c#, and using Micorosoft Graph API
//Get redemption URL
var invitation = new Invitation
{
InvitedUserEmailAddress = "<email address>",
InviteRedirectUrl = "<Redirect URL>",
SendInvitationMessage = false,
};
var inviteResponse = graphClient.Invitations
.Request()
.AddAsync(invitation).Result;
I see following error in the log
ProcessEmailInvitation - Exception while processing email invitation: One or more errors occurred. (Code: BadRequest
Message: The invited user already exists in the directory as object ID: xxxxx-xxxxxx-xxxxxxxxx-xxxxxxxxx.
Is it anything to do with SendInvitationMessage parameter?
I tried to change the code as follow,
var inviteResponse = Task.Run(async () => await graphClient.Invitations.Request().AddAsync(invitation).Result);
but it says
Microsoft.Graph.Invition is not awaitable.
The error indicated that you may invite a user who is already be invited into your tenant, you may test with another email account. And pls allow me to post a sample code here for a better look. If it doesn't help you I'll delete it. By the way SendInvitationMessage is false by default according to the api document.
public async Task<string> inviteUserAsync() {
var invitation = new Invitation
{
InvitedUserEmailAddress = "xxxx#outlook.com",
InviteRedirectUrl = "https://www.google.com"
};
await _graphServiceClient.Invitations.Request().AddAsync(invitation);
return "success";
}

"The tenant for tenant guid does not exist" when using GraphAPI - Even with user type as Member

I am trying to access email using Microsoft Graph API. When I try to access the email I got the below error.
Microsoft.Graph.ServiceException: 'Code: OrganizationFromTenantGuidNotFound
Message: The tenant for tenant guid '<some id>' does not exist.
Here is the code to get the emails
var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.CompletedTask;
}));
var userId = "quicksilverconnect#outlook.com"; // Tried vijaynirmal#quicksilverconnectoutlook.onmicrosoft.com also
var messageId = "Actual message id";
var email = await graphServiceClient.Users[userId].Messages[messageId].Request().GetAsync();
Here is the code to get access token
private const string _clientId = "xxxxxxx-xxxxxx-xxxxxxx-xxxx";
private const string _clientSecret = "xxxxxxx-xxxxxx-xxxxxxx-xxxx";
private const string _tenantName = "ecd90453-34b6-xxxx-xxxx-xxxxxxxxx";
private readonly string _uri = $"https://login.microsoftonline.com/{_tenantName}/oauth2/v2.0/token";
private const string _grantType = "client_credentials";
private const string _scope = "https://graph.microsoft.com/.default";
public async Task<string> GetAccessTokenAsync()
{
var content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("Grant_Type", _grantType),
new KeyValuePair<string, string>("Scope", _scope),
new KeyValuePair<string, string>("Client_Id", _clientId),
new KeyValuePair<string, string>("Client_Secret", _clientSecret)
});
var responce = await _httpClient.PostAsync(_uri, content);
responce.EnsureSuccessStatusCode();
var jsonString = await responce.Content.ReadAsStringAsync();
var document = await JsonDocument.ParseAsync(jsonString.ToStream());
return document.RootElement.GetProperty("access_token").GetString();
}
I have searched in net for solutions. I found some solutions but none of them is working for me.
User Type must be a Member. My user type is already Member. Original issue - “The tenant for tenant guid does not exist” even though user is listed on users endpoint?
Using Domain as tenentId. Its not working. Original issue - Getting "The tenant for tenant guid '' does not exist"
private const string _tenantName = "quicksilverconnectoutlook.onmicrosoft.com";
Some interesting observations
I was able to get the user but not their mails. Note: In this below code, only user id is working not their email id.
var userId = "8685e56b-b1a8-45cf-a5d1-5c5ddadd0f3e";
// EmailId (quicksilverconnect#outlook.com) is not working here
var user = await graphServiceClient.Users[userId].Request().GetAsync();
I found out that if I use the access token generated by Graph Explorer then my code is working properly. So probably the issue is in my GetAccessTokenAsync code or its configuration details.
Update:
I want to use Application permissions not Delegated permissions because my application will use Notification Subscriptions to get a notification when a new mail is received by any users. Also, I want to get the full email details of the new mail. In short, this application will run in the background.
var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
return Task.CompletedTask;
}));
var subscription = await graphServiceClient.Subscriptions.Request().AddAsync(new Subscription()
{
Resource = "/users/quicksilverconnect#outlook.com/messages",
ChangeType = "created",
ExpirationDateTime = DateTimeOffset.Now.AddDays(3).AddHours(-1),
NotificationUrl = "https://asdasdasd.azurewebsites.net/Outlook/NewMailListener",
ClientState = Guid.NewGuid().ToString()
});
It seems the problem was caused by you don't have O365 subscription. Although you have azure subscription and have an email for your azure account, but you do not have O365 subscription. So you can just get the users by graph but can not get email messages by graph.
For this problem, you can just go to this page(login with you azure admin account) and buy O365 subscription.(for example: Office 65 E3)
Maybe you can also buy Exchange online(such as Exchange Online (Plan 2)) on the same page to access the email message.
By the way, there is a mistake in your code. You use client_credentials as "Grant_Type" and use DelegateAuthenticationProvider. If you want to use DelegateAuthenticationProvider, you need to set "Grant_Type" as password but not client_credentials.
To use client credential authentication, You need to install Microsoft.Graph.Auth. Note: this is a prerelease package. Here is a code snippet
var confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(configuration.ClientId)
.WithTenantId(configuration.TenantId)
.WithClientSecret(configuration.ClientSecret)
.Build();
var authProvider = new ClientCredentialProvider(confidentialClientApplication);
var graphServiceClient = new GraphServiceClient(_clientCredentialProviderauthProvider);

Failing to get Team details from a c# console app while it works from Graph Explorer

I need to collect Microsoft Teams data from a C# console application using Microsoft Graph.
I am using ADAL and cloned the authentication methods from the https://github.com/microsoftgraph/console-csharp-connect-sample sample.
The only difference is that I am using an HttpClient client and not a GraphServiceClient that does not implement Teams objects.
The list of required permissions have been determined with a Fiddler trace of a request made with Graph Explorer (no need for User.Read.All or User.Write.All) :
User.Read, Mail.Send, Files.ReadWrite, User.ReadWrite, User.ReadBasic.All, Sites.ReadWrite.All, Contacts.ReadWrite, People.Read, Notes.ReadWrite.All, Tasks.ReadWrite, Mail.ReadWrite, Files.ReadWrite.All, Calendars.ReadWrite
Everything works fine with my console app as long as I am not requesting any Teams resource:
I can get the list of groups "that are Teams" with the following
request: https://graph.microsoft.com/beta/groups?$filter=resourceProvisioningOptions/any(v:v eq 'Team')&$select=id,displayname,groupTypes,resourceBehaviorOptions,resourceProvisioningOptions
I can successfully get the group details with: https://graph.microsoft.com/beta/groups/{groupId}
But when I try to get the team view of that group (which I am member of) it fails with HTTP
403-Unautorized:
https://graph.microsoft.com/beta/groups/{groupId}/team
Very
frustrating to see that this last step is working well from the
Graph Explorer
My problem is very similiar with Access Denied when querying Teams in Microsoft Graph but in my case I am member of the teams I am trying to access and the request works with Graph Explorer.
Code details:
class AuthenticationHelper
{
// The Client ID is used by the application to uniquely identify itself to the v2.0 authentication endpoint.
static string clientId = Constants.ClientId;
// The list of required permissions have been determined with a Fiddler trace of a request made with Graph Explorer
// e.g. below are the permissions Grap Explorer requires to run the sample requests
public static string[] Scopes = {
"User.Read"
, "Mail.Send"
, "Files.ReadWrite"
, "User.ReadWrite"
, "User.ReadBasic.All"
, "Sites.ReadWrite.All"
, "Contacts.ReadWrite"
, "People.Read"
, "Notes.ReadWrite.All"
, "Tasks.ReadWrite"
, "Mail.ReadWrite"
, "Files.ReadWrite.All"
, "Calendars.ReadWrite"
};
public static PublicClientApplication IdentityClientApp = new PublicClientApplication(clientId);
public static string UserToken = null;
public static DateTimeOffset Expiration;
//-----------------------------------------------------------------------------------------------------------------
public static async Task<HttpClient> GetAuthenticatedHttpClient()
{
HttpClient client = null;
try
{
client= new HttpClient(new HttpClientHandler { UseCookies = true });
var token = await GetTokenForUserAsync();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// This header has been added to identify our sample in the Microsoft Graph service. If extracting this code for your project please remove.
client.DefaultRequestHeaders.Add("SampleID", "TestCSharp-AzureToken");
return client;
}
catch (Exception ex)
{
Debug.WriteLine("Could not create a graph client: " + ex.Message);
}
return client;
}
//-----------------------------------------------------------------------------------------------------------------
public static async Task<string> GetTokenForUserAsync()
{
AuthenticationResult authResult;
try
{
IEnumerable<IAccount> accounts = await IdentityClientApp.GetAccountsAsync();
IAccount firstAccount = accounts.FirstOrDefault();
authResult = await IdentityClientApp.AcquireTokenSilentAsync(Scopes, firstAccount);
UserToken = authResult.AccessToken;
}
catch (Exception)
{
if (UserToken == null || Expiration <= DateTimeOffset.UtcNow.AddMinutes(5))
{
authResult = await IdentityClientApp.AcquireTokenAsync(Scopes );
UserToken = authResult.AccessToken;
Expiration = authResult.ExpiresOn;
}
}
return UserToken;
}
}
//----------------------------------------------------
// Console entry point
class Program
{
//public static GraphServiceClient client;
public static HttpClient _client;
static async Task<string> GetHttpResponse(string url)
{
string responseBody = null;
_client = await AuthenticationHelper.GetAuthenticatedHttpClient();
HttpResponseMessage response = await _client.GetAsync(url);
response.EnsureSuccessStatusCode();
using (HttpContent content = response.Content)
{
responseBody = await response.Content.ReadAsStringAsync();
logger.Trace(responseBody);
}
return responseBody;
}
static void Main(string[] args)
{
// call 1 is working: list groups that "are Microsoft Teams"
string s;
string url = "https://graph.microsoft.com/beta/groups?$filter=resourceProvisioningOptions/any(v:v eq 'Team')&$select=id,displayname,groupTypes,resourceBehaviorOptions,resourceProvisioningOptions";
s = await GetHttpResponse(url);
Console.WriteLine(s);
// call 2 is working: Display details of one of these groups
Console.Write($"Enter the id of the group/teams to search for: ");
string groupId = Console.ReadLine().Trim().ToLower();
url = $"https://graph.microsoft.com/beta/groups/{groupId}";
s = await GetHttpResponse(url);
Console.WriteLine(s);
// call 3 is failing: Display the team view of this groups
url = url + "/team";
s = await GetHttpResponse(url);
Console.WriteLine(s);
}
}
You're missing a scope. You need to have Group.Read.All in order to read a Group or Team.

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;
}

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