微软Azure AD图形API:如何获取用户的电子邮件地址?

12

我能够获取用户的accessToken,并且正在使用带有 Authorization: Bearer <token> 头的调用 GET https://graph.microsoft.com/v1.0/me

但是,我在响应主体中得到的结果类似于:

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users",
    "value": [
        {
            "givenName": "Foo",
            "surname": "Bar",
            "displayName": "Foo Bar",
            "id": "b41efha115adcca29",
            "userPrincipalName": "email.address@outlook.com",
            "businessPhones": [],
            "jobTitle": null,
            "mail": null,
            "mobilePhone": null,
            "officeLocation": null,
            "preferredLanguage": null
        }
    ]
}

mail属性值为null,而此响应体中的userPrincipalName恰好是用户的电子邮件地址。但是,根据Microsoft的文档

尽管UPN和电子邮件具有相同的格式,但用户的UPN值可能与用户的电子邮件地址相同也可能不同。

在启动用户登录请求时,我们请求"user.read""email"范围。我们使用MSAL.js库获取访问令牌,我们的代码类似于:

login (): ng.IPromise<IMicrosoftOAuthResponse> {
  const prom = this.userAgentApplication.loginPopup(["user.read", "email"])
    .then((idToken) => {
      return this.userAgentApplication.acquireTokenSilent(["user.read", "email"]);
    })
    .then((accessToken) => {
      // at this point, we have the accessToken and make a call to the graph api
    });
  return this.$q.when(prom);
}

我该如何获取此处用户的实际电子邮件地址?


在快速阅读userEntity文档时,看起来userPrincipalName始终是用户的电子邮件。其主要含义是:UPN是基于互联网标准RFC 822的用户互联网样式登录名。根据惯例,这应映射到用户的电子邮件名称。一般格式为“alias@domain”。对于工作或学校帐户,域必须存在于租户的已验证域集合中。 - vishwarajanand
1
从他们的文档中得知:“尽管UPN和电子邮件具有相同的格式,但用户的UPN值可能与用户的电子邮件地址相同,也可能不同。” https://learn.microsoft.com/en-us/azure/active-directory/connect/active-directory-aadconnect-user-signin#user-sign-in-and-user-principal-name - bioball
我曾经认为将UPN与电子邮件保持一致是一种“推荐”的做法。但事实上它更像是一种通用做法,这篇博客还提到很多应用程序实际上是将UPN作为电子邮件使用的。 - vishwarajanand
1
是的。看起来 mail 属性应该是用户的实际电子邮件地址,但它返回了 null - bioball
附加说明:我还发现其他差异:使用我的OAuth令牌,我得到了我的帐户businessPhones和UUID4作为id值(我的AAD中的用户对象ID)。使用Graph Explorer的令牌,businessPhones数组为空,并且id具有16位数字值,我无法在AAD中找到它。 - Steffen Roßkamp
显示剩余2条评论
6个回答

6

mail属性有两种设置方式:

  1. 在本地AD中进行设置,然后使用AD连接同步到Azure AD中;
  2. 云用户已被分配了Office 365许可证(以及邮箱),此时为此受许可的用户设置mail属性。

如果用户没有O365邮箱/许可证,您也可以通过userPrincipalName、displayName等搜索该用户。$filter支持OR运算符。

希望这能帮到您。


嗨,丹,感谢您的回复。如果“mail”属性为null,这是否意味着它不存在?此外:我们现在采取的获取电子邮件地址的方法是首先检查“mail”属性,否则转而使用“userPrincipalName”。这样做是否明智? - bioball
1
抱歉耽搁了。让我问一个我本应早些时候就该问的问题。您打算如何使用用户的邮件地址?例如,您是否计划向他们发送电子邮件通知?如果是这样的话,邮件未设置,可能是因为用户没有来自Office 365的邮箱,或者此属性未在本地设置(并通过AD Connect同步)。 - Dan Kershaw - MSFT
没问题。我们使用电子邮件地址来去重和匹配可能已经存在于我们帐户系统中的用户。例如:一个用户可能已经通过电子邮件/密码创建了一个帐户,并在之后通过MS OAuth进行了登录。我们还希望拥有他们的电子邮件地址,以便我们可以向他们提供注册新闻通讯,发送交易电子邮件等选项。 - bioball
那么如果“mail”属性没有设置,这是否意味着Microsoft不知道用户的电子邮件地址? - bioball
有关此事有任何更新吗?我也遇到了同样的问题@DanKershaw-MSFT。 - garrettm

1
尽管这是一个老问题,但我想分享一下我的解决方案,以获取已登录用户的电子邮件。请注意,此解决方案需要访问用户的id_token
调用/me端点的响应如下所示:
Object {
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
  "businessPhones": Array [],
  "displayName": "John Doe",
  "givenName": "John",
  "id": "xxxxxx",
  "jobTitle": null,
  "mail": null,
  "mobilePhone": null,
  "officeLocation": null,
  "preferredLanguage": null,
  "surname": "Doe",
  "userPrincipalName": "johndoe_gmail.com#EXT#@johndoegmail.onmicrosoft.com",
}

正如我们所看到的,此响应的邮件属性为null。然而,在调用/token端点时,通过解码传递的access_token中的id_token,我能够获取用户电子邮件。
通过将解码id_token的结果应用于附在本帖子末尾的decodeJwtToken()函数,我能够从结果中获取用户电子邮件。
Object {
  "aio": "xxxxxxxx",
  "amr": Array [
    "pwd",
  ],
  "aud": "xxxxx",
  "email": "johndoe@gmail.com",
  "exp": xxxxxx,
  "family_name": "Doe",
  "given_name": "John",
  "iat": xxxxxx,
  "idp": "live.com",
  "ipaddr": "xxx.xxx.xxx.xxx",
  "iss": "https://sts.windows.net/xxxx/",
  "name": "John Doe",
  "nbf": xxxx,
  "nonce": "xxx",
  "oid": "xxxxxxx",
  "sub": "xxxxx",
  "tid": "xxxxx",
  "unique_name": "live.com#johndoe@gmail.com",
  "uti": "xxxx",
  "ver": "1.0",
}

解码函数如下所示:

decodeIdToken = (token) => {
  var base64Url = token.split('.')[1];
  var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
  var jsonPayload = decodeURIComponent(Buffer.from(base64, 'base64').toString().split('').map(function(c) {
      return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
  }).join(''));

  return JSON.parse(jsonPayload);
};

我一直在做类似的事情,但也有一些场景无法奏效。例如,在使用MSAL并刷新ID令牌时,MSAL将返回一个没有电子邮件字段的ID令牌(因为MSAL的编写方式)。手动或使用其他库请求刷新令牌时,通常情况下这是有效的。 - Devis L.

0
对于我来说,https://graph.microsoft.com/v1.0/users/<userid>/authentication/emailMethods端点可行。为此,客户端必须具备UserAuthenticationMethod.Read.All权限。您可以在这里找到更多文档。
示例响应:
{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('8225f1cd-8025-4f8b-bf94-c595e02e3403')/authentication/emailMethods",
    "value": [
        {
            "id": "3ddfcfc8-9383-446f-83cc-3ab9be4be18f",
            "emailAddress": "kim@contoso.com"
        }
    ]
}

0

我最近也遇到了这个问题,当尝试访问https://graph.microsoft.com/v1.0/me时出现错误:

requests.exceptions.HTTPError: 403 Client Error: Forbidden for url: https://graph.microsoft.com/v1.0/me | Error Message: Exception of type 'Microsoft.Fast.Profile.Core.Exception.ProfileAccessDeniedException' was thrown.

我是这样解决的:

我使用了包含在访问令牌中的返回id_token。您可能并不总是会收到此信息,例如,如果仅刷新访问令牌,则MSAL库可能不会始终返回此信息,因此,如果您在第一次获取它时未提取和保存id_token的信息,则应该将其副本存储在某个地方。

id_token按照标准进行了base64编码,您可以使用此工具来调试以找出正确的详细信息:https://jwt.io/#debugger

我使用了这个库来可靠地解码令牌:https://github.com/jpadilla/pyjwt/

pip install PyJWT

from jwt import decode

id_token = ''  # Replace this with the id_token provided to you when getting the access token
decoded_id_token = decode(id_token, algorithms=['RS256'], options={'verify_signature': False})
user_email = decoded_id_token.get('email') if decoded_id_token.get('email') else decoded_id_token.get('preferred_username')

-1
var user = graphServiceClient.Users[UserId].Request().Select(x=>x.Identities).GetAsync();
var email = user.Identities.FirstOrDefault().IssuerAssignedId;

-2

使用以下链接在Microsoft Office 365 Business Premium中创建试用帐户: https://signup.microsoft.com/Signup?OfferId=467eab54-127b-42d3-b046-3844b860bebf&dl=O365_BUSINESS_PREMIUM&culture=en-IN&country=IN&ali=1

在创建帐户时,请按照步骤进行操作。这将允许我们在Office 365中创建用户。这些用户就像组织的内部用户一样。现在使用上述凭据打开Azure门户。Office 365的所有用户都将被导入到活动的Azure目录中。

现在,在活动的Azure目录中注册一个应用程序,并授予“读取用户基本配置文件”委派权限。记下客户端ID、客户端密钥和租户域,以获取服务到服务身份验证的访问令牌。此访问令牌可用于获取包含邮件字段为abc@.onmicrosoft.com的用户记录。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接