c# - 通过 OpenID Connect 从 Azure AD 获取用户的电子邮件地址

标签 c# owin azure-active-directory openid-connect

我正在尝试使用他们的 Office 365 帐户对访问我网站的用户进行身份验证,因此我一直遵循有关使用 OWIN OpenID Connect 中间件添加身份验证的指南,并成功地设法对他们的个人资料进行身份验证和检索。

我现在正在尝试获取用户的电子邮件地址(以便我可以使用他们的详细联系信息填充他们的系统帐户),但我似乎无法收回电子邮件声明。我已尝试使用范围 openid profile email 发出请求,但声明集不包含任何邮件信息。

有没有办法通过 OpenID Connect 端点从 Azure AD 获取用户的电子邮件?

最佳答案

在找到解决方案之前,我为同一个问题苦苦挣扎了几天。在回答您的问题时:是的,只要您满足以下条件,您就应该能够在 claim 中取回电子邮件地址:

  1. 在您的请求中包含 profileemail 范围,并且
  2. 在 Azure 门户 Active Directory 部分配置您的应用程序以在委派权限下包含登录和阅读用户配置文件

请注意,电子邮件地址可能不会在 email 声明中返回:在我的情况下(一旦我开始工作)它会在 name 声明中返回.

但是,根本 无法取回电子邮件地址可能是由以下问题之一引起的:

没有与 Azure AD 帐户关联的电子邮件地址

根据本指南Scopes, permissions, and consent in the Azure Active Directory v2.0 endpoint ,即使您包含 email 范围,您也可能无法取回电子邮件地址:

The email claim is included in a token only if an email address is associated with the user account, which is not always the case. If it uses the email scope, your app should be prepared to handle a case in which the email claim does not exist in the token.

如果您收到其他与配置文件相关的声明(例如 given_namefamily_name),这可能就是问题所在。

被中间件丢弃的声明

这就是我的原因。我没有收到任何个人资料相关的声明(名字、姓氏、用户名、电子邮件等)。

在我的例子中,身份处理堆栈如下所示:

问题出在 IdentityServer3.AspNetIdentity AspNetIdentityUserService 类中:InstantiateNewUserFromExternalProviderAsync() method looks like this :

protected virtual Task<TUser> InstantiateNewUserFromExternalProviderAsync(
    string provider,
    string providerId,
    IEnumerable<Claim> claims)
{
    var user = new TUser() { UserName = Guid.NewGuid().ToString("N") };
    return Task.FromResult(user);
}

请注意,它传入一个声明集合然后忽略它。我的解决方案是创建一个由此派生的类并将该方法重写为如下所示:

protected override Task<TUser> InstantiateNewUserFromExternalProviderAsync(
    string provider,
    string providerId,
    IEnumerable<Claim> claims)
{
    var user = new TUser
    {
        UserName = Guid.NewGuid().ToString("N"),
        Claims = claims
    };
    return Task.FromResult(user);
}

我不确切知道您使用的是什么中间件组件,但很容易看到从您的外部提供商返回的原始声明;这至少会告诉您它们恢复正常,并且问题出在您的中间件中。只需将 Notifications 属性添加到您的 OpenIdConnectAuthenticationOptions 对象,如下所示:

// Configure Azure AD as a provider
var azureAdOptions = new OpenIdConnectAuthenticationOptions
{
    AuthenticationType = Constants.Azure.AuthenticationType,
    Caption = Resources.AzureSignInCaption,
    Scope = Constants.Azure.Scopes,
    ClientId = Config.Azure.ClientId,
    Authority = Constants.Azure.AuthenticationRootUri,
    PostLogoutRedirectUri = Config.Identity.RedirectUri,
    RedirectUri = Config.Azure.PostSignInRedirectUri,
    AuthenticationMode = AuthenticationMode.Passive,
    TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = false
    },
    Notifications = new OpenIdConnectAuthenticationNotifications
    {
        AuthorizationCodeReceived = context =>
        {
            // Log all the claims returned by Azure AD
            var claims = context.AuthenticationTicket.Identity.Claims;
            foreach (var claim in claims)
            {
                Log.Debug("{0} = {1}", claim.Type, claim.Value);
            }
            return null;
        }
    },
    SignInAsAuthenticationType = signInAsType // this MUST come after TokenValidationParameters
};

app.UseOpenIdConnectAuthentication(azureAdOptions);

另见

关于c# - 通过 OpenID Connect 从 Azure AD 获取用户的电子邮件地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30983694/

相关文章:

c# - 如何从内容的代码隐藏页面调用母版页中的方法?

asp.net - 使用 Microsoft.Owin.Testing.TestServer 进行身份验证以进行内存中集成测试

oauth - 了解 client_id 和 client_secret

c# - Java 增强的 for 循环 VS .NET foreach 循环

c# - 这个多行 if 语句是否太复杂?

c# - SignalR HubConnection 无效的 URI

reactjs - 在没有登录用户的 Azure AD 中调用 API 的 Web 应用程序的身份验证/授权场景是什么?

sql-server - 仅与 Active Directory 建立连接...用户 '<token-identified principal>' 登录失败

azure - 如何获取Azure Active Directory应用程序的权限?

c# - 如何从 Visual studio 2017 预览版 2 指定 Azure Function 的输出绑定(bind)?