【问题标题】:How do I modify WIF's ValidatingIssuerNameRegistry to support Azure AD, ACS, Facebook, LiveID and other IDPs?如何修改 WIF 的 ValidatingIssuerNameRegistry 以支持 Azure AD、ACS、Facebook、LiveID 和其他 IDP?
【发布时间】:2014-03-20 20:25:54
【问题描述】:

我想向尽可能多的用户公开一个应用程序。为此,我按照as explained here 的指示将我的应用程序连接到 Azure Active Directory,并将 a variation of these instructions to connect AAD 连接到 Azure ACS 2.0。

Azure ACS 2.0 将处理所有联合域和 Microsoft 帐户(以前称为 LiveID 或 Passport)。它还将处理 Facebook、Twitter 和其他 OAuth 服务。

Azure Active Directory 将处理 Office 365,以及将其公司 Active Directory 同步到云的任何人。

我的主领域发现页面将在以下 URL 发出 GET 以确定是否应使用 LiveID 或 AzureAD 域。

https://login.microsoftonline.com/GetUserRealmExtended.srf?login=EMAIL@COMPANY.com 

http://odc.officeapps.live.com/odc/emailhrd/getidp?hm=0&emailAddress=USER%COMPANY.com

如果用户不存在,我将使用 Azure ACS 与该公司的联合。缺少它,用户将无法登录。

现在我解释了我的配置,我打算让 Windows Identity Foundation (WIF) 允许来自 ACS 2.0 和 ADFS 的身份验证。

问题

  • 如何获得 WIF 4.5,特别是 ValidatingIssuerNameRegistry 以正确处理对多个 IDP 的多个信任?

以下是在将应用程序与 Azure Active Directory 联合时随 VS2013 提供的代码。它响应所有联合请求并执行我不理解的其他事情。有关此类的任何链接或信息都会有所帮助

  public class DatabaseIssuerNameRegistry : ValidatingIssuerNameRegistry
  { 
    public static bool ContainsTenant(string tenantId)
    {
        using (TenantDbContext context = new TenantDbContext())
        {
            return context.Tenants
                .Where(tenant => tenant.Id == tenantId)
                .Any();
        }
    }

    public static bool ContainsKey(string thumbprint)
    {
        using (TenantDbContext context = new TenantDbContext())
        {
            return context.IssuingAuthorityKeys
                .Where(key => key.Id == thumbprint)
                .Any();
        }
    }

    public static void RefreshKeys(string metadataLocation)
    {
        IssuingAuthority issuingAuthority = ValidatingIssuerNameRegistry.GetIssuingAuthority(metadataLocation);

        bool newKeys = false;
        foreach (string thumbprint in issuingAuthority.Thumbprints)
        {
            if (!ContainsKey(thumbprint))
            {
                newKeys = true;
                break;
            }
        }

        if (newKeys)
        {
            using (TenantDbContext context = new TenantDbContext())
            {
                context.IssuingAuthorityKeys.RemoveRange(context.IssuingAuthorityKeys);
                foreach (string thumbprint in issuingAuthority.Thumbprints)
                {
                    context.IssuingAuthorityKeys.Add(new IssuingAuthorityKey { Id = thumbprint });
                }
                context.SaveChanges();
            }
        }
    }

    public static bool TryAddTenant(string tenantId, string signupToken)
    {
        if (!ContainsTenant(tenantId))
        {
            using (TenantDbContext context = new TenantDbContext())
            {
                SignupToken existingToken = context.SignupTokens.Where(token => token.Id == signupToken).FirstOrDefault();
                if (existingToken != null)
                {
                    context.SignupTokens.Remove(existingToken);
                    context.Tenants.Add(new Tenant { Id = tenantId });
                    context.SaveChanges();
                    return true;
                }
            }
        }

        return false;
    }

    public static void AddSignupToken(string signupToken, DateTimeOffset expirationTime)
    {
        using (TenantDbContext context = new TenantDbContext())
        {
            context.SignupTokens.Add(new SignupToken
            {
                Id = signupToken,
                ExpirationDate = expirationTime
            });
            context.SaveChanges();
        }
    }

    public static void CleanUpExpiredSignupTokens()
    {
        DateTimeOffset now = DateTimeOffset.UtcNow;
        using (TenantDbContext context = new TenantDbContext())
        {
            IQueryable<SignupToken> tokensToRemove = context.SignupTokens.Where(token => token.ExpirationDate <= now);
            if (tokensToRemove.Any())
            {
                context.SignupTokens.RemoveRange(tokensToRemove);
                context.SaveChanges();
            }
        }
    }

    protected override bool IsThumbprintValid(string thumbprint, string issuer)
    {
        string issuerID = issuer.TrimEnd('/').Split('/').Last();

        return ContainsTenant(issuerID) &&
            ContainsKey(thumbprint);
    }
}

【问题讨论】:

  • 您的“此处解释”链接丢失。我认为这很重要。 :-)
  • @Jaxidian - 修复它!
  • 一个明确的问题:您似乎表明点击 Active Directory 将不会落后于 ACS。这是有原因的还是我误解了你的计划?如果是我,我会使用 ACS 来代理一切,包括 AD,因此您的应用只有一个系统可以与之通信。 (如果您希望我更快回复,请在 Twitter 上联系我:@Jaxidian)
  • @Jaxidian 好问题。如果使用 ACS 与常规 O365/AAD 登录,Office365/Azure AD SSO 登录流程是否会发生变化?我有多租户域“eventvwr.com”和“perfmon.com”,我希望 MSFT 极客尽可能轻松地登录这些域。
  • 它的工作原理是这样的:1)浏览器从应用程序请求页面。 2) 应用程序因身份不明而使用户失败。 3) 被动联合将用户重定向到 ACS。 4) 你应该有一个实时令牌,如果登录的话,ACS 将允许你获得一个特定于你的应用程序的令牌;否则,用户通过 ACS 的代理系统登录到第三方 STS/equiv 以向 ACS 提供受信任的令牌,然后允许用户为您的应用获取令牌。 5)通过被动联合,浏览器被重定向到您的应用程序。 6) 重复第 1 步,但由于您通过身份验证,请跳过所有其他步骤并使用该应用! - 这能回答你的问题吗?

标签: azure wif multi-tenant acs azure-active-directory


【解决方案1】:

Vittorio Bertocci 在这篇文章中很好地解释了 DatabaseIssuerNameRegistry。

VS2013 RTM, Organizational Accounts and Publishing to Windows Azure Web Sites!

底线是DatabaseIssuerNameRegistry 只是一个基于实体框架的ValidatingIssuerNameRegistry,它使用令牌的指纹从数据库中查找颁发者名称,并验证它与颁发者名称的配置值匹配,如反对使用web.config。它更灵活,并且可以在授权更改指纹时处理更新指纹。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-08
    • 1970-01-01
    相关资源
    最近更新 更多