Reputation: 2432
Currently, I am following this article https://dev.outlook.com/restapi/tutorial/dotnet . Everything works great expect I cannot retrieve the user's email address from acess-token.
private string GetEmailFromIdToken(string token) {
// JWT is made of three parts, separated by a '.'
// First part is the header
// Second part is the token
// Third part is the signature
string[] tokenParts = token.Split('.');
if (tokenParts.Length < 3) {
// Invalid token, return empty
}
// Token content is in the second part, in urlsafe base64
string encodedToken = tokenParts[1];
// Convert from urlsafe and add padding if needed
int leftovers = encodedToken.Length % 4;
if (leftovers == 2) {
encodedToken += "==";
} else if (leftovers == 3) {
encodedToken += "=";
}
encodedToken = encodedToken.Replace('-', '+').Replace('_', '/');
// Decode the string
var base64EncodedBytes = System.Convert.FromBase64String(encodedToken);
string decodedToken = System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
// Load the decoded JSON into a dynamic object
dynamic jwt = Newtonsoft.Json.JsonConvert.DeserializeObject(decodedToken);
// User's email is in the preferred_username field
return jwt.preferred_username;
}
In some article people says to add "openid"
private static string[] scopes = { "openid", "https://outlook.office.com/mail.read",
"https://outlook.office.com/calendars.read" };
in the scope. However it still doesn't work. It doesn't even give a successful login, throws exception. When I don't add the "openid", I get a successful login however preferred_username is empty.
Upvotes: 4
Views: 1801
Reputation: 17692
I've seen some cases where the preferred_username
claim isn't an SMTP address, so it's actually not the best way to get the user's email address. I'd recommend instead of parsing the ID token, just make an API call to GET /Me
, which should include an EmailAddress
property.
I'm reworking the tutorial on dev.outlook.com to do this.
Upvotes: 1
Reputation: 2030
That's a good catch!
You need to include the "profile" scope in order to read the preferred_username,
private static string[] scopes = { "profile",
"https://outlook.office.com/mail.read",
"https://outlook.office.com/calendars.read" };
Alternatively, try using the "contacts.read" scope
private static string[] scopes = { "https://outlook.office.com/contacts.read",
"https://outlook.office.com/mail.read",
"https://outlook.office.com/calendars.read" };
Find out more on the permission scopes in https://github.com/OfficeDev/microsoft-graph-docs/blob/master/content/authorization/permission_scopes.md
Upvotes: 2