Как изменить ValidatingIssuerNameRegistry WIF для поддержки Azure AD, ACS, Facebook, LiveID и других IDP?

У меня есть приложение, которое я хотел бы предоставить как можно большему количеству пользователей. Для этого я следую указаниям , как описано здесь, для подключения моего приложения к Azure Active Directory и вариант этих инструкций для подключения AAD к Azure ACS 2.0.

Azure ACS 2.0 будет обрабатывать все федеративные домены и учетные записи Microsoft (ранее LiveID или Passport). Он также будет обрабатывать Facebook, Twitter и другие службы OAuth.

Azure Active Directory будет обрабатывать Office 365 и всех, кто синхронизирует свою корпоративную Active Directory с облаком.

Моя страница обнаружения домашней области выдаст GET по следующему URL-адресу, чтобы определить, следует ли использовать домен LiveID или AzureAD.

https://login.microsoftonline.com/[email protected] 

или 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?

Ниже приведен код, поставляемый с VS2013 при объединении приложения с Azure Active Directory. Он отвечает на все запросы федерации и делает другие вещи, которых я не понимаю. Любые ссылки или информация об этом классе были бы полезны

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

person halfbit    schedule 30.01.2014    source источник
comment
Как объяснено здесь, ссылка отсутствует. Я думаю, это важно. :-)   -  person Jaxidian    schedule 31.01.2014
comment
@Jaxidian - Исправлено!   -  person halfbit    schedule 31.01.2014
comment
Уточняющий вопрос: похоже, вы указываете, что нажатие на Active Directory не будет позади ACS. Есть ли причина для этого или я неправильно понимаю ваши планы? Если бы это был я, я бы использовал ACS для посредничества всего, включая AD, чтобы у вашего приложения была только одна система, с которой можно было бы общаться. (Напишите мне в Twitter, если хотите, чтобы я ответил быстрее: @Jaxidian)   -  person Jaxidian    schedule 31.01.2014
comment
@Jaxidian Хороший вопрос. Изменится ли поток входа в систему единого входа Office365 / Azure AD, если используется ACS, по сравнению с обычным входом в O365 / AAD? У меня есть мультитенантные домены eventvwr.com и perfmon.com, и я хочу, чтобы компьютерные фанаты MSFT входили в эти домены как можно проще.   -  person halfbit    schedule 31.01.2014
comment
Это работает так: 1) Браузер запрашивает страницу из приложения. 2) Приложение не может определить пользователя как неопознанного. 3) Пассивная федерация перенаправляет пользователя в ACS. 4) У вас должен быть активный токен, если вы вошли в систему, с ACS, который позволит вам получить токен, специфичный для вашего приложения; в противном случае пользователь входит в систему через посредническую систему ACS на стороннем STS / эквиваленте, чтобы предоставить ACS доверенный токен, чтобы затем позволить пользователю получить токен для вашего приложения. 5) Через пассивную федерацию браузер перенаправляется в ваше приложение. 6) Повторите действия с шага 1, но, поскольку вы аутентифицированы, пропустите все остальные шаги и используйте приложение! - Это ответ на ваш вопрос?   -  person Jaxidian    schedule 01.02.2014
comment
В конечном счете, если вы не хотите использовать ACS для передачи пользователя множеству различных аутентификаторов, вам придется создать его самостоятельно. НАМНОГО проще позволить ACS делать всю эту работу за вас.   -  person Jaxidian    schedule 01.02.2014
comment
@Jaxidian Я думаю, мой вопрос лучше всего сформулирован: «Предположим, мне нужен лучший SSO для пользователей ACS и O365». Я также хочу, чтобы для этого был процесс обнаружения умного дома. Мой процесс HRD должен включать пользователей OAuth, AzureAD, O365, LiveID. Кроме того, они могут войти в систему как федерации, подключенные к ACS.   -  person halfbit    schedule 01.02.2014
comment
Я полагаю, что мне нужно прояснить (или поправить, если я ошибаюсь), что Azure AD намного проще, чем ACS. В основном потому, что люди объединяются с MSFT, и это позволяет им использовать единый вход для всех. Не то же самое для ACS. Федерация с ACS означает, что только владелец ACS является IDP или RP.   -  person halfbit    schedule 01.02.2014


Ответы (1)


Витторио Берточчи хорошо объяснил DatabaseIssuerNameRegistry в этом посте.

VS2013 RTM, учетные записи организаций и публикация на веб-сайтах Windows Azure!

Суть в том, что DatabaseIssuerNameRegistry - это просто ValidatingIssuerNameRegistry на основе Entity Framework, которая ищет имя эмитента в базе данных, используя отпечаток токена и проверяет, соответствует ли оно настроенному значению для имени эмитента, в отличие от использования web.config. является более гибким и обрабатывает обновление отпечатков пальцев, если / когда орган власти изменяет их.

person Pete    schedule 20.03.2014